Skip to content

feat(runtime): L3 DeviceTensor args, reusable prepare() handle, and shared DeviceMemoryHandle#1451

Merged
lyfne123 merged 6 commits into
hw-native-sys:mainfrom
luohuan19:child_mem_l3
May 25, 2026
Merged

feat(runtime): L3 DeviceTensor args, reusable prepare() handle, and shared DeviceMemoryHandle#1451
lyfne123 merged 6 commits into
hw-native-sys:mainfrom
luohuan19:child_mem_l3

Conversation

@luohuan19

@luohuan19 luohuan19 commented May 21, 2026

Copy link
Copy Markdown
Contributor

Summary

Builds out the L3 distributed runtime so worker-resident device buffers can be reused across dispatches, mirroring the existing L2 CompiledProgram conventions. Three stacked changes:

  • Accept DeviceTensor args in L3+ programsDistributedCompiledProgram.__call__ now accepts worker-resident DeviceTensor arguments in place of host torch.Tensor. Such args skip the H2D/D2H copies and stay caller-managed, matching L2. Distributed codegen emits a pypto-owned make_tensor_arg and widens orchestration conversion to wrap DeviceTensor handles as ContinuousTensor(child_memory=True). The DeviceTensor -> ContinuousTensor conversion is extracted into a shared device_tensor_to_continuous helper reused by both L2 and L3 paths.

  • Reusable prepare() handleDistributedCompiledProgram.prepare() returns a reusable DistributedRuntime handle that runs the expensive L3 setup (compile_and_assemble, module load, Worker construction + registration + init/fork) once and dispatches many times. execute_distributed's setup is refactored into shared free helpers so the one-shot and reuse paths run identical setup without duplication. The handle exposes device-memory helpers (malloc/copy_to/copy_from/free/alloc_tensor) for buffers that survive across dispatches.

  • sub_worker_overrides + shared DeviceMemoryHandle — extract the duplicated alloc_tensor/free_tensor device-tensor surface from Worker (L2) and DistributedRuntime (L3) into a shared DeviceMemoryHandle ABC. The four memory primitives stay abstract; only the genuine per-level differences are hooks (_require_ready, _prepare_init). prepare(sub_worker_overrides=...) replaces a generated sub-worker placeholder (matched by name) with a caller-supplied callable; unknown names fail fast.

Testing

  • All tests pass
  • Code review completed
  • Documentation updated (getting_started en + zh-cn document prepare())

New tests: test_distributed_runtime.py, test_device_memory.py, test_tensor_arg.py, test_distributed_compiled_program.py, test_l3_device_tensor.py; rewritten L3 ST to exercise prepare().

@coderabbitai

coderabbitai Bot commented May 21, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR adds a DeviceMemoryHandle abstraction and DeviceTensor-aware marshalling, updates DistributedCompiledProgram with prepare() to return a reusable DistributedRuntime, refactors the distributed runner for one-shot and prepared execution, updates codegen and docs, and adds unit/integration tests for the new flows.

Changes

L3 Distributed Runtime Reuse with Worker-Resident DeviceTensors

