You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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
Copy file name to clipboardExpand all lines: docs/source/extension-tensor.md
+23-38Lines changed: 23 additions & 38 deletions
Original file line number
Diff line number
Diff line change
@@ -31,9 +31,9 @@ You must ensure `sizes`, `dim_order`, `strides`, and `data` stay valid. This mak
31
31
32
32
## Introducing TensorPtr
33
33
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.
35
35
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.
37
37
38
38
Here’s how you can use it:
39
39
@@ -50,16 +50,13 @@ auto tensor = make_tensor_ptr(
50
50
module.forward(tensor);
51
51
```
52
52
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.
54
54
55
55
`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.
56
56
57
57
## API Overview
58
58
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.
63
60
64
61
### Creating Tensors
65
62
@@ -75,15 +72,15 @@ You can create a scalar tensor, i.e. a tensor with zero dimensions or with one o
75
72
auto tensor = make_tensor_ptr(3.14);
76
73
```
77
74
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.
79
76
80
77
*Providing A Single Data Value with a Type*
81
78
82
79
```cpp
83
80
auto tensor = make_tensor_ptr(42, ScalarType::Float);
84
81
```
85
82
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.
87
84
88
85
#### Owning Data from a Vector
89
86
@@ -139,7 +136,7 @@ auto tensor = make_tensor_ptr(
139
136
ScalarType::Float); // float scalar type
140
137
```
141
138
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.
143
140
144
141
*Providing Raw Data with Custom Deleter*
145
142
@@ -151,36 +148,25 @@ auto tensor = make_tensor_ptr(
151
148
{2, 3}, // sizes
152
149
data, // data pointer
153
150
ScalarType::Double, // double scalar type
154
-
TensorShapeDynamism::DYNAMIC_BOUND, // some default dynamism
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.
159
156
160
157
#### Sharing Existing Tensor
161
158
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.
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.
207
193
Likewise, you can create a clone of an existing `TensorPtr`.
208
194
209
195
```cpp
210
-
auto original_tensor = make_tensor_ptr();
196
+
auto original_tensor = make_tensor_ptr(/* ... */);
211
197
auto tensor = clone_tensor_ptr(original_tensor);
212
198
```
213
199
@@ -219,9 +205,9 @@ The `TensorShapeDynamism` enum specifies the mutability of a tensor's shape:
219
205
220
206
-`STATIC`: The tensor's shape cannot be changed.
221
207
-`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`.
223
209
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.
225
211
226
212
```cpp
227
213
auto tensor = make_tensor_ptr(
@@ -378,7 +364,7 @@ This also applies when using functions like `set_input()` or `set_output()` that
378
364
379
365
## Interoperability with ATen
380
366
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.
382
368
383
369
### API Equivalence Table
384
370
@@ -413,14 +399,13 @@ Here's a table matching `TensorPtr` creation functions with their corresponding
413
399
414
400
## Best Practices
415
401
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.
418
404
- *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.
421
406
422
407
## Conclusion
423
408
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.
425
410
426
411
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.
0 commit comments