Skip to content

Ec connector nixl transfer#9

Open
omerpaz95 wants to merge 22 commits into
mainfrom
ec_connector_nixl_transfer
Open

Ec connector nixl transfer#9
omerpaz95 wants to merge 22 commits into
mainfrom
ec_connector_nixl_transfer

Conversation

@omerpaz95

@omerpaz95 omerpaz95 commented May 5, 2026

Copy link
Copy Markdown
Owner

Summary

Adds a remote-transport mechanism on top of the existing ECCPUConnector: a
consumer (PD) scheduler can fetch an encoder output by mm_hash from a remote
producer (E) scheduler over ZMQ + NIXL, landing the bytes directly in the
consumer's CPU encoder-cache mmap. Scheduler-to-scheduler control plane,
scheduler-side NIXL data plane; worker processes are untouched.

  • Scheduler↔scheduler control plane: DEALER (consumer) ↔ ROUTER (producer)
    over ZMQ; one listener thread on the producer; consumer drains acks on demand.
  • Data plane: NIXL CPU→CPU WRITE from a producer dict[mm_hash, Tensor]
    into the consumer's pre-registered ECSharedRegion mmap.
  • Consumer state: _encoding_map (mm_hash → block indices, populated at alloc
    time) + _ready (hashes whose xfer completed).
  • Producer state: _encodings (populated by a future save_caches PR; this
    PR's tests fixture-inject) + _waiting (parked requests).
  • ECSharedRegion gains alloc(n) / free(indices) so the free-list lives
    with the memory it tracks.
  • Wire schemas + compatibility hash in a new messages.py module.

See docs/superpowers/specs/2026-05-04-ec-nixl-transfer-design.md for the
full design and docs/superpowers/plans/2026-05-04-ec-nixl-transfer.md for
the TDD plan.

Out of scope (future PRs)

  • Producer worker-side save_caches (GPU→CPU staging into _encodings).
  • Plumbing encoder_node_address onto kv_transfer_params — this PR uses
    ec_connector_extra_config.default_encoder_node as an MVP fallback.
  • Block eviction / free-on-request-finished hook.

Test plan

  • tests/v1/ec_connector/unit/test_ec_shared_region_alloc.py — 5/5
  • tests/v1/ec_connector/unit/test_ec_messages.py — 4/4
  • tests/v1/ec_connector/unit/test_ec_consumer_scheduler.py — state machine
  • tests/v1/ec_connector/unit/test_ec_producer_scheduler.py — listener + NIXL xfer + drain
  • tests/v1/ec_connector/unit/test_ec_cpu_connector.py — existing worker tests, no regression
  • tests/v1/ec_connector/integration/test_ec_zmq_loopback.py — end-to-end over real ZMQ, asserts bytes land correctly in consumer mmap
  • Full tests/v1/ec_connector/: 59/59
  • pre-commit run clean on every commit

Real NIXL runtime not exercised here (no NIXL install in dev env);
FakeNixlWrapper mirrors the NixlWrapper method surface and synchronously
ctypes.memmoves bytes between registered regions on WRITE.

Signed-off-by: omerpaz95 <omerpaz95@gmail.com>
Signed-off-by: omerpaz95 <omerpaz95@gmail.com>
Signed-off-by: omerpaz95 <omerpaz95@gmail.com>
…nd pass to EC CPU Connector

Signed-off-by: omerpaz95 <omerpaz95@gmail.com>
…from config for cleaner implementation

Signed-off-by: omerpaz95 <omerpaz95@gmail.com>
Signed-off-by: omerpaz95 <omerpaz95@gmail.com>
The block free-list belongs with the memory it tracks.  Scheduler-only
usage (workers never call alloc/free) so no locking is needed.
alloc() returns block indices (not tensor views) because that's what
downstream consumers — ZMQ payloads, connector metadata, NIXL descriptor
offset math — actually need.

Signed-off-by: omerpaz95 <omerpaz95@gmail.com>
Producer ROUTER socket binds to these; consumer DEALERs connect to the
host:port advertised by each E node.  Default 5601 (one above NIXL)
so both connectors can coexist on one host in tests.

Signed-off-by: omerpaz95 <omerpaz95@gmail.com>
Enables unconditional ``connector.shutdown()`` from teardown paths
without subclass checks.  Subclasses override to release resources.

Signed-off-by: omerpaz95 <omerpaz95@gmail.com>
XferReq/XferAck are the sole messages on the scheduler↔scheduler ZMQ
side channel.  Compatibility hash guards against silently talking to a
peer built from a drifted codebase.

Signed-off-by: omerpaz95 <omerpaz95@gmail.com>
Implements the NixlWrapper subset the connector uses (register_memory,
add_remote_agent, prep_xfer_dlist, make_prepped_xfer, transfer,
check_xfer_state, ...).  WRITE xfers synchronously memmove bytes
between registered regions via ctypes, so tests can assert real data
arrival without a real NIXL runtime.

Signed-off-by: omerpaz95 <omerpaz95@gmail.com>
Opens the same mmap workers use, creates a NIXL agent, registers the
region, and builds an xfer_descs handle + serialized peer descriptor
bytes for inclusion in XferReq messages.  State dicts (_encoding_map,
_ready, _dealers) are initialized empty.  Producer scaffolding is
added but the listener thread arrives in the next task.

Signed-off-by: omerpaz95 <omerpaz95@gmail.com>
Listener decodes XferReq messages, validates the compatibility hash,
and either dispatches (when the encoding is locally available) or
parks in _waiting for the future save_caches path to drain.  NIXL
transfer itself is stubbed; implemented in the next task.

Signed-off-by: omerpaz95 <omerpaz95@gmail.com>
Per-transfer lifecycle: register source tensor, add remote agent, prep
local/remote dlists, make_prepped_xfer + transfer, poll for DONE,
release everything on return.  Consumer's memory descriptor arrives as
a msgpack-encoded list[tuple[int,int,int]] and is rebuilt on our side
via get_xfer_descs — no opaque NIXL object crosses the wire.

Signed-off-by: omerpaz95 <omerpaz95@gmail.com>
has_cache_item is the engine's readiness probe: True if the xfer
completed, None if still in flight, False if we've never heard of the
hash.  _drain_acks polls every DEALER non-blocking and routes each
ack to _complete.  Success adds to _ready; failure frees the blocks.

Signed-off-by: omerpaz95 <omerpaz95@gmail.com>
Allocates N blocks from the region, records the mapping in
_encoding_map, sends XferReq to the E node.  Encoder node address is
resolved from kv_transfer_params (future PR) or falls back to
ec_connector_extra_config.default_encoder_node.  DEALER sockets are
cached per (host,port) so we don't churn connections.

Signed-off-by: omerpaz95 <omerpaz95@gmail.com>
Drains any pending acks, then pops every ready mm_hash from
_encoding_map into the ECCPUConnectorMetadata the worker receives.
Worker's start_load_caches CPU→GPU-copies from the block indices.
Hand-off-and-forget: clears _ready each step.  Updates existing
worker-path tests to match the new _ready-gated semantics.

Signed-off-by: omerpaz95 <omerpaz95@gmail.com>
Future save_caches populates _encodings[mm_hash] then calls
_drain_waiting(mm_hash) so any XferReqs that arrived early get served.
Tested via a direct call until save_caches lands in a follow-up PR.

Signed-off-by: omerpaz95 <omerpaz95@gmail.com>
Two connectors (producer + consumer) wired over real ZMQ sockets with
FakeNixlWrapper standing in for NIXL's data plane.  Asserts the
consumer's mmap actually receives the producer's tensor bytes, and
that the wait-then-drain path works for requests that arrive before
the encoding is ready.

Signed-off-by: omerpaz95 <omerpaz95@gmail.com>
Spec covers the scheduler-side NIXL transport, scheduler↔scheduler ZMQ
control plane, transient NIXL agent metadata per transfer, and the
single-dict + ready-set state model on the consumer.

Plan decomposes the work into 13 TDD-structured tasks.

Signed-off-by: omerpaz95 <omerpaz95@gmail.com>
Signed-off-by: omerpaz95 <omerpaz95@gmail.com>
Signed-off-by: omerpaz95 <omerpaz95@gmail.com>
almogtavor pushed a commit that referenced this pull request Jun 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant