Repository navigation
fix(executorch): move compile_and_save's engines into the program instead of copying them - #5045
Draft
shoumikhin wants to merge 1 commit into
Draft
shoumikhin wants to merge 1 commit into
shoumikhin wants to merge 1 commit into
Conversation
shoumikhin
force-pushed
the
compile-and-save-consume
branch
from
November 15, 2026 01:40
03b81ce to
ba01aca
Compare
shoumikhin
force-pushed
the
compile-and-save-consume
branch
2 times, most recently
from
November 15, 2026 07:04
3cf1702 to
306a9de
Compare
shoumikhin
marked this pull request as draft
November 15, 2026 07:04
…tead of copying them `compile_and_save` writes each engine to a temporary file as soon as it is built. The ExecuTorch save then copies that file into the `.pte` (or the `.ptd`), and the file is deleted only when the call returns. So while the program is written, every engine is on disk twice. For a model whose engine is tens of gigabytes, the copy needs that much more free disk, and an export can fail on a device that has room for the result. The backend now hands each of these engine files to ExecuTorch as data that owns the file (`FileBackedData(path, cleanup=True)`); it is deleted after the save anyway. Inside `compile_and_save`, the program is then written with ExecuTorch's `save(file_path, consume=True)` and the `.ptd` with `write_tensor_data_to_file(..., consume=True)`, so ExecuTorch moves each engine into the program: it copies it back to front and truncates the file as it goes, and each engine is on disk once. ExecuTorch does this only when the temporary directory, where the engine files are, is on the same file system as the program; otherwise it copies. Its `save` takes only a `.pte` path, so a program saved under another name is written as before, with a copy. With `generate_etrecord`, the save copies the engines instead. The ETRecord is written after the program and reads the engines, so a move would leave it nothing to read. The program is still written first, as before, so it is kept when the ETRecord can't be written. The `save` docstring now says that `compile_and_save` can write an ETRecord. This needs the `consume` argument of `save` and `write_tensor_data_to_file` from pytorch/executorch#25099. It lands after that change, together with an ExecuTorch pin that has it, which also raises the minimum ExecuTorch version. Only the move passes `consume`, so every other save calls ExecuTorch exactly as before and keeps working with releases that don't have it. Test plan: - `tests/py/dynamo/executorch/test_api.py`, CPU, with the lowering stubbed: - New `test_compile_and_save_moves_the_engines`: inside `compile_and_save`'s offload block, the program and the `.ptd` are written with `consume=True`. It fails without this change. - New `test_compile_and_save_copies_the_engines_into_a_path_without_pte`: the same block with a `.bin` path writes both without `consume`. - New `test_compile_and_save_keeps_the_program_when_the_etrecord_fails`: in the same block with an ETRecord, the program and the `.ptd` are written first, without `consume`, and the `.pte` is kept when the ETRecord save raises. - New `test_save_executorch_keeps_the_program_when_the_etrecord_fails`: the same for a plain save. - New `test_save_executorch_does_not_consume`: a plain save writes without `consume`. The stubbed ExecuTorch raises TypeError for any `consume` other than `True`, as releases without it do, so this test fails if a plain save passes `consume=False`. - `tests/py/dynamo/executorch/test_compile_and_save.py` on an A100: - The streaming test now also checks that each engine reaches ExecuTorch as data that owns its file. - New `test_engines_move_into_the_program_without_a_second_copy` checks that ExecuTorch copied no engine and that both are in the `.pte`. - New `test_an_etrecord_makes_the_save_copy_the_engines`: with an ETRecord, the engine files keep their bytes, and both the `.pte` and the ETRecord are written.
shoumikhin
force-pushed
the
compile-and-save-consume
branch
from
November 15, 2026 14:02
306a9de to
7c3b535
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Depends on pytorch/executorch#25099; lands after it.
compile_and_saveuses theconsumeargument that change adds to ExecuTorch'ssaveandwrite_tensor_data_to_file, so the ExecuTorch pin bump to the first nightly that has it (which also raises the minimum ExecuTorch version) will be added to this PR once #25099 merges. Only the move passesconsume; every other save calls ExecuTorch exactly as before.What is wrong today
compile_and_savewrites each engine to a temporary file as soon as it is built. The ExecuTorch save then copies that file into the.pte(or the.ptd), and the file is deleted only when the call returns. So while the program is written, every engine is on disk twice. For a model whose engine is tens of gigabytes, the export needs that much more free disk, and it can fail on a device that has room for the result.What this change does
FileBackedData(path, cleanup=True)). The file is deleted after the save anyway.compile_and_save, the program is written with ExecuTorch'ssave(file_path, consume=True)and the.ptdwithwrite_tensor_data_to_file(..., consume=True). ExecuTorch then moves each engine into the program, so each engine is on disk once. ExecuTorch does this only when the temporary directory, where the engine files are, is on the same file system as the program. Itssavetakes only a.ptepath, so a program saved under another name is copied as before.generate_etrecord, the save copies the engines instead. The ETRecord is written after the program and reads the engines, so a move would leave it nothing to read. The program is still written first, as before, so it is kept when the ETRecord can't be written.compile_and_savecan write an ETRecord, unlike a plain save of a program with TensorRT engines.Tests
tests/py/dynamo/executorch/test_api.py, on CPU with the lowering stubbed:test_compile_and_save_moves_the_engines: in the offload block thatcompile_and_savesaves in, the program and the.ptdare written withconsume=True.test_compile_and_save_copies_the_engines_into_a_path_without_pte: the same with a.binpath writes both withoutconsume.test_compile_and_save_keeps_the_program_when_the_etrecord_fails: in the same block with an ETRecord, the program and the.ptdare written first, withoutconsume, and the.pteis kept when the ETRecord save fails.test_save_executorch_keeps_the_program_when_the_etrecord_fails: the same for a plain save.test_save_executorch_does_not_consume: a plain save writes withoutconsume. The stubbed ExecuTorch rejects anyconsumeother thanTrue, as releases without it do.tests/py/dynamo/executorch/test_compile_and_save.py, on an A100, with ExecuTorch from #25099:test_engines_move_into_the_program_without_a_second_copy: ExecuTorch copied no engine, and both are in the.pte;test_an_etrecord_makes_the_save_copy_the_engines: with an ETRecord, the engine files keep their bytes, and both the.pteand the ETRecord are written.