[EC Connector] CPU Offloading EC Connector#47423
Merged
orozery merged 16 commits intoJul 13, 2026
Merged
Conversation
Signed-off-by: omerpaz95 <omerpaz95@gmail.com>
Signed-off-by: omerpaz95 <omerpaz95@gmail.com>
Signed-off-by: omerpaz95 <omerpaz95@gmail.com>
Signed-off-by: omerpaz95 <omerpaz95@gmail.com>
Signed-off-by: omerpaz95 <omerpaz95@gmail.com>
Signed-off-by: omerpaz95 <omerpaz95@gmail.com>
orozery
reviewed
Jul 6, 2026
Comment on lines
+1
to
+10
| # SPDX-License-Identifier: Apache-2.0 | ||
| # SPDX-FileCopyrightText: Copyright contributors to the vLLM project | ||
| """CPU-cache NIXL-transport EC connector. | ||
|
|
||
| See `ec-nixl-transfer-v4.md` for the full design. | ||
| """ | ||
|
|
||
| # from vllm.distributed.ec_transfer.ec_connector.cpu.connector import ( | ||
| # ECCPUConnector, | ||
| # ) |
Signed-off-by: omerpaz95 <omerpaz95@gmail.com>
Signed-off-by: omerpaz95 <omerpaz95@gmail.com>
Signed-off-by: omerpaz95 <omerpaz95@gmail.com>
1. ECSharedRegion demoted to pure mmap lifecycle manager Previously it was a full block allocator with alloc/free, ref-counting (pin/unpin), and locking. Now it just handles open/mmap/CUDA-host-register/cleanup. All allocation logic moved into the new EmbeddingCache class, which is a proper named-entry cache with explicit states (not-ready -> ready -> pinned) and FIFO eviction. 2. New StepTracker for DMA completion safety The old scheduler assumed saves were immediately ready. The new design introduces StepTracker - it delays mark_ready and unpin operations by a configurable step count (max_concurrent_batches), ensuring GPU<->host DMA transfers actually complete before entries are served or evicted. 3. Batched DMA instead of per-block copies The worker previously iterated per-block with tensor.copy_() on a CUDA stream + synchronize. Now it fills pooled descriptor buffers (DescriptorBufferPool) and flushes everything in a single swap_blocks_batch call - no explicit CUDA stream management. 4. Save-rank filtering Only tp_rank==0 && pcp_rank==0 performs the GPU-to-host write, avoiding redundant bandwidth usage across ranks that hold identical encoder output. 5. Region sizing by byte budget Changed from a raw num_ec_blocks count to an ec_cpu_bytes byte budget divided by per-block size. 6. Removed ECRegionContext The intermediary dataclass bundling region + layout fields is gone; the region is returned directly from create_ec_shared_region(). 7. Add e2e test and CI integration Signed-off-by: Or Ozeri <oro@il.ibm.com>
Signed-off-by: Or Ozeri <oro@il.ibm.com>
Signed-off-by: omerpaz95 <omerpaz95@gmail.com>
orozery
approved these changes
Jul 13, 2026
NickLucche
pushed a commit
to NickLucche/vllm
that referenced
this pull request
Jul 15, 2026
Signed-off-by: omerpaz95 <omerpaz95@gmail.com> Signed-off-by: Or Ozeri <oro@il.ibm.com> Co-authored-by: Or Ozeri <oro@il.ibm.com>
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.
Add
ECCPUConnector— CPU encoder-cache offloading connectorPurpose
This PR adds
ECCPUConnector, a self-contained encoder-cache (EC) connector that offloads encoder outputs to a shared CPUmmapregion on/dev/shmso a singleec_role=bothvLLM instance can reuse them on a later request instead of re-running the vision encoder.The connector is intentionally minimal. Everything happens within one instance — encode once, save the encoder output to the CPU region, and on a subsequent cache hit reload
mmap→GPU rather than recomputing.This is a self-contained, minimal version of the bigger, p2p-enabled EC CPU NIXL Connector.
Logic
ECCPUScheduler) — owns themmapregion and theshared offload bookkeeping (
_local_encodings,_blocks), and routesscheduler-side calls to a producer and/or consumer sub-delegate. It exposes
has_cache_item,ensure_cache_available,update_state_after_alloc, andbuild_connector_meta.ECCPUProducer— the encode/offload pipeline.ensure_cache_availableperforms a speculative region-capacity check;update_state_after_allocrecords a pending save;build_savesallocates region blocks (FIFO eviction of unpinned entries on exhaustion) and promotes themm_hashinto the shared_local_encodingscache.ECCPUConsumer— the local-reload path.has_cache_itemreports a hit when anmm_hashis present in_local_encodings;update_state_after_allocpins the producer-allocated blocks;build_loadsre-serves them for anmmap→GPU copy and unpins.ECCPUWorker) — a separate OS process, pure torch/mmap.save_cachescopies GPU→mmap(synchronizing the save stream so the copy is CPU-complete before the block is served);start_load_cachescopiesmmap→GPU.End-to-End Architecture
Lifecycle (offload-reuse cycle)
mm_featuresarrives.has_cache_item(mm_hash)returnsFalse(first sight), so the encoder input is scheduled for compute.save_cachescopies the encoder output GPU→mmap(with a blocking stream sync, so the bytes are durable before the step's model execution returns).build_savesallocates the region blocks, recordsmm_hash → block_indicesin_blocks, and promotesmm_hashinto_local_encodings(the shared local cache) — so the item is reusable from the next step.mm_hash:has_cache_itemnow returnsTrue, so the scheduler routes it toexternal_load_encoder_inputinstead of recomputing.update_state_after_allocpins the cached blocks;build_loadsre-serves the same block indices as a load and unpins them.start_load_cachescopiesmmap→GPU. The encoder is not re-invoked.Configuration
Selected by name through the transfer config, with the
ec_bothrole:ECTransferConfigis unchanged — connector selection is purely by name. Allthree roles (
ec_producer,ec_consumer,ec_both) are accepted, but onlyec_bothdelivers reuse in the CPU-only connector (a producer-only instancesaves but never reloads; a consumer-only instance finds nothing locally). The
full role plumbing is kept so the NIXL subclasses inherit role handling
untouched.
Files
All new files live under
vllm/distributed/ec_transfer/ec_connector/:ec_shared_region.pyECSharedRegion—mmaplifecycle,alloc/free/pin/unpin,cudaHostRegistercpu/__init__.pycpu/common.pyECRegionContext,ECCPUConnectorMetadata,setup_ec_regioncpu/worker.pyECCPUWorker— GPU↔mmapcopiescpu/connector.pyECCPUConnector— role router +_make_*seamscpu/scheduler/__init__.pyECCPUScheduler— scheduler delegate +_build_*seamscpu/scheduler/common.pyevict_and_alloc— FIFO eviction helpercpu/scheduler/producer.pyECCPUProducer— encode/offload pipelinecpu/scheduler/consumer.pyECCPUConsumer— localmmapreloadPlus one line in
factory.pyregisteringECCPUConnector.Test Plan
Unit tests live under
tests/v1/ec_connector/unit/and run in full with:Coverage:
test_ec_shared_region.py— regionalloc/free/try_free/pin/unpinrefcount semantics, FIFO eviction skipping pinned blocks, exhaustion raising
AllocationError.test_metadata.py—ECCPUConnectorMetadatadefaults and independence ofsaves/loads.test_producer.py—build_savesallocate-and-promote,_fifo_alloceviction (oldest-unpinned / skip-pinned),ensure_cache_availabledeferral on a full region.test_consumer.py—has_cache_item,update_state_after_allocpin,build_loadsre-serve + unpin.test_worker.py— GPU↔mmapround-trip reproduces the source tensor bit-for-bit (CUDA-gated tests skip on hosts without a GPU).test_scheduler_cpu.py— delegate wiring and the full offload-reuse cycle (save → promote → hit → reload of the same blocks).test_connector_cpu.py— role dispatch (scheduler vs worker) and factory registration.test_no_nixl_imports.py— AST guard asserting nonixl/zmq/msgspecimports (and no NIXL-only submodules) are reachable from thecpu/package.