Skip to content

Commit a093fe4

Browse files
[slimtensor] Add from_etensor factory function for ETensor to SlimTensor conversion (#16996)
This PR was created by the merge bot to help merge the original PR into the main branch. ghstack PR number: #16551 by @Gasoonjia ^ Please use this as the source of truth for the PR details, comments, and reviews ghstack PR base: https://github.com/pytorch/executorch/tree/gh/gasoonjia/100/base ghstack PR head: https://github.com/pytorch/executorch/tree/gh/gasoonjia/100/head Merge bot PR base: https://github.com/pytorch/executorch/tree/gh/gasoonjia/99/orig Merge bot PR head: https://github.com/pytorch/executorch/tree/gh/gasoonjia/100/orig Differential Revision: [D90539554](https://our.internmc.facebook.com/intern/diff/D90539554/) @diff-train-skip-merge --------- Co-authored-by: gasoonjia <[email protected]>
1 parent 38a3c10 commit a093fe4

File tree

4 files changed

+469
-0
lines changed

4 files changed

+469
-0
lines changed
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree.
7+
*/
8+
9+
#pragma once
10+
11+
#include <executorch/backends/aoti/slim/core/slim_tensor.h>
12+
#include <executorch/backends/aoti/slim/factory/empty.h>
13+
#include <executorch/backends/aoti/slim/util/array_ref_util.h>
14+
#include <executorch/runtime/core/error.h>
15+
#include <executorch/runtime/core/exec_aten/util/tensor_util.h>
16+
#include <executorch/runtime/core/portable_type/tensor.h>
17+
18+
namespace executorch::backends::aoti::slim {
19+
20+
/// Creates a SlimTensor from an ETensor (ExecuTorch portable tensor).
21+
///
22+
/// This factory function converts an ETensor to a SlimTensor, optionally
23+
/// copying the data to a target device. The source device can be specified
24+
/// to support ETensors residing on different devices (CPU, CUDA).
25+
///
26+
/// @param etensor The source ETensor.
27+
/// @param source_device The device where the ETensor data resides.
28+
/// @param target_device The target device for the output SlimTensor.
29+
/// @return A new SlimTensor with data copied to the target device.
30+
///
31+
/// @note ETensor uses int32_t (SizesType/StridesType) for sizes and strides,
32+
/// while SlimTensor uses int64_t. This function handles the conversion.
33+
///
34+
/// Example usage:
35+
/// @code
36+
/// // CPU ETensor to GPU SlimTensor
37+
/// auto* cpu_tensor = &(args[i]->toTensor());
38+
/// SlimTensor gpu_tensor = from_etensor(*cpu_tensor, CPU_DEVICE,
39+
/// DEFAULT_CUDA_DEVICE);
40+
///
41+
/// // GPU ETensor to GPU SlimTensor (same device copy)
42+
/// SlimTensor gpu_tensor2 = from_etensor(*gpu_etensor, DEFAULT_CUDA_DEVICE,
43+
/// DEFAULT_CUDA_DEVICE);
44+
///
45+
/// // GPU ETensor to CPU SlimTensor
46+
/// SlimTensor cpu_tensor = from_etensor(*gpu_etensor, DEFAULT_CUDA_DEVICE,
47+
/// CPU_DEVICE);
48+
/// @endcode
49+
inline SlimTensor from_etensor(
50+
const executorch::runtime::etensor::Tensor& etensor,
51+
const c10::Device& source_device = CPU_DEVICE,
52+
const c10::Device& target_device = CPU_DEVICE) {
53+
// Step 1: Extract metadata from ETensor
54+
const auto ndim = static_cast<size_t>(etensor.dim());
55+
56+
// Convert sizes from exec_aten::SizesType (int32_t) to int64_t
57+
std::vector<int64_t> sizes_vec(ndim);
58+
for (size_t i = 0; i < ndim; ++i) {
59+
sizes_vec[i] = static_cast<int64_t>(etensor.size(static_cast<ssize_t>(i)));
60+
}
61+
62+
// Convert strides from exec_aten::StridesType (int32_t) to int64_t
63+
std::vector<int64_t> strides_vec(ndim);
64+
auto etensor_strides = etensor.strides();
65+
for (size_t i = 0; i < ndim; ++i) {
66+
strides_vec[i] = static_cast<int64_t>(etensor_strides[i]);
67+
}
68+
69+
// Map ETensor ScalarType to SlimTensor ScalarType
70+
c10::ScalarType dtype = static_cast<c10::ScalarType>(etensor.scalar_type());
71+
72+
// Step 2: Create SlimTensor on target device
73+
SlimTensor result = empty_strided(
74+
makeArrayRef(sizes_vec), makeArrayRef(strides_vec), dtype, target_device);
75+
76+
// Step 3: Copy data from ETensor (source device) to SlimTensor (target
77+
// device) Supports CPU→CPU, CPU→CUDA, CUDA→CPU, or CUDA→CUDA copy
78+
const void* src_data = etensor.const_data_ptr();
79+
void* dst_data = result.data_ptr();
80+
size_t nbytes = etensor.nbytes();
81+
82+
if (nbytes > 0) {
83+
// const_cast is safe here because copy_ only reads from src_data
84+
result.storage()->copy_(
85+
dst_data, const_cast<void*>(src_data), nbytes, source_device);
86+
}
87+
88+
return result;
89+
}
90+
91+
/// Creates a SlimTensor from an ETensor pointer.
92+
///
93+
/// Convenience overload that accepts a pointer instead of a reference.
94+
///
95+
/// @param etensor Pointer to the source ETensor (must not be null).
96+
/// @param source_device The device where the ETensor data resides.
97+
/// @param target_device The target device for the output SlimTensor.
98+
/// @return A new SlimTensor with data copied to the target device.
99+
inline SlimTensor from_etensor(
100+
const executorch::runtime::etensor::Tensor* etensor,
101+
const c10::Device& source_device = CPU_DEVICE,
102+
const c10::Device& target_device = CPU_DEVICE) {
103+
ET_CHECK_MSG(
104+
etensor != nullptr, "from_etensor: etensor pointer cannot be nullptr");
105+
return from_etensor(*etensor, source_device, target_device);
106+
}
107+
108+
} // namespace executorch::backends::aoti::slim

backends/aoti/slim/factory/targets.bzl

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,16 @@ def define_common_targets():
2929
"//executorch/backends/aoti/slim/util:size_util",
3030
],
3131
)
32+
33+
runtime.cxx_library(
34+
name = "from_etensor",
35+
headers = [
36+
"from_etensor.h",
37+
],
38+
visibility = ["@EXECUTORCH_CLIENTS"],
39+
exported_deps = [
40+
"//executorch/backends/aoti/slim/factory:empty",
41+
"//executorch/backends/aoti/slim/util:array_ref_util",
42+
"//executorch/runtime/core/portable_type:portable_type",
43+
],
44+
)

backends/aoti/slim/factory/test/targets.bzl

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,17 @@ def define_common_targets():
4444
],
4545
**backend_kwargs
4646
)
47+
48+
runtime.cxx_test(
49+
name = "test_from_etensor" + backend_suffix,
50+
srcs = [
51+
"test_from_etensor.cpp",
52+
],
53+
deps = [
54+
"//executorch/backends/aoti/slim/core:storage",
55+
"//executorch/backends/aoti/slim/factory:empty",
56+
"//executorch/backends/aoti/slim/factory:from_etensor",
57+
"//executorch/runtime/core/exec_aten/testing_util:tensor_util",
58+
],
59+
**backend_kwargs
60+
)

0 commit comments

Comments
 (0)