Skip to content

Commit 88e9fb8

Browse files
Yi Lifacebook-github-bot
authored andcommitted
Extend data file support for other pybindings
Summary: This diff extends the support for external data_file to other executorch runtime pybindings. In Turing, executorch programs can be executed from both buffer or files. Differential Revision: D82046648
1 parent 2a06efb commit 88e9fb8

File tree

3 files changed

+95
-5
lines changed

3 files changed

+95
-5
lines changed

extension/pybindings/pybindings.cpp

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,11 +160,32 @@ void setup_output_storage(
160160

161161
inline std::unique_ptr<Module> load_module_from_buffer(
162162
const void* ptr,
163+
std::optional<const std::string>& data_map_path,
163164
size_t ptr_len,
164165
std::unique_ptr<runtime::EventTracer> event_tracer,
165166
Program::Verification program_verification) {
166167
EXECUTORCH_SCOPE_PROF("load_module_from_buffer");
167168
auto loader = std::make_unique<BufferDataLoader>(ptr, ptr_len);
169+
170+
if (data_map_path.has_value()) {
171+
Result<MmapDataLoader> data_map_loader_res = MmapDataLoader::from(
172+
data_map_path->c_str(),
173+
MmapDataLoader::MlockConfig::UseMlockIgnoreErrors);
174+
THROW_IF_ERROR(
175+
data_map_loader_res.error(),
176+
"Failed to create MmapDataLoader from file %s, error: 0x:%" PRIx32,
177+
data_map_path->c_str(),
178+
static_cast<uint32_t>(data_map_loader_res.error()));
179+
auto data_map_loader =
180+
std::make_unique<MmapDataLoader>(std::move(data_map_loader_res.get()));
181+
return std::make_unique<Module>(
182+
std::move(loader),
183+
nullptr, // memory_allocator
184+
nullptr, // temp_allocator
185+
std::move(event_tracer), // event_tracer
186+
std::move(data_map_loader)); // data_map_loader
187+
}
188+
168189
return std::make_unique<Module>(
169190
std::move(loader),
170191
nullptr, // memory_allocator
@@ -504,27 +525,31 @@ struct PyMethodMeta final {
504525
struct PyModule final {
505526
explicit PyModule(
506527
const py::bytes& buffer,
528+
std::optional<const std::string>& data_path,
507529
bool enable_etdump,
508530
size_t debug_buffer_size = 0,
509531
Program::Verification program_verification =
510532
Program::Verification::InternalConsistency)
511533
: debug_buffer_size_(debug_buffer_size),
512534
module_(load_module_from_buffer(
513535
buffer.cast<std::string_view>().data(),
536+
data_path,
514537
py::len(buffer),
515538
setup_event_tracer(enable_etdump, debug_buffer_size),
516539
program_verification)) {}
517540

518541
explicit PyModule(
519542
const void* ptr,
520543
size_t ptr_len,
544+
std::optional<const std::string>& data_path,
521545
bool enable_etdump,
522546
size_t debug_buffer_size = 0,
523547
Program::Verification program_verification =
524548
Program::Verification::InternalConsistency)
525549
: debug_buffer_size_(debug_buffer_size),
526550
module_(load_module_from_buffer(
527551
ptr,
552+
data_path,
528553
ptr_len,
529554
setup_event_tracer(enable_etdump, debug_buffer_size),
530555
program_verification)) {}
@@ -551,12 +576,13 @@ struct PyModule final {
551576
// Module is only valid as long as the python buffer is alive.
552577
static std::unique_ptr<PyModule> load_from_buffer(
553578
const py::bytes& buffer,
579+
std::optional<const std::string>& data_path,
554580
bool enable_etdump,
555581
size_t debug_buffer_size = 0,
556582
Program::Verification program_verification =
557583
Program::Verification::InternalConsistency) {
558584
return std::make_unique<PyModule>(
559-
buffer, enable_etdump, debug_buffer_size, program_verification);
585+
buffer, data_path, enable_etdump, debug_buffer_size, program_verification);
560586
}
561587

562588
static std::unique_ptr<PyModule> load_from_file(
@@ -576,11 +602,13 @@ struct PyModule final {
576602

577603
static std::unique_ptr<PyModule> load_from_bundled_program(
578604
PyBundledModule& m,
605+
std::optional<const std::string>& data_path,
579606
bool enable_etdump,
580607
size_t debug_buffer_size = 0) {
581608
return std::make_unique<PyModule>(
582609
m.get_program_ptr(),
583610
m.get_program_len(),
611+
data_path,
584612
enable_etdump,
585613
debug_buffer_size);
586614
}
@@ -1390,6 +1418,7 @@ PYBIND11_MODULE(EXECUTORCH_PYTHON_MODULE_NAME, m) {
13901418
"_load_for_executorch_from_buffer",
13911419
&PyModule::load_from_buffer,
13921420
py::arg("buffer"),
1421+
py::arg("data_path") = std::nullopt,
13931422
py::arg("enable_etdump") = false,
13941423
py::arg("debug_buffer_size") = 0,
13951424
py::arg("program_verification") =
@@ -1399,6 +1428,7 @@ PYBIND11_MODULE(EXECUTORCH_PYTHON_MODULE_NAME, m) {
13991428
"_load_for_executorch_from_bundled_program",
14001429
&PyModule::load_from_bundled_program,
14011430
py::arg("ptr"),
1431+
py::arg("data_path") = std::nullopt,
14021432
py::arg("enable_etdump") = false,
14031433
py::arg("debug_buffer_size") = 0,
14041434
call_guard);

extension/pybindings/test/TARGETS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ runtime.python_library(
2424
"//executorch/exir/emit:lib",
2525
"//executorch/exir/passes:lib",
2626
"//executorch/runtime/core:core",
27+
"//executorch/devtools/bundled_program:core",
28+
"//executorch/devtools/bundled_program:config",
29+
"//executorch/devtools/bundled_program/serialize:lib",
2730
],
2831
)
2932

extension/pybindings/test/test_pybindings.py

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -651,8 +651,65 @@ def test_program_data_separation(self) -> None:
651651
)
652652
ptd.write(tensor_data)
653653

654-
executorch_program = self.runtime._load_for_executorch(pte_file, ptd_file)
655-
656654
expected = eager_module(inputs[0])
657-
executorch_output = executorch_program.forward(inputs)[0]
658-
self.assertTrue(torch.allclose(expected, executorch_output))
655+
656+
# Test 1: File-based loading with external data file
657+
executorch_module_file = self.runtime._load_for_executorch(pte_file, ptd_file)
658+
executorch_output_file = executorch_module_file.forward(inputs)[0]
659+
self.assertTrue(torch.allclose(expected, executorch_output_file))
660+
661+
# Test 2: Buffer-based loading with external data file
662+
with open(pte_file, "rb") as f:
663+
program_buffer = f.read()
664+
executorch_module_buffer = self.load_fn(program_buffer, ptd_file)
665+
executorch_output_buffer = executorch_module_buffer.forward(inputs)[0]
666+
self.assertTrue(torch.allclose(expected, executorch_output_buffer))
667+
668+
# Test 3: Buffer-based loading without external data file (should fail or work differently)
669+
# This should fail because the program expects external data
670+
with self.assertRaises(RuntimeError):
671+
executorch_module_no_data = self.load_fn(program_buffer)
672+
executorch_module_no_data.forward(inputs)
673+
674+
# Test 4: Test with invalid data file path (should fail)
675+
invalid_ptd_file = os.path.join(tmpdir, "nonexistent.ptd")
676+
with self.assertRaises(RuntimeError):
677+
self.load_fn(program_buffer, invalid_ptd_file)
678+
679+
# Test 5: Test bundled program loading with external data
680+
# First create a bundled program with external constants
681+
from executorch.devtools.bundled_program.core import BundledProgram
682+
from executorch.devtools.bundled_program.config import MethodTestCase, MethodTestSuite
683+
from executorch.devtools.bundled_program.serialize import (
684+
serialize_from_bundled_program_to_flatbuffer,
685+
)
686+
687+
method_test_suites = [
688+
MethodTestSuite(
689+
method_name="forward",
690+
test_cases=[
691+
MethodTestCase(
692+
inputs=input,
693+
expected_outputs=expected,
694+
)
695+
for input in inputs
696+
],
697+
),
698+
]
699+
bundled_program = BundledProgram(exec_program, method_test_suites)
700+
bundled_buffer = serialize_from_bundled_program_to_flatbuffer(bundled_program)
701+
bundled_module = self.runtime._load_bundled_program_from_buffer(bundled_buffer)
702+
703+
# Load module from bundled program with external data
704+
executorch_module_bundled = self.runtime._load_for_executorch_from_bundled_program(
705+
bundled_module, ptd_file
706+
)
707+
executorch_output_bundled = executorch_module_bundled.forward(inputs)[0]
708+
self.assertTrue(torch.allclose(expected, executorch_output_bundled))
709+
710+
# Test 6: Bundled program without external data should fail
711+
with self.assertRaises(RuntimeError):
712+
executorch_module_bundled_no_data = self.runtime._load_for_executorch_from_bundled_program(
713+
bundled_module
714+
)
715+
executorch_module_bundled_no_data.forward(inputs)

0 commit comments

Comments
 (0)