You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit bd1e461
Browse filesBrowse the repository at this point in the historyBrowse files
Return an error instead of aborting when device memory cannot be allocated (pytorch#22085)
### What is going on
A model can be exported so that some of its memory-planned buffers live
on an
accelerator instead of on the CPU. When `Module` loads such a model, it
asks the
runtime for a block of memory on that device.
That request can fail for two ordinary reasons that say nothing about
the model
file being loaded:
1. this build has no allocator registered for that device type, or
2. the device is out of memory.
Both were checked with `ET_CHECK_MSG`, which terminates the whole
process. So
loading a device-annotated model on a machine without the matching
backend, or
on a busy GPU, killed the process outright. Through the Python bindings
(`_load_for_executorch_from_buffer` and friends build one of these
`Module`
objects) that shows up as an abort and a core dump: no traceback, no
error to
catch, no chance to fall back to the CPU.
Neither condition means the program is broken. They describe the machine
it is
running on, so `load_method` now returns the error to the caller and the
process stays alive.
Before this change, both reasons ended the same way:
```
module.load_method("forward"); // never returns, process aborts
```
After, the two reasons stay apart, because `DeviceMemoryBuffer::create`
already
reports them with different codes:
```
// nothing registered for that device type
module.load_method("forward"); // returns Error::NotFound
// allocator is there, the device has no room
module.load_method("forward"); // returns Error::MemoryAllocationFailed
```
### Also in this change
Two other `MethodMeta` lookups in the same function, one for the buffer
size
and one for the buffer device, also used `ET_CHECK_MSG`. They return the
error
now as well, so all three abort sites in that function are gone.
Separately, the loop that decides whether a model uses device buffers at
all
threw away a failed device query, which then read as "this buffer is on
the
CPU". It returns the error now instead of ignoring it.
That last path is not reachable today. The only case the query rejects
is an
out-of-range buffer index, and the loop bounds already keep the index in
range.
It is fixed for consistency, not because it can fire. Quietly treating a
failed
query as CPU would hand a backend host memory, which is a far harder
failure to
debug than an error return.
### Test coverage
`extension/module/test/module_device_memory_test.cpp` was registered in
the
internal build only, so no open source job ran any of it. This change
adds it
to `extension/module/test/CMakeLists.txt`, along with the model file it
needs,
so it now builds and runs as part of `extension_module_test`.
### Test plan
* New test
`ModuleDeviceMemoryTest.DeviceAllocationFailureIsReportedNotFatal`.
It makes the test allocator refuse the request, then checks that
`load_method` returns `MemoryAllocationFailed`, that the method is not
left
half loaded, and that a later attempt with the device healthy allocates
normally.
* Confirmed the test actually catches the bug. With this fix reverted,
the test
binary exits with signal 6 (abort) and prints no result at all. With the
fix,
all 6 tests in the suite pass.
* Ran the whole `extension_module_test` binary: 57 tests, all pass. The
51
pre-existing tests are unaffected by adding the device test file to the
same
binary.
* Ran `extension_module_test --gtest_repeat=3`: 57 tests pass on every
iteration, exit 0. The suite registers its allocator only once, so
process
reuse is safe. Put the unconditional registration back and the same
command
aborts with exit 134.
* `clang-format` reports no changes needed on the touched source file.
`cmake-format` 0.6.13 and `cmake-lint` report no changes needed on the
touched `CMakeLists.txt`.
### Not covered
Nothing here runs against real accelerator hardware. The failure is
driven
through the existing mock allocator, which stands in for a device that
is out
of memory. The committed test therefore covers the
`Error::MemoryAllocationFailed` branch only. The `Error::NotFound`
branch, for
a device type with no registered allocator, is not exercised by a
committed
test.
0 commit comments