Layer / File(s) Summary
Device Memory Handle Foundation
python/pypto/runtime/device_memory.py, python/pypto/runtime/device_tensor.py, python/pypto/runtime/__init__.py
New DeviceMemoryHandle ABC standardizes device memory primitives (malloc, free, copy_to, copy_from) and overridable hooks (_require_ready, _prepare_init). Companion helpers default_init_prep and alloc_device_tensor handle allocation with optional host-to-device upload and rollback on failure. DeviceMemoryHandle exposes alloc_tensor/free_tensor convenience methods and is exported from pypto.runtime.
Worker Integration with DeviceMemoryHandle
python/pypto/runtime/worker.py
Worker now inherits from DeviceMemoryHandle, adds _require_ready() hook, and delegates device-tensor convenience APIs to the base class.
DistributedCompiledProgram API
python/pypto/ir/distributed_compiled_program.py
__call__ now accepts DeviceTensor arguments alongside host tensors, validates device tensors via _validate_device_tensor, expands return type to include DeviceTensor, and adds prepare(config, sub_worker_overrides) method to create reusable DistributedRuntime.
Runtime Tensor Marshalling Helpers
python/pypto/runtime/task_interface.py, python/pypto/runtime/tensor_arg.py, python/pypto/runtime/runner.py
New device_tensor_to_continuous helper wraps DeviceTensor as ContinuousTensor with child_memory=True to avoid H2D/D2H transfers. New tensor_arg.py module provides make_tensor_arg wrapper that dispatches on argument type (delegating non-device args to existing implementation). execute_compiled now uses the new helpers for clearer error attribution.
Distributed Runner Refactoring
python/pypto/runtime/distributed_runner.py
Major refactor extracting shared setup helpers (_assemble_chip_callables, _load_orch_entry, _load_sub_worker_fns, setup/dispatch plumbing). Both execute_distributed (one-shot) and DistributedRuntime (reusable via prepare) now share setup logic, support DeviceTensor passthrough, enforce pre-fork shared-memory constraints for host tensors and alloc_tensor(init=...), validate/merge sub_worker_overrides, and tighten per-call validation and lifecycle handling.
Code Generation Updates
src/codegen/distributed/distributed_codegen.cpp
Generated distributed Python now imports make_tensor_arg from pypto.runtime.tensor_arg instead of simpler_setup.torch_interop.
User Documentation
docs/en/user/00-getting_started.md, docs/zh-cn/user/00-getting_started.md
New "Distributed (L3+) programs" sections describe DistributedCompiledProgram.prepare(), DeviceTensor argument handling to avoid H2D/D2H copies, shared-memory IO buffer requirements, and example usage with DistributedRuntime.
Integration Tests & Codegen Validation
tests/st/distributed/test_l3_device_tensor.py, tests/st/distributed/test_l3_distributed.py, tests/ut/codegen/test_distributed_codegen.py
End-to-end tests covering resident weight reuse across multiple dispatches, sub-worker override execution with shared-memory markers, unknown override name rejection, and codegen import assertions.
Unit Tests: Device Memory & Worker
tests/ut/runtime/test_device_memory.py
Tests for DeviceMemoryHandle lifecycle (alloc with/without init, upload byte counts, rollback on failure, prepare_init hooks, worker_id validation, readiness gating) and Worker/DistributedRuntime subclass relationships.
Unit Tests: Distributed Runtime & Marshalling
tests/ut/runtime/test_distributed_runtime.py, tests/ut/runtime/test_tensor_arg.py
Tests for setup-once/dispatch-many semantics, per-call tensor validation (DeviceTensor/shared-memory host tensors accepted, non-shared rejected), alloc_tensor orchestration calls and rollback, lifecycle/context-manager behavior, sub-worker override merging and wiring, and tensor-arg marshalling conversion flows.
Unit Tests: DistributedCompiledProgram
tests/ut/ir/test_distributed_compiled_program.py
Tests for __call__ argument acceptance (DeviceTensor forwarded unchanged), rejection of non-tensor arguments with guidance, and DeviceTensor shape validation.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

Possibly related PRs

  • hw-native-sys/pypto#1193: Upstream L3 distributed pipeline foundation that this PR extends with resident DeviceTensor and DistributedRuntime.prepare() support
  • hw-native-sys/pypto#1275: Parallel work implementing worker-resident DeviceTensor support and ContinuousTensor(child_memory=True) conversion to skip H2D/D2H transfers
  • hw-native-sys/pypto#990: Related changes around DeviceTensor/ContinuousTensor conversion utilities and dtype mapping used by this PR

Suggested reviewers

  • lyfne123
  • Hzfengsy

Poem

🐰 I hopped through code and buffered dreams,
Threads of shared memory, gentle streams,
Prepare once, dispatch and play,
No frantic copies in the way—
Reuse the runtime, loop with beams ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 43.75% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: accepting DeviceTensor args, introducing reusable prepare() handle, and extracting shared DeviceMemoryHandle base class.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The PR description clearly explains the three major feature additions (DeviceTensor args, reusable prepare() handle, and shared DeviceMemoryHandle) and directly relates to the changeset shown in the raw summary.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements Distributed (L3+) program reuse via DistributedCompiledProgram.prepare() and adds support for worker-resident DeviceTensor arguments. It introduces a DeviceMemoryHandle base class to unify memory management across runtime levels and refactors the distributed runner for better modularity. Documentation and tests are updated to reflect these changes. Reviewer feedback correctly identified opportunities to improve temporary file handling, reduce coupling with internal APIs, and optimize validation logic.

Comment thread python/pypto/runtime/distributed_runner.py
Comment thread python/pypto/runtime/distributed_runner.py Outdated
Comment thread python/pypto/runtime/distributed_runner.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/en/user/00-getting_started.md`:
- Around line 257-261: The snippet is missing imports causing NameError; add the
required imports at the top so the example is runnable: import torch and from
pypto.runtime import DeviceTensor, then keep the existing lines using
ir.compile(MyDistributedProgram), DeviceTensor(dev_ptr, (1024, 4096),
torch.float16), and compiled(x, weight, out) unchanged so DeviceTensor and
torch.float16 are defined when the snippet executes.

In `@docs/zh-cn/user/00-getting_started.md`:
- Around line 251-255: The snippet uses DeviceTensor and torch.float16 but lacks
imports, causing NameError when copied; update the example so it is runnable by
adding the missing imports (import torch and from pypto.runtime import
DeviceTensor) above the shown lines where ir.compile(MyDistributedProgram),
DeviceTensor(dev_ptr, (1024, 4096), torch.float16) and compiled(...) are used,
so DeviceTensor, torch and related symbols are defined when the snippet is
copied.

In `@python/pypto/runtime/device_tensor.py`:
- Around line 126-128: The alloc_device_tensor function currently coerces
dimensions with int(d) and only rejects negatives, which lets bools, floats,
zeros, and empty shapes slip through; update its validation to mirror
DeviceTensor's contract: do not coerce values–require shape to be a non-empty
iterable, convert to tuple shape_t but validate each dim is an int
(isinstance(d, int) and not isinstance(d, bool)), and ensure each dim is > 0
(reject zeros and negatives); raise TypeError for non-int/bool dims and
ValueError for empty shapes or non-positive dims, and keep the rest of the
allocation logic using shape_t.

In `@python/pypto/runtime/distributed_runner.py`:
- Around line 471-484: Move the setup calls that can fail into the try/finally
so cleanup always runs: keep creating the worker via _construct_worker(...)
where it is, but call _register_callables(...) and w.init() inside the try block
(before calling _dispatch(...)) so that any exception during registration or
init will trigger the finally that calls w.close() and unlinks rootinfo_path;
ensure the order remains _register_callables -> w.init() -> _dispatch and that
the finally still removes rootinfo_path and handles FileNotFoundError.
- Around line 540-568: Make DistributedRuntime.__init__ exception-safe by
wrapping the post-registration setup (after _register_callables returns and
before the constructor returns) in a try/except/finally so partial resources are
cleaned up on error: if any of self._w.init() or self._w._start_hierarchical()
(or subsequent steps) raise, call the worker cleanup method (e.g.
self._w.shutdown() / close / stop whichever exists on the worker) and remove or
close any rootinfo artifacts referenced by self._rootinfo_path, then re-raise
the exception; use the unique symbols _register_callables, self._w,
self._rootinfo_path, self._w.init(), and self._w._start_hierarchical() to locate
where to add the try/finally and cleanup calls.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 908ca164-d556-4028-bd26-512ac769023f

📥 Commits

Reviewing files that changed from the base of the PR and between 55ff78e and 61a0b48.

📒 Files selected for processing (19)
  • docs/en/user/00-getting_started.md
  • docs/zh-cn/user/00-getting_started.md
  • python/pypto/ir/distributed_compiled_program.py
  • python/pypto/runtime/__init__.py
  • python/pypto/runtime/device_memory.py
  • python/pypto/runtime/device_tensor.py
  • python/pypto/runtime/distributed_runner.py
  • python/pypto/runtime/runner.py
  • python/pypto/runtime/task_interface.py
  • python/pypto/runtime/tensor_arg.py
  • python/pypto/runtime/worker.py
  • src/codegen/distributed/distributed_codegen.cpp
  • tests/st/distributed/test_l3_device_tensor.py
  • tests/st/distributed/test_l3_distributed.py
  • tests/ut/codegen/test_distributed_codegen.py
  • tests/ut/ir/test_distributed_compiled_program.py
  • tests/ut/runtime/test_device_memory.py
  • tests/ut/runtime/test_distributed_runtime.py
  • tests/ut/runtime/test_tensor_arg.py

Comment thread docs/en/user/00-getting_started.md
Comment thread docs/zh-cn/user/00-getting_started.md
Comment thread python/pypto/runtime/device_tensor.py Outdated
Comment thread python/pypto/runtime/distributed_runner.py Outdated
Comment thread python/pypto/runtime/distributed_runner.py Outdated
luohuan19 added a commit to luohuan19/pypto that referenced this pull request May 21, 2026
- ruff format distributed_runner.py and test_l3_distributed.py
- suppress reportAttributeAccessIssue on simpler re-exports
  (ContinuousTensor/make_tensor_arg/torch_dtype_to_datatype) imported
  via .task_interface, matching the existing runner.py pattern
- use callable MagicMock sentinels for sub_worker_overrides in tests
  so they satisfy the dict[str, Callable] signature
luohuan19 added a commit to luohuan19/pypto that referenced this pull request May 21, 2026
- tensor_arg: keep the lazy ``make_tensor_arg`` import as a parenthesized
  block so the ``# noqa: PLC0415`` stays on the ``from ... import (`` line,
  surviving ruff isort's re-wrap (fixes the pre-commit ruff failure).
- device_tensor: validate ``alloc_device_tensor`` shape up front and without
  coercion, mirroring DeviceTensor's contract (reject bool/non-int, empty, and
  non-positive dims) so a wrong logical shape never reaches malloc.
- distributed_runner: make the one-shot dispatch path and
  DistributedRuntime.__init__ exception-safe — construct/register/init now run
  under cleanup so a setup failure still closes the worker and unlinks the
  comm rootinfo temp file.
- docs: add the missing ``import torch`` / ``DeviceTensor`` imports to the
  distributed DeviceTensor snippet (en + zh) so it runs as-is.
luohuan19 added a commit to luohuan19/pypto that referenced this pull request May 21, 2026
…ve-sys#1451

alloc_device_tensor now enforces DeviceTensor's positive-int shape contract,
so the pre-malloc rejection message changed from "non-negative" to "positive".
Update the test accordingly and add coverage for the newly rejected zero-dim
and empty-shape cases (both must fail before malloc).
luohuan19 added 6 commits May 25, 2026 09:12
…/D2H)

DistributedCompiledProgram.__call__ now accepts worker-resident DeviceTensor
arguments in place of host torch.Tensor, mirroring the L2 CompiledProgram
convention: such args skip the H2D/D2H copies and stay caller-managed.

Distributed codegen emits a pypto-owned make_tensor_arg (pypto.runtime.tensor_arg)
instead of simpler_setup.torch_interop, widening the orchestration conversion to
wrap DeviceTensor handles as ContinuousTensor(child_memory=True). The
DeviceTensor->ContinuousTensor conversion is extracted into a shared
device_tensor_to_continuous helper reused by both the L2 and L3 paths.
Add DistributedCompiledProgram.prepare() returning a reusable
DistributedRuntime handle that runs the expensive L3 setup
(compile_and_assemble, module load, Worker construction + registration
+ init/fork) once and dispatches many times on the held Worker.

Refactor execute_distributed's setup into shared free helpers
(_assemble_chip_callables, _load_orch_entry, _load_sub_worker_fns,
_build_chip_bootstrap, _construct_worker, _register_callables,
_make_call_config, _dispatch) so the one-shot and reuse paths run
identical setup without duplication.

The handle exposes device-memory helpers (malloc/copy_to/copy_from/free/
alloc_tensor) for worker-resident DeviceTensor buffers that survive across
dispatches; per-call IO uses shared-memory host tensors reused in place.
Extract the shared alloc_device_tensor helper into device_tensor.py
(reused by L2 Worker.alloc_tensor).

Add UT covering the setup-once/dispatch-many contract and per-call
validation, rewrite the L3 ST to exercise prepare(), and document
prepare() in getting_started (en + zh-cn).
Extract the duplicated alloc_tensor/free_tensor device-tensor surface from
Worker (L2) and DistributedRuntime (L3) into a shared DeviceMemoryHandle ABC.
The four memory primitives stay abstract; only the genuine per-level
differences are hooks: _require_ready (readiness guard) and _prepare_init
(host-init upload policy). L2 keeps the defensive contiguous CPU copy; L3
requires shared memory because its upload runs in a forked child.

Add DistributedCompiledProgram.prepare(sub_worker_overrides=...) to replace a
generated sub-worker placeholder (matched by name) with a caller-supplied
callable. Unknown names fail fast via _merge_sub_worker_overrides.
- ruff format distributed_runner.py and test_l3_distributed.py
- suppress reportAttributeAccessIssue on simpler re-exports
  (ContinuousTensor/make_tensor_arg/torch_dtype_to_datatype) imported
  via .task_interface, matching the existing runner.py pattern
- use callable MagicMock sentinels for sub_worker_overrides in tests
  so they satisfy the dict[str, Callable] signature
- tensor_arg: keep the lazy ``make_tensor_arg`` import as a parenthesized
  block so the ``# noqa: PLC0415`` stays on the ``from ... import (`` line,
  surviving ruff isort's re-wrap (fixes the pre-commit ruff failure).
- device_tensor: validate ``alloc_device_tensor`` shape up front and without
  coercion, mirroring DeviceTensor's contract (reject bool/non-int, empty, and
  non-positive dims) so a wrong logical shape never reaches malloc.
- distributed_runner: make the one-shot dispatch path and
  DistributedRuntime.__init__ exception-safe — construct/register/init now run
  under cleanup so a setup failure still closes the worker and unlinks the
  comm rootinfo temp file.
- docs: add the missing ``import torch`` / ``DeviceTensor`` imports to the
  distributed DeviceTensor snippet (en + zh) so it runs as-is.
…ve-sys#1451

alloc_device_tensor now enforces DeviceTensor's positive-int shape contract,
so the pre-malloc rejection message changed from "non-negative" to "positive".
Update the test accordingly and add coverage for the newly rejected zero-dim
and empty-shape cases (both must fail before malloc).
@lyfne123 lyfne123 merged commit 6597053 into hw-native-sys:main May 25, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

2 participants