Skip to content

Commit e31e0b6

Browse files
shoumikhinfacebook-github-bot
authored andcommitted
Merge TensorImplPtr into TensorPtr.
Summary: Looks like the users can totally share the same Tensor backed by the same TensorImpl. That way sharing a Tensor happens naturally, and whoever wants to build a truly separate Tensor would just unsafeGetTensorImpl() and go ahead. Cloning also seems straightforward - just copy the data\metadata from an existing TensorPtr into a new one, like we do now. Reviewed By: swolchok Differential Revision: D63557912 fbshipit-source-id: 98939ab410ab4c13ccad1c1d64189ed51fce3621
1 parent 8b5cf96 commit e31e0b6

File tree

12 files changed

+464
-1286
lines changed

12 files changed

+464
-1286
lines changed

docs/source/extension-tensor.md

Lines changed: 23 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ You must ensure `sizes`, `dim_order`, `strides`, and `data` stay valid. This mak
3131
3232
## Introducing TensorPtr
3333
34-
To alleviate these issues, ExecuTorch provides `TensorPtr` and `TensorImplPtr` via the new [Tensor Extension](https://github.com/pytorch/executorch/tree/main/extension/tensor) that manage the lifecycle of tensors and their implementations. These are essentially smart pointers (`std::unique_ptr<Tensor>` and `std::shared_ptr<TensorImpl>`, respectively) that handle the memory management of both the tensor's data and its dynamic metadata.
34+
To alleviate these issues, ExecuTorch provides `TensorPtr`, a smart pointer that manages the lifecycle of both the tensor's data and its dynamic metadata.
3535
36-
Now, users no longer need to worry about metadata lifetimes separately. Data ownership is determined based on whether it is passed by pointer or moved into the `TensorPtr` as an `std::vector`. Everything is bundled in one place and managed automatically, enabling you to focus on actual computations.
36+
With `TensorPtr`, users no longer need to worry about metadata lifetimes separately. Data ownership is determined based on whether it is passed by pointer or moved into the `TensorPtr` as an `std::vector`. Everything is bundled in one place and managed automatically, enabling you to focus on actual computations.
3737
3838
Here’s how you can use it:
3939
@@ -50,16 +50,13 @@ auto tensor = make_tensor_ptr(
5050
module.forward(tensor);
5151
```
5252

53-
The data is now owned by the tensor instance because it's provided as a vector. To create a non-owning `TensorPtr` just pass the data by pointer. The `type` is deduced automatically based on the data vector (`float`). `strides` and `dim_order` are computed automatically to the default values based on the `sizes` if not specified explicitly as additional arguments.
53+
The data is now owned by the tensor instance because it's provided as a vector. To create a non-owning `TensorPtr`, just pass the data by pointer. The `type` is deduced automatically based on the data vector (`float`). `strides` and `dim_order` are computed automatically to default values based on the `sizes` if not specified explicitly as additional arguments.
5454

5555
`EValue` in `Module::forward()` accepts `TensorPtr` directly, ensuring seamless integration. `EValue` can now be constructed implicitly with a smart pointer to any type that it can hold. This allows `TensorPtr` to be dereferenced implicitly when passed to `forward()`, and `EValue` will hold the `Tensor` that the `TensorPtr` points to.
5656

5757
## API Overview
5858

59-
The new API revolves around two main smart pointers:
60-
61-
- `TensorPtr`: `std::unique_ptr` managing a `Tensor` object. Since each `Tensor` instance is unique, `TensorPtr` ensures exclusive ownership.
62-
- `TensorImplPtr`: `std::shared_ptr` managing a `TensorImpl` object. Multiple `Tensor` instances can share the same `TensorImpl`, so `TensorImplPtr` ensures shared ownership.
59+
`TensorPtr` is literally an alias for `std::shared_ptr<Tensor>`, so you can work with it easily without duplicating the data and metadata. Each `Tensor` instance may either own its data or reference external data.
6360

6461
### Creating Tensors
6562

@@ -75,15 +72,15 @@ You can create a scalar tensor, i.e. a tensor with zero dimensions or with one o
7572
auto tensor = make_tensor_ptr(3.14);
7673
```
7774

78-
The resulting tensor will contain a single value 3.14 of type double, which is deduced automatically.
75+
The resulting tensor will contain a single value `3.14` of type double, which is deduced automatically.
7976

8077
*Providing A Single Data Value with a Type*
8178

8279
```cpp
8380
auto tensor = make_tensor_ptr(42, ScalarType::Float);
8481
```
8582

86-
Now the integer 42 will be cast to float and the tensor will contain a single value 42 of type float.
83+
Now the integer `42` will be cast to float and the tensor will contain a single value `42` of type float.
8784

8885
#### Owning Data from a Vector
8986

@@ -139,7 +136,7 @@ auto tensor = make_tensor_ptr(
139136
ScalarType::Float); // float scalar type
140137
```
141138

142-
The `TensorPtr` does not own the data, you must ensure the `data` remains valid.
139+
The `TensorPtr` does not own the data, so you must ensure the `data` remains valid.
143140

144141
*Providing Raw Data with Custom Deleter*
145142

@@ -151,36 +148,25 @@ auto tensor = make_tensor_ptr(
151148
{2, 3}, // sizes
152149
data, // data pointer
153150
ScalarType::Double, // double scalar type
154-
TensorShapeDynamism::DYNAMIC_BOUND, // some default dynamism
151+
TensorShapeDynamism::DYNAMIC_BOUND, // default dynamism
155152
[](void* ptr) { delete[] static_cast<double*>(ptr); });
156153
```
157154
158-
The `TensorPtr` will call the custom deleter when it is destroyed, i.e. when the smart pointer is reset and no more references to the underlying `TensorImplPtr` exist.
155+
The `TensorPtr` will call the custom deleter when it is destroyed, i.e., when the smart pointer is reset and no more references to the underlying `Tensor` exist.
159156
160157
#### Sharing Existing Tensor
161158
162-
You can create a `TensorPtr` by wrapping an existing `TensorImplPtr`, and the latter can be created with the same collection of APIs as `TensorPtr`. Any changes made to `TensorImplPtr` or any `TensorPtr` sharing the same `TensorImplPtr` are reflected across all.
163-
164-
*Sharing Existing TensorImplPtr*
165-
166-
```cpp
167-
auto tensor_impl = make_tensor_impl_ptr(
168-
{2, 3},
169-
{1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f});
170-
auto tensor = make_tensor_ptr(tensor_impl);
171-
auto tensor_copy = make_tensor_ptr(tensor_impl);
172-
```
173-
174-
Both `tensor` and `tensor_copy` share the underlying `TensorImplPtr`, reflecting changes to data but not to metadata.
175-
176-
Also, you can create a new `TensorPtr` that shares the same `TensorImplPtr` as an existing `TensorPtr`.
159+
Since `TensorPtr` is a `std::shared_ptr<Tensor>`, you can easily create a `TensorPtr` that shares an existing `Tensor`. Any changes made to the shared data are reflected across all instances sharing the same data.
177160
178161
*Sharing Existing TensorPtr*
179162
180163
```cpp
181-
auto tensor_copy = make_tensor_ptr(tensor);
164+
auto tensor = make_tensor_ptr({2, 3}, {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f});
165+
auto tensor_copy = tensor;
182166
```
183167

168+
Now `tensor` and `tensor_copy` point to the same data and metadata.
169+
184170
#### Viewing Existing Tensor
185171

186172
You can create a `TensorPtr` from an existing `Tensor`, copying its properties and referencing the same data.
@@ -203,11 +189,11 @@ Tensor original_tensor = /* some existing tensor */;
203189
auto tensor = clone_tensor_ptr(original_tensor);
204190
```
205191

206-
The newly created `TensorPtr` has its own copy of the data, so can modify and manage it independently.
192+
The newly created `TensorPtr` has its own copy of the data, so it can modify and manage it independently.
207193
Likewise, you can create a clone of an existing `TensorPtr`.
208194

209195
```cpp
210-
auto original_tensor = make_tensor_ptr();
196+
auto original_tensor = make_tensor_ptr(/* ... */);
211197
auto tensor = clone_tensor_ptr(original_tensor);
212198
```
213199

@@ -219,9 +205,9 @@ The `TensorShapeDynamism` enum specifies the mutability of a tensor's shape:
219205

220206
- `STATIC`: The tensor's shape cannot be changed.
221207
- `DYNAMIC_BOUND`: The tensor's shape can be changed but cannot contain more elements than it originally had at creation based on the initial sizes.
222-
- `DYNAMIC`: The tensor's shape can be changed arbitrarily. Note that, currently, `DYNAMIC` is an alias for `DYNAMIC_BOUND`.
208+
- `DYNAMIC`: The tensor's shape can be changed arbitrarily. Currently, `DYNAMIC` is an alias for `DYNAMIC_BOUND`.
223209

224-
When resizing a tensor, you must respect its dynamism setting. Resizing is only allowed for tensors with `DYNAMIC` or `DYNAMIC_BOUND` shapes, and you cannot resize `DYNAMIC_BOUND` tensor to contain more elements than it had initially.
210+
When resizing a tensor, you must respect its dynamism setting. Resizing is only allowed for tensors with `DYNAMIC` or `DYNAMIC_BOUND` shapes, and you cannot resize `DYNAMIC_BOUND` tensors to contain more elements than they had initially.
225211

226212
```cpp
227213
auto tensor = make_tensor_ptr(
@@ -378,7 +364,7 @@ This also applies when using functions like `set_input()` or `set_output()` that
378364
379365
## Interoperability with ATen
380366
381-
If your code is compiled with the preprocessor flag `USE_ATEN_LIB` enabled, all the `TensorPtr` APIs will use `at::` APIs under the hood. E.g. `TensorPtr` becomes a `std::unique_ptr<at::Tensor>` and `TensorImplPtr` becomes `c10::intrusive_ptr<at::TensorImpl>`. This allows for seamless integration with [PyTorch ATen](https://pytorch.org/cppdocs) library.
367+
If your code is compiled with the preprocessor flag `USE_ATEN_LIB` enabled, all the `TensorPtr` APIs will use `at::` APIs under the hood. E.g. `TensorPtr` becomes a `std::shared_ptr<at::Tensor>`. This allows for seamless integration with [PyTorch ATen](https://pytorch.org/cppdocs) library.
382368
383369
### API Equivalence Table
384370
@@ -413,14 +399,13 @@ Here's a table matching `TensorPtr` creation functions with their corresponding
413399
414400
## Best Practices
415401
416-
- *Manage Lifetimes Carefully*: Even though `TensorPtr` and `TensorImplPtr` handle memory management, you still need to ensure that any non-owned data (e.g., when using `from_blob()`) remains valid while the tensor is in use.
417-
- *Use Convenience Functions*: Utilize the provided helper functions for common tensor creation patterns to write cleaner and more readable code.
402+
- *Manage Lifetimes Carefully*: Even though `TensorPtr` handles memory management, ensure that any non-owned data (e.g., when using `from_blob()`) remains valid while the tensor is in use.
403+
- *Use Convenience Functions*: Utilize helper functions for common tensor creation patterns to write cleaner and more readable code.
418404
- *Be Aware of Data Ownership*: Know whether your tensor owns its data or references external data to avoid unintended side effects or memory leaks.
419-
- *Ensure TensorPtr Outlives EValue*: When passing tensors to modules that expect `EValue`, make sure the `TensorPtr` remains valid as long as the `EValue` is in use.
420-
- *Understand Scalar Types*: Be mindful of the scalar types when creating tensors, especially when casting between types.
405+
- *Ensure `TensorPtr` Outlives `EValue`*: When passing tensors to modules that expect `EValue`, ensure that the `TensorPtr` remains valid as long as the `EValue` is in use.
421406
422407
## Conclusion
423408
424-
The `TensorPtr` and `TensorImplPtr` in ExecuTorch simplify tensor memory management by bundling the data and dynamic metadata into smart pointers. This design eliminates the need for users to manage multiple pieces of data and ensures safer and more maintainable code.
409+
The `TensorPtr` in ExecuTorch simplifies tensor memory management by bundling the data and dynamic metadata into a smart pointer. This design eliminates the need for users to manage multiple pieces of data and ensures safer and more maintainable code.
425410
426411
By providing interfaces similar to PyTorch's ATen library, ExecuTorch simplifies the adoption of the new API, allowing developers to transition without a steep learning curve.

extension/tensor/targets.bzl

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,11 @@ def define_common_targets():
1313
runtime.cxx_library(
1414
name = "tensor" + aten_suffix,
1515
srcs = [
16-
"tensor_impl_ptr.cpp",
1716
"tensor_ptr.cpp",
1817
"tensor_ptr_maker.cpp",
1918
],
2019
exported_headers = [
2120
"tensor.h",
22-
"tensor_impl_ptr.h",
2321
"tensor_ptr.h",
2422
"tensor_ptr_maker.h",
2523
],

extension/tensor/tensor.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,5 @@
99
#pragma once
1010

1111
// Umbrella header for the Tensor extension.
12-
#include <executorch/extension/tensor/tensor_impl_ptr.h>
1312
#include <executorch/extension/tensor/tensor_ptr.h>
1413
#include <executorch/extension/tensor/tensor_ptr_maker.h>

extension/tensor/tensor_impl_ptr.cpp

Lines changed: 0 additions & 154 deletions
This file was deleted.

0 commit comments

Comments
 (0)