Skip to content

Commit c3460e5

Browse files
shoumikhinfacebook-github-bot
authored andcommitted
Use aliasing constructor instead of a custom deleter in TensorImplPtr. (#5725)
Summary: Pull Request resolved: #5725 `shared_ptr` has a so-called aliasing constructor, that allows it to manage one object, but effectively point to another one. Here we use it to keep the data/metadata bundled together, but make the `shared_ptr` expose the address of the `TensorImpl` instance only. Reviewed By: dbort Differential Revision: D63545997 fbshipit-source-id: 374d8a0ed1a1558608d59246b4c6c44cb0c13398
1 parent a5c9dee commit c3460e5

File tree

1 file changed

+42
-43
lines changed

1 file changed

+42
-43
lines changed

extension/tensor/tensor_impl_ptr.cpp

Lines changed: 42 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -17,37 +17,37 @@ namespace executorch {
1717
namespace extension {
1818
namespace {
1919
#ifndef USE_ATEN_LIB
20-
// No-op deleter that does nothing when called.
21-
static void noop_deleter(void*) {}
22-
2320
/**
24-
* Custom deleter for TensorImplPtr that ensures the memory associated with
25-
* dynamic metadata (sizes, dim_order, and strides) is properly managed when the
26-
* TensorImpl is destroyed.
27-
*
28-
* Since TensorImpl does not own the metadata arrays (sizes, dim_order,
29-
* strides), this deleter is responsible for releasing that memory when the
30-
* TensorImpl is destroyed.
21+
* A structure that consolidates the metadata (sizes, dim_order, strides) and
22+
* the data buffer associated with a TensorImpl. Since TensorImpl does not own
23+
* the memory for these metadata arrays or the data itself, this structure
24+
* ensures that they are managed together and have the same lifetime as the
25+
* TensorImpl. When the TensorImpl is destroyed, the Storage structure ensures
26+
* proper cleanup of the associated metadata and data if needed.
3127
*/
32-
struct TensorImplPtrDeleter final {
33-
// A custom deleter of the std::shared_ptr is required to be copyable until
34-
// C++20, so any data it holds must be copyable too. Hence, we use shared_ptr
35-
// to hold the data and metadata to avoid unnecessary copies.
36-
std::shared_ptr<void> data;
37-
std::shared_ptr<std::vector<exec_aten::SizesType>> sizes;
38-
std::shared_ptr<std::vector<exec_aten::DimOrderType>> dim_order;
39-
std::shared_ptr<std::vector<exec_aten::StridesType>> strides;
28+
struct Storage final {
29+
exec_aten::TensorImpl tensor_impl;
30+
std::vector<exec_aten::SizesType> sizes;
31+
std::vector<exec_aten::DimOrderType> dim_order;
32+
std::vector<exec_aten::StridesType> strides;
33+
std::function<void(void*)> deleter;
4034

41-
void operator()(exec_aten::TensorImpl* pointer) {
42-
// Release all resources immediately since the data held by the
43-
// TensorImplPtrDeleter is tied to the managed object, not the smart pointer
44-
// itself. We need to free this memory when the object is destroyed, not
45-
// when the smart pointer (and deleter) are eventually destroyed or reset.
46-
data.reset();
47-
sizes.reset();
48-
dim_order.reset();
49-
strides.reset();
50-
delete pointer;
35+
Storage(
36+
exec_aten::TensorImpl&& tensor_impl,
37+
std::vector<exec_aten::SizesType>&& sizes,
38+
std::vector<exec_aten::DimOrderType>&& dim_order,
39+
std::vector<exec_aten::StridesType>&& strides,
40+
std::function<void(void*)>&& deleter)
41+
: tensor_impl(std::move(tensor_impl)),
42+
sizes(std::move(sizes)),
43+
dim_order(std::move(dim_order)),
44+
strides(std::move(strides)),
45+
deleter(std::move(deleter)) {}
46+
47+
~Storage() {
48+
if (deleter) {
49+
deleter(tensor_impl.mutable_data());
50+
}
5151
}
5252
};
5353
#endif // USE_ATEN_LIB
@@ -89,24 +89,23 @@ TensorImplPtr make_tensor_impl_ptr(
8989
strides = std::move(computed_strides);
9090
}
9191
#ifndef USE_ATEN_LIB
92-
auto tensor_impl = std::make_unique<exec_aten::TensorImpl>(
92+
exec_aten::TensorImpl tensor_impl(
9393
type,
9494
dim,
9595
sizes.data(),
9696
data,
9797
dim_order.data(),
9898
strides.data(),
9999
dim > 0 ? dynamism : exec_aten::TensorShapeDynamism::STATIC);
100-
return TensorImplPtr(
101-
tensor_impl.release(),
102-
TensorImplPtrDeleter{
103-
std::shared_ptr<void>(
104-
data, deleter ? std::move(deleter) : noop_deleter),
105-
std::make_shared<std::vector<exec_aten::SizesType>>(std::move(sizes)),
106-
std::make_shared<std::vector<exec_aten::DimOrderType>>(
107-
std::move(dim_order)),
108-
std::make_shared<std::vector<exec_aten::StridesType>>(
109-
std::move(strides))});
100+
auto storage = std::make_shared<Storage>(
101+
std::move(tensor_impl),
102+
std::move(sizes),
103+
std::move(dim_order),
104+
std::move(strides),
105+
std::move(deleter));
106+
const auto tensor_impl_ptr = &storage->tensor_impl;
107+
return std::shared_ptr<exec_aten::TensorImpl>(
108+
std::move(storage), tensor_impl_ptr);
110109
#else
111110
auto options = c10::TensorOptions()
112111
.dtype(c10::scalarTypeToTypeMeta(type))
@@ -139,16 +138,16 @@ TensorImplPtr make_tensor_impl_ptr(
139138
data.size() >= exec_aten::compute_numel(sizes.data(), sizes.size()) *
140139
exec_aten::elementSize(type),
141140
"Data size is smaller than required by sizes and scalar type.");
142-
auto raw_data_ptr = data.data();
143-
auto data_ptr = std::make_shared<std::vector<uint8_t>>(std::move(data));
141+
auto data_ptr = data.data();
144142
return make_tensor_impl_ptr(
145143
std::move(sizes),
146-
raw_data_ptr,
144+
data_ptr,
147145
std::move(dim_order),
148146
std::move(strides),
149147
type,
150148
dynamism,
151-
[data_ptr = std::move(data_ptr)](void*) {});
149+
// Data is moved into the deleter and is destroyed together with Storage.
150+
[data = std::move(data)](void*) {});
152151
}
153152

154153
} // namespace extension

0 commit comments

Comments
 (0)