Add native (DeepNVMe) host-memory pinning backend for accelerators - #8211
Add native (DeepNVMe) host-memory pinning backend for accelerators#8211sfc-gh-truwase wants to merge 12 commits into
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: cee4d7771e
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
|
I think host memory pinning accouting is needed. I made an attempt in the following PR (in order to justfy pin_memory as default offload option). |
Introduce a native host-memory pinning backend selectable via the DS_PIN_MEMORY_BACKEND env var (defaults to "torch"). When set to "native", CPU memory is page-locked through the DeepNVMe async-io (mlock) allocator instead of torch.pin_memory(). - deepspeed/utils/pin_memory.py: new NativePinnedMemory manager (process-wide shared singleton) that pins CPU memory, tracks pinned pointer ranges so slices/views report as pinned, tags buffers with .ds_pinned, supports make_copy/match_shape, and frees on unpin. Construction fails early with a clear error if the async-io op cannot be built. - Accelerator owns the native-vs-torch dispatch. pin_memory drops align_bytes and gains make_copy/match_shape; is_pinned is FakeTensor/meta-tensor safe; add unpin_memory (native frees, torch is a no-op). Subclasses keep only the device-specific _torch_pin_memory/_torch_is_pinned primitives. - Consolidate XPU's bespoke native pinning path into the shared backend. - Route swap_tensor and compile callers through the accelerator. - Add unit tests for the native manager and the accelerator pinning APIs, and move the accelerator tests under tests/unit/v1. Signed-off-by: Olatunji Ruwase <tunji.ruwase@snowflake.com> Co-authored-by: Cursor <cursoragent@cursor.com>
cee4d77 to
f7934bc
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f7934bc8d3
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
|
Addressed Codex review (r3713658448) by landing #8212 first, then rebasing this PR onto updated With the process-wide shared |
Native pin_memory() allocations are page-locked through the AIO manager and were only released by an explicit unpin_memory(), which existing callers relying on torch.pin_memory()'s GC-based lifetime never call -- so repeatedly creating and dropping pinned buffers leaked mlocked host memory until process exit. Attach a weakref.finalize to the returned tensor that frees the allocation when it is garbage-collected, and cancel that finalizer on explicit unpin() to avoid a redundant free. Addresses Codex review r3721965881. Signed-off-by: Olatunji Ruwase <tunji.ruwase@snowflake.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Release optimizer-owned page-locked host buffers when engines tear down under DS_PIN_MEMORY_BACKEND=native, so mlocked memory does not wait on GC. Signed-off-by: Olatunji Ruwase <tunji.ruwase@snowflake.com> Co-authored-by: Cursor <cursoragent@cursor.com>
|
Addressed the native pin lifetime concern with two complementary paths:
Coverage:
|
Document DS_PIN_MEMORY_BACKEND (torch vs native), the async-io build requirement, and native unpin/lifetime semantics for the new pinning backend. Signed-off-by: Olatunji Ruwase <tunji.ruwase@snowflake.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Make Host Memory Pinning a peer of Memory Requirements, with Backend Selection, Requirements for native, and Lifetime and unpinning nested under it. Signed-off-by: Olatunji Ruwase <tunji.ruwase@snowflake.com> Co-authored-by: Cursor <cursoragent@cursor.com>
|
@delock , @stas00, @tohtana see this page for docs preview of this feature: |
Colocate NativePinnedMemory unit tests with the destroy-path pin_memory suite under the v1 tree. Signed-off-by: Olatunji Ruwase <tunji.ruwase@snowflake.com> Co-authored-by: Cursor <cursoragent@cursor.com>
…-memory Signed-off-by: Olatunji Ruwase <tunji.ruwase@snowflake.com> Co-authored-by: Cursor <cursoragent@cursor.com> # Conflicts: # accelerator/abstract_accelerator.py # accelerator/cpu_accelerator.py # accelerator/cuda_accelerator.py # accelerator/hpu_accelerator.py # accelerator/mlu_accelerator.py # accelerator/mps_accelerator.py # accelerator/npu_accelerator.py # accelerator/sdaa_accelerator.py # accelerator/supa_accelerator.py # accelerator/xpu_accelerator.py # docs/code-docs/source/memory.rst
|
@delock Accounting from #8207 is now integrated into this PR (merged latest
Pinned-memory volume notes in the RTD Memory Usage page also include the #8207 updates, alongside the Host Memory Pinning section for backend selection. |
Add a list-table under Host Memory Pinning so users can compare allocator, requirements, unpin/lifetime, DeepNVMe recognition, and CPU behavior. Signed-off-by: Olatunji Ruwase <tunji.ruwase@snowflake.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Torch-pinned host buffers already skip DeepNVMe bounce buffers via tensor.is_pinned(); the docs incorrectly suggested otherwise. Signed-off-by: Olatunji Ruwase <tunji.ruwase@snowflake.com> Co-authored-by: Cursor <cursoragent@cursor.com>
handle.is_pinned() checks tensor.is_pinned(), so torch pins are recognized by DeepNVMe I/O handles the same way native-managed pins are. Signed-off-by: Olatunji Ruwase <tunji.ruwase@snowflake.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Move AIO/GDS visibility, bounce-buffer skipping, and host accounting into a shared preamble so the table only lists real differences. Signed-off-by: Olatunji Ruwase <tunji.ruwase@snowflake.com> Co-authored-by: Cursor <cursoragent@cursor.com>
Summary
Adds a native host-memory pinning backend, selectable via the
DS_PIN_MEMORY_BACKENDenvironment variable (defaults totorch). When set tonative, CPU memory is page-locked through the DeepNVMe async-io (mlock) allocator instead oftorch.pin_memory().Depends on / builds atop #8212 (shared DeepNVMe pinned-tensor manager). Native allocations go through
new_cpu_locked_tensor, so I/O handles recognize them via the process-wide manager and skip bounce buffers.deepspeed/utils/pin_memory.py: a process-wide sharedNativePinnedMemorymanager that pins CPU memory, tracks pinned pointer ranges (so slices/views report as pinned), tags buffers with.ds_pinned, supportsmake_copy/match_shape, and frees on unpin. It fails early with a clear error if the async-io op cannot be built (no silent torch fallback). Native pins also use aweakreffinalizer so GC releases mlocked pages when tensors are dropped without an explicit unpin.pin_memorydropsalign_bytesand gainsmake_copy/match_shape;is_pinnedis FakeTensor/meta-tensor safe; newunpin_memory(native frees, torch no-op). Subclasses retain only the device-specific_torch_pin_memory/_torch_is_pinnedprimitives.compilepaths route throughget_accelerator(). Swap-tensor buffers continue to allocate via I/O handles (from Share DeepNVMe pinned-tensor manager and route swap buffers through I/O handles #8212); with the shared manager they interoperate with native-pinned tensors. ZeRO / ZenFlowdestroy()explicitly unpins optimizer-owned CPU-offload buffers under the native backend.docs/code-docs/source/memory.rst); also pushed tortd-staging.tests/unit/v1.Test plan
pre-commit(yapf, flake8, check-license, check-torchdist, codespell) passes on all changed files.masterafter Share DeepNVMe pinned-tensor manager and route swap buffers through I/O handles #8212 merge; shared-manager overlap resolved by keeping Share DeepNVMe pinned-tensor manager and route swap buffers through I/O handles #8212's swap/I/O paths.tests/unit/v1/pin_memory/test_pin_memory.py+tests/unit/v1/accelerator/test_accelerator.py+tests/unit/v1/nvme/test_pinned_manager.py(incl. GC finalizer coverage) — 21 passed.tests/unit/v1/pin_memory/test_destroy_unpin.py— 5/5 passed (native ZeRO-2/3 free all optimizer-owned ranges; native ZeRO-3+param-offload decreases ranges; torch backend destroy is a clean no-op).tests/unit/v1/suite on a GPU node — 1134 passed, 63 skipped, exit 0 (~2.5h).DS_PIN_MEMORY_BACKEND=native, a buffer fromget_accelerator().pin_memory(and a narrow of it) isis_pinnedon a different AIO handle than the allocator.tunji-cpu-ds-0(DS_ACCELERATOR=cpu): torch backend 11 passed; native backend 11 passed (test_pin_memory.py, native accelerator APIs,test_pinned_manager.py). XPU/HPU still pending reviewer hardware.Made with Cursor