Skip to content

Commit 0f49134

Browse files
committed
[executorch][flat_tensor] DataMap implementation
Pull Request resolved: #7900 DataMap implementation that * Loads a flat_tensor file * Populates a map with {fqn: tensor} and {fqn: TensorLayout}. * Makes tensor information available via the named_data_map.h interface. For now, DataMap doesn't store the DataLoader. - If/when tensors are in their own segments, DataMap should also store a DataLoader. ghstack-source-id: 262941711 Differential Revision: [D67064580](https://our.internmc.facebook.com/intern/diff/D67064580/)
1 parent 2c08e55 commit 0f49134

File tree

8 files changed

+503
-2
lines changed

8 files changed

+503
-2
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
load("@fbsource//xplat/executorch/build:runtime_wrapper.bzl", "runtime")
2+
load(":targets.bzl", "define_common_targets")
3+
4+
oncall("executorch")
5+
6+
define_common_targets()
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
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+
#include <executorch/extension/flat_tensor/named_data_map/data_map.h>
10+
#include <executorch/extension/flat_tensor/serialize/flat_tensor_header.h>
11+
#include <executorch/extension/flat_tensor/serialize/schema_generated.h>
12+
#include <executorch/runtime/core/error.h>
13+
#include <executorch/runtime/core/exec_aten/util/tensor_util.h>
14+
#include <executorch/runtime/core/freeable_buffer.h>
15+
#include <executorch/runtime/core/result.h>
16+
#include <executorch/runtime/core/span.h>
17+
#include <executorch/runtime/platform/compiler.h>
18+
19+
#include <tuple>
20+
#include <unordered_map>
21+
22+
using executorch::runtime::Error;
23+
using executorch::runtime::FreeableBuffer;
24+
using executorch::runtime::Result;
25+
using executorch::runtime::Span;
26+
27+
using executorch::aten::ScalarType;
28+
using executorch::runtime::DataLoader;
29+
using executorch::runtime::TensorLayout;
30+
31+
namespace executorch {
32+
namespace extension {
33+
34+
namespace {
35+
/**
36+
* FlatTensor data must be aligned to this value to properly parse it. Must be a
37+
* power of 2. Note that max_align_t is the alignment that malloc() and new
38+
* guarantee.
39+
*/
40+
constexpr size_t kMinimumAlignment = alignof(std::max_align_t);
41+
42+
bool IsAligned(const void* data) {
43+
uintptr_t addr = reinterpret_cast<uintptr_t>(data);
44+
return addr % kMinimumAlignment == 0;
45+
}
46+
} // namespace
47+
48+
ET_NODISCARD Result<const TensorLayout> DataMap::get_metadata(
49+
const char* fqn) const {
50+
auto result = _name_to_tensor.find(fqn);
51+
if (result == _name_to_tensor.end()) {
52+
return Error::InvalidArgument;
53+
}
54+
// value is a tuple of (segment_index, offset, tensor_layout)
55+
return std::get<2>(result->second);
56+
}
57+
58+
ET_NODISCARD Result<FreeableBuffer> DataMap::get_data(const char* fqn) const {
59+
auto result = _name_to_tensor.find(fqn);
60+
if (result == _name_to_tensor.end()) {
61+
return Error::InvalidArgument;
62+
}
63+
int offset = std::get<1>(result->second);
64+
TensorLayout tensor = std::get<2>(result->second);
65+
66+
const uint8_t* data = static_cast<const uint8_t*>(_data_ro.data()) + offset;
67+
return FreeableBuffer(data, tensor.nbytes(), nullptr);
68+
}
69+
70+
ET_NODISCARD Result<size_t>
71+
DataMap::load_data_into(const char* fqn, size_t size, void* buffer) const {
72+
return Error::NotImplemented;
73+
}
74+
75+
ET_NODISCARD Result<size_t> DataMap::get_num_keys() const {
76+
return _name_to_tensor.size();
77+
}
78+
79+
ET_NODISCARD Result<const char*> DataMap::get_key(size_t index) const {
80+
if (index < 0 || index >= _name_to_tensor.size()) {
81+
return Error::InvalidArgument;
82+
}
83+
84+
auto iter = _name_to_tensor.begin();
85+
for (int i = 0; i < index; ++i) {
86+
++iter;
87+
}
88+
return iter->first.c_str();
89+
}
90+
91+
/* static */ Result<DataMap> DataMap::load(DataLoader* loader) {
92+
// Load data map.
93+
size_t flatbuffer_offset = 0;
94+
size_t flatbuffer_size = 0;
95+
size_t segment_base_offset = 0;
96+
size_t segment_data_size = 0;
97+
{
98+
// Check header.
99+
Result<FreeableBuffer> header = loader->load(
100+
/*offset=*/0,
101+
FlatTensorHeader::kNumHeadBytes,
102+
DataLoader::SegmentInfo(DataLoader::SegmentInfo::Type::External));
103+
if (!header.ok()) {
104+
return header.error();
105+
}
106+
Result<FlatTensorHeader> fh =
107+
FlatTensorHeader::Parse(header->data(), header->size());
108+
if (fh.ok()) {
109+
// The header has the data map size.
110+
flatbuffer_offset = fh->flatbuffer_offset;
111+
flatbuffer_size = fh->flatbuffer_size;
112+
segment_base_offset = fh->segment_base_offset;
113+
segment_data_size = fh->segment_data_size;
114+
} else if (fh.error() == Error::NotFound) {
115+
// No header, throw error.
116+
ET_LOG(Error, "No FlatTensorHeader found.");
117+
return fh.error();
118+
} else {
119+
// corruption, throw error.
120+
ET_LOG(Error, "Flat tensor header may be corrupt.");
121+
return fh.error();
122+
}
123+
}
124+
125+
// Load flatbuffer data as a segment.
126+
Result<FreeableBuffer> flat_tensor_data = loader->load(
127+
/*offset=*/flatbuffer_offset,
128+
flatbuffer_size,
129+
DataLoader::SegmentInfo(DataLoader::SegmentInfo::Type::External));
130+
if (!flat_tensor_data.ok()) {
131+
return flat_tensor_data.error();
132+
}
133+
134+
// Make sure magic matches.
135+
if (!flat_tensor::FlatTensorBufferHasIdentifier(flat_tensor_data->data())) {
136+
ET_LOG(
137+
Error,
138+
"FlatTensor identifier '%.4s' != expected '%.4s'",
139+
flatbuffers::GetBufferIdentifier(flat_tensor_data->data()),
140+
flat_tensor::FlatTensorIdentifier());
141+
return Error::InvalidExternalData;
142+
}
143+
144+
// The flatbuffer data must start at an aligned address to ensure internal
145+
// alignment of flatbuffer fields.
146+
ET_CHECK_OR_RETURN_ERROR(
147+
IsAligned(flat_tensor_data->data()),
148+
InvalidArgument,
149+
"FlatTensor data 0x%p must be aligned to %zu",
150+
flat_tensor_data->data(),
151+
kMinimumAlignment);
152+
153+
// Get pointer to root of flatbuffer table.
154+
const flat_tensor::FlatTensor* flat_tensor =
155+
flat_tensor::GetFlatTensor(flat_tensor_data->data());
156+
157+
// Get pointer to tensor metadata.
158+
const auto* s_tensor_metadata = flat_tensor->tensors();
159+
assert(s_tensor_metadata != nullptr);
160+
161+
std::unordered_map<std::string, std::tuple<int, int, TensorLayout>>
162+
name_to_tensor = {};
163+
for (int i = 0; i < s_tensor_metadata->size(); i++) {
164+
// Create TensorLayouts.
165+
ScalarType scalar_type =
166+
static_cast<ScalarType>(s_tensor_metadata->Get(i)->scalar_type());
167+
const int dim = s_tensor_metadata->Get(i)->sizes()->size();
168+
169+
const auto serialized_sizes = s_tensor_metadata->Get(i)->sizes()->data();
170+
const auto serialized_dim_order =
171+
s_tensor_metadata->Get(i)->dim_order()->data();
172+
TensorLayout tensor_layout = TensorLayout(
173+
scalar_type,
174+
Span<const int32_t>(serialized_sizes, dim),
175+
Span<const uint8_t>(serialized_dim_order, dim));
176+
177+
int segment_index = s_tensor_metadata->Get(i)->segment_index();
178+
int offset = s_tensor_metadata->Get(i)->offset();
179+
std::string fqn = s_tensor_metadata->Get(i)->fully_qualified_name()->str();
180+
181+
auto val = std::make_tuple(segment_index, offset, tensor_layout);
182+
name_to_tensor.insert({fqn, std::move(val)});
183+
}
184+
185+
// Load constant data.
186+
const auto* s_data_segment = flat_tensor->segments();
187+
188+
// Only support one segment for now.
189+
assert(s_data_segment->size() == 1);
190+
// First segment offset should be 0.
191+
int segment_offset = s_data_segment->Get(0)->offset();
192+
assert(segment_offset == 0);
193+
// First segment size should be <= the total segment data size.
194+
int segment_size = s_data_segment->Get(0)->size();
195+
assert(segment_size <= segment_data_size);
196+
197+
Result<FreeableBuffer> _data_ro = loader->load(
198+
/*offset=*/segment_base_offset + segment_offset,
199+
segment_size,
200+
DataLoader::SegmentInfo(DataLoader::SegmentInfo::Type::External));
201+
if (!_data_ro.ok()) {
202+
return _data_ro.error();
203+
}
204+
205+
return DataMap(
206+
std::move(flat_tensor_data.get()),
207+
std::move(name_to_tensor),
208+
std::move(_data_ro.get()));
209+
}
210+
211+
DataMap::~DataMap() {}
212+
213+
} // namespace extension
214+
} // namespace executorch
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
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/runtime/core/data_loader.h>
12+
#include <executorch/runtime/core/exec_aten/exec_aten.h>
13+
#include <executorch/runtime/core/named_data_map.h>
14+
#include <executorch/runtime/core/result.h>
15+
#include <executorch/runtime/core/tensor_layout.h>
16+
#include <executorch/runtime/platform/compiler.h>
17+
18+
#include <unordered_map>
19+
#include <utility>
20+
21+
// Forward declare flatbuffer types. This is a public header and must not
22+
// include the generated flatbuffer header.
23+
namespace flat_tensor {
24+
struct FlatTensor;
25+
} // namespace flat_tensor
26+
27+
namespace executorch {
28+
namespace extension {
29+
30+
class DataMap final : public executorch::runtime::NamedDataMap {
31+
public:
32+
static executorch::runtime::Result<DataMap> load(
33+
executorch::runtime::DataLoader* loader);
34+
35+
ET_NODISCARD
36+
executorch::runtime::Result<const executorch::runtime::TensorLayout>
37+
get_metadata(const char* fqn) const override;
38+
ET_NODISCARD
39+
executorch::runtime::Result<executorch::runtime::FreeableBuffer> get_data(
40+
const char* fqn) const override;
41+
ET_NODISCARD executorch::runtime::Result<size_t>
42+
load_data_into(const char* fqn, size_t size, void* buffer) const override;
43+
44+
ET_NODISCARD executorch::runtime::Result<size_t> get_num_keys()
45+
const override;
46+
ET_NODISCARD executorch::runtime::Result<const char*> get_key(
47+
size_t index) const override;
48+
49+
DataMap(DataMap&&) noexcept = default;
50+
~DataMap() override;
51+
52+
private:
53+
DataMap(
54+
executorch::runtime::FreeableBuffer&& flat_tensor_data,
55+
std::unordered_map<
56+
std::string,
57+
std::tuple<int, int, executorch::runtime::TensorLayout>>
58+
name_to_tensor,
59+
executorch::runtime::FreeableBuffer&& data_ro)
60+
: _flat_tensor_data(std::move(flat_tensor_data)),
61+
_name_to_tensor(std::move(name_to_tensor)),
62+
_data_ro(std::move(data_ro)) {}
63+
64+
// Not copyable or assignable.
65+
DataMap(const DataMap& rhs) = delete;
66+
DataMap& operator=(DataMap&& rhs) noexcept = delete;
67+
DataMap& operator=(const DataMap& rhs) = delete;
68+
69+
// FlatTensor flatbuffer data. Contains the data backing up
70+
// TensorLayout information in the _name_to_tensor map; must outlive it.
71+
executorch::runtime::FreeableBuffer _flat_tensor_data;
72+
73+
// Map of name to {segment index, offset, TensorLayout}.
74+
std::unordered_map<
75+
std::string,
76+
std::tuple<int, int, executorch::runtime::TensorLayout>>
77+
_name_to_tensor;
78+
79+
// Raw, read-only tensor data.
80+
executorch::runtime::FreeableBuffer _data_ro;
81+
};
82+
83+
} // namespace extension
84+
} // namespace executorch
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
load("@fbsource//xplat/executorch/build:runtime_wrapper.bzl", "runtime")
2+
3+
def define_common_targets():
4+
runtime.cxx_library(
5+
name = "data_map",
6+
srcs = [
7+
"data_map.cpp",
8+
],
9+
exported_headers = ["data_map.h"],
10+
deps = [
11+
"//executorch/extension/flat_tensor/serialize:schema",
12+
"//executorch/extension/flat_tensor/serialize:serialize",
13+
"//executorch/extension/flat_tensor/serialize:generated_headers",
14+
"//executorch/extension/flat_tensor/serialize:flat_tensor_header",
15+
"//executorch/runtime/core:core",
16+
"//executorch/runtime/core:evalue",
17+
"//executorch/runtime/core/exec_aten:lib",
18+
"//executorch/runtime/core/exec_aten/util:tensor_util",
19+
],
20+
visibility = [
21+
"//executorch/...",
22+
],
23+
)

extension/flat_tensor/test/TARGETS

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ load(":targets.bzl", "define_common_targets")
66

77
oncall("executorch")
88

9-
define_common_targets()
9+
define_common_targets(is_fbcode=True)
1010

1111
python_unittest(
1212
name = "serialize",

0 commit comments

Comments
 (0)