Skip to content

Commit 03b17e1

Browse files
committed
[executorch][flat_tensor] DataMap implementation
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. Differential Revision: [D67064580](https://our.internmc.facebook.com/intern/diff/D67064580/) ghstack-source-id: 262756077 Pull Request resolved: #7900
1 parent c9437f7 commit 03b17e1

File tree

8 files changed

+455
-2
lines changed

8 files changed

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