Skip to content

Commit f74f914

Browse files
authored
Make mmap an option for read_tensor_data (#29449)
### Details: - Make mmap an option for read_tensor_data ### Tickets: - [CVS-162941](https://jira.devtools.intel.com/browse/CVS-162941)
1 parent 871ab4a commit f74f914

File tree

3 files changed

+87
-32
lines changed

3 files changed

+87
-32
lines changed

src/core/include/openvino/runtime/tensor.hpp

+4-1
Original file line numberDiff line numberDiff line change
@@ -270,9 +270,12 @@ using TensorVector = std::vector<Tensor>;
270270
/// length of the file content and `offset`. Default value is [?].
271271
/// \param offset_in_bytes Read file starting from specified offset. Default is 0. The remining size of the file should
272272
/// be compatible with shape.
273+
/// \param mmap Use mmap that postpones real read from file until data is accessed. If mmap is used, the file
274+
/// should not be modified until returned tensor is destroyed.
273275
OPENVINO_API
274276
Tensor read_tensor_data(const std::filesystem::path& file_name,
275277
const element::Type& element_type = element::u8,
276278
const PartialShape& shape = PartialShape::dynamic(1),
277-
std::size_t offset_in_bytes = 0);
279+
std::size_t offset_in_bytes = 0,
280+
bool mmap = true);
278281
} // namespace ov

src/core/src/runtime/tensor.cpp

+32-14
Original file line numberDiff line numberDiff line change
@@ -162,26 +162,44 @@ ov::Shape calc_static_shape_for_file(const std::filesystem::path& file_name,
162162
dynamic_dimension = Dimension(new_dimension_size);
163163
return partial_shape_copy.get_shape();
164164
}
165+
166+
void read_tensor_data(const std::filesystem::path& file_name, Tensor& tensor, size_t offset) {
167+
OPENVINO_ASSERT(tensor.get_element_type() != ov::element::string);
168+
std::ifstream fin(file_name, std::ios::binary);
169+
fin.seekg(offset);
170+
auto bytes_to_read = tensor.get_byte_size();
171+
fin.read(static_cast<char*>(tensor.data()), bytes_to_read);
172+
OPENVINO_ASSERT(static_cast<size_t>(fin.gcount()) == bytes_to_read,
173+
"Cannot read ",
174+
bytes_to_read,
175+
"bytes from ",
176+
file_name);
177+
}
165178
} // namespace
166179

167180
Tensor read_tensor_data(const std::filesystem::path& file_name,
168181
const ov::element::Type& element_type,
169182
const ov::PartialShape& partial_shape,
170-
size_t offset_in_bytes) {
183+
size_t offset_in_bytes,
184+
bool mmap) {
171185
OPENVINO_ASSERT(element_type != ov::element::string);
172186
auto static_shape = calc_static_shape_for_file(file_name, element_type, partial_shape, offset_in_bytes);
173-
174-
auto mapped_memory = ov::load_mmap_object(file_name);
175-
auto shared_buffer =
176-
std::make_shared<ov::SharedBuffer<std::shared_ptr<ov::MappedMemory>>>(mapped_memory->data() + offset_in_bytes,
177-
mapped_memory->size() - offset_in_bytes,
178-
mapped_memory);
179-
180-
auto view_tensor = Tensor(element_type, static_shape, shared_buffer->get_ptr());
181-
auto impl = get_tensor_impl(view_tensor);
182-
impl._so = shared_buffer;
183-
view_tensor = make_tensor(impl);
184-
185-
return view_tensor;
187+
if (mmap) {
188+
auto mapped_memory = ov::load_mmap_object(file_name);
189+
auto shared_buffer = std::make_shared<ov::SharedBuffer<std::shared_ptr<ov::MappedMemory>>>(
190+
mapped_memory->data() + offset_in_bytes,
191+
mapped_memory->size() - offset_in_bytes,
192+
mapped_memory);
193+
194+
auto view_tensor = Tensor(element_type, static_shape, shared_buffer->get_ptr());
195+
auto impl = get_tensor_impl(view_tensor);
196+
impl._so = shared_buffer;
197+
view_tensor = make_tensor(impl);
198+
return view_tensor;
199+
} else {
200+
ov::Tensor tensor(element_type, static_shape);
201+
read_tensor_data(file_name, tensor, offset_in_bytes);
202+
return tensor;
203+
}
186204
}
187205
} // namespace ov

src/core/tests/tensor_utils.cpp

+51-17
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@ TYPED_TEST_P(ParametredOffloadTensorTest, read_tensor) {
5858
EXPECT_NO_THROW(tensor = read_tensor_data(this->file_name, this->ov_type, this->shape, 0));
5959
EXPECT_EQ(0, memcmp(tensor.data(), this->initial_tensor.data(), this->data_size));
6060
}
61+
{
62+
ov::Tensor tensor;
63+
EXPECT_NO_THROW(tensor = read_tensor_data(this->file_name, this->ov_type, this->shape, 0, false));
64+
EXPECT_EQ(0, memcmp(tensor.data(), this->initial_tensor.data(), this->data_size));
65+
}
6166
}
6267

6368
REGISTER_TYPED_TEST_SUITE_P(ParametredOffloadTensorTest, read_tensor);
@@ -80,18 +85,6 @@ using TypesToTest = ::testing::Types<float,
8085

8186
INSTANTIATE_TYPED_TEST_SUITE_P(OffloadTensorTest, ParametredOffloadTensorTest, TypesToTest);
8287

83-
TEST(OffloadTensorTest, string_tensor_throws) {
84-
auto file_name = ov::test::utils::generateTestFilePrefix();
85-
{
86-
std::ofstream fout(file_name, std::ios::binary);
87-
fout << "Hello, world!";
88-
fout.close();
89-
ASSERT_TRUE(std::filesystem::exists(file_name));
90-
EXPECT_THROW(read_tensor_data(file_name, ov::element::string), ov::Exception);
91-
std::filesystem::remove(file_name);
92-
}
93-
}
94-
9588
class FunctionalOffloadTensorTest : public ::testing::Test {
9689
public:
9790
void SetUp() override {
@@ -124,6 +117,14 @@ class FunctionalOffloadTensorTest : public ::testing::Test {
124117
std::string file_name;
125118
};
126119

120+
TEST_F(FunctionalOffloadTensorTest, string_tensor_throws) {
121+
{
122+
EXPECT_THROW(std::ignore = read_tensor_data(file_name, ov::element::string), ov::Exception);
123+
EXPECT_THROW(std::ignore = read_tensor_data(file_name, ov::element::string, PartialShape::dynamic(1), 0, false),
124+
ov::Exception);
125+
}
126+
}
127+
127128
TEST_F(FunctionalOffloadTensorTest, read_with_offset) {
128129
{
129130
float dummy = 0;
@@ -138,22 +139,33 @@ TEST_F(FunctionalOffloadTensorTest, read_with_offset) {
138139
EXPECT_NO_THROW(tensor = read_tensor_data(file_name, ov_type, shape, sizeof(float)));
139140
EXPECT_EQ(0, memcmp(tensor.data(), init_values.data(), data_size));
140141
}
142+
{
143+
ov::Tensor tensor;
144+
EXPECT_NO_THROW(tensor = read_tensor_data(file_name, ov_type, shape, sizeof(float), false));
145+
EXPECT_EQ(0, memcmp(tensor.data(), init_values.data(), data_size));
146+
}
141147
}
142148

143149
TEST_F(FunctionalOffloadTensorTest, read_small_file) {
144150
auto new_shape = shape;
145151
new_shape[0] = 10;
146-
{ EXPECT_THROW(std::ignore = read_tensor_data(file_name, ov_type, new_shape, 0), ov::Exception); }
152+
{
153+
EXPECT_THROW(std::ignore = read_tensor_data(file_name, ov_type, new_shape, 0), ov::Exception);
154+
EXPECT_THROW(std::ignore = read_tensor_data(file_name, ov_type, new_shape, 0, false), ov::Exception);
155+
}
147156
}
148157

149158
TEST_F(FunctionalOffloadTensorTest, read_too_big_offset) {
150159
{
151160
// offset + data_size > file_size
152161
EXPECT_THROW(std::ignore = read_tensor_data(file_name, ov_type, shape, 1), ov::Exception);
162+
EXPECT_THROW(std::ignore = read_tensor_data(file_name, ov_type, shape, 1, false), ov::Exception);
153163
// offset == file_size
154164
EXPECT_THROW(std::ignore = read_tensor_data(file_name, ov_type, shape, data_size), ov::Exception);
165+
EXPECT_THROW(std::ignore = read_tensor_data(file_name, ov_type, shape, data_size, false), ov::Exception);
155166
// offset > file_size
156167
EXPECT_THROW(std::ignore = read_tensor_data(file_name, ov_type, shape, data_size + 1), ov::Exception);
168+
EXPECT_THROW(std::ignore = read_tensor_data(file_name, ov_type, shape, data_size + 1, false), ov::Exception);
157169
}
158170
}
159171

@@ -163,23 +175,39 @@ TEST_F(FunctionalOffloadTensorTest, read_dynamic_shape) {
163175
EXPECT_NO_THROW(tensor = read_tensor_data(file_name, ov_type, PartialShape::dynamic(1), 0));
164176
EXPECT_EQ(0, memcmp(tensor.data(), init_values.data(), data_size));
165177
}
178+
{
179+
ov::Tensor tensor;
180+
EXPECT_NO_THROW(tensor = read_tensor_data(file_name, ov_type, PartialShape::dynamic(1), 0, false));
181+
EXPECT_EQ(0, memcmp(tensor.data(), init_values.data(), data_size));
182+
}
166183
{
167184
ov::Tensor tensor;
168185
EXPECT_NO_THROW(tensor = read_tensor_data(file_name));
169186
EXPECT_EQ(0, memcmp(tensor.data(), init_values.data(), data_size));
170187
}
188+
{
189+
ov::Tensor tensor;
190+
EXPECT_NO_THROW(tensor = read_tensor_data(file_name, ov::element::u8, PartialShape::dynamic(1), 0, false));
191+
EXPECT_EQ(0, memcmp(tensor.data(), init_values.data(), data_size));
192+
}
171193
}
172194

173195
TEST_F(FunctionalOffloadTensorTest, read_1_dynamic_dimension) {
196+
auto shape_with_1_dynamic_dimension = shape;
197+
size_t dynamic_dimension_number = shape_with_1_dynamic_dimension.size() - 1;
198+
shape_with_1_dynamic_dimension[dynamic_dimension_number] = -1;
174199
{
175200
ov::Tensor tensor;
176-
auto shape_with_1_dynamic_dimension = shape;
177-
size_t dynamic_dimension_number = shape_with_1_dynamic_dimension.size() - 1;
178-
shape_with_1_dynamic_dimension[dynamic_dimension_number] = -1;
179201
EXPECT_NO_THROW(tensor = read_tensor_data(file_name, ov_type, shape_with_1_dynamic_dimension, 0));
180202
EXPECT_EQ(tensor.get_shape()[dynamic_dimension_number], shape.get_shape()[dynamic_dimension_number]);
181203
EXPECT_EQ(0, memcmp(tensor.data(), init_values.data(), data_size));
182204
}
205+
{
206+
ov::Tensor tensor;
207+
EXPECT_NO_THROW(tensor = read_tensor_data(file_name, ov_type, shape_with_1_dynamic_dimension, 0, false));
208+
EXPECT_EQ(tensor.get_shape()[dynamic_dimension_number], shape.get_shape()[dynamic_dimension_number]);
209+
EXPECT_EQ(0, memcmp(tensor.data(), init_values.data(), data_size));
210+
}
183211
}
184212

185213
TEST_F(FunctionalOffloadTensorTest, read_wrong_dynamic_shape) {
@@ -189,6 +217,8 @@ TEST_F(FunctionalOffloadTensorTest, read_wrong_dynamic_shape) {
189217
shape_with_1_dynamic_dimension[shape_with_1_dynamic_dimension.size() - 2] = 100;
190218
EXPECT_THROW(std::ignore = read_tensor_data(file_name, ov_type, shape_with_1_dynamic_dimension, 0),
191219
ov::Exception);
220+
EXPECT_THROW(std::ignore = read_tensor_data(file_name, ov_type, shape_with_1_dynamic_dimension, 0, false),
221+
ov::Exception);
192222
}
193223
}
194224

@@ -199,6 +229,10 @@ TEST_F(FunctionalOffloadTensorTest, read_type_doesnt_fit_file_size) {
199229
}
200230
ASSERT_TRUE(std::filesystem::exists(file_name));
201231

202-
{ EXPECT_THROW(std::ignore = read_tensor_data(file_name, ov::element::f32), ov::Exception); }
232+
{
233+
EXPECT_THROW(std::ignore = read_tensor_data(file_name, ov::element::f32), ov::Exception);
234+
EXPECT_THROW(std::ignore = read_tensor_data(file_name, ov::element::f32, PartialShape::dynamic(1), 0, false),
235+
ov::Exception);
236+
}
203237
}
204238
} // namespace ov::test

0 commit comments

Comments
 (0)