vllm-project/vllm
Executors and workers
Active contributors: Nick Hill, Cyrus Leung, youkaichao, Robert Shaw.
Purpose
EngineCore does not run forward passes itself. It hands a SchedulerOutput to an Executor, which fans out to one or more Worker processes. Each worker holds part of the model and is driven by a ModelRunner. This separation is what lets vLLM target tensor/pipeline/data parallelism, Ray-managed clusters, and external launchers from a single engine API.
Directory layout
vllm/v1/executor/
├── abstract.py # Executor base class (gets_class dispatch)
├── multiproc_executor.py # MultiprocExecutor (1,235 lines) — the default
├── ray_executor.py # RayDistributedExecutor (legacy)
├── ray_executor_v2.py # RayExecutorV2 (default Ray backend going forward)
├── ray_utils.py # Ray initialization, env propagation
├── ray_env_utils.py
└── uniproc_executor.py # UniProcExecutor + ExecutorWithExternalLauncher
vllm/v1/worker/
├── worker_base.py # WorkerBase (sleep/wake, KV init, RPC dispatch)
├── gpu_worker.py # GPU worker (1,400 lines)
├── gpu_model_runner.py # The forward orchestrator (~7,070 lines)
├── gpu_input_batch.py # InputBatch builder for GPUModelRunner
├── gpu_ubatch_wrapper.py # Sub-batch grouping for piecewise CUDA graphs
├── cpu_worker.py / cpu_model_runner.py
├── xpu_worker.py / xpu_model_runner.py
├── tpu_input_batch.py # TPU model runner inputs
├── encoder_cudagraph.py # Encoder-side CUDA graph manager
├── lora_model_runner_mixin.py
├── kv_connector_model_runner_mixin.py
├── ec_connector_model_runner_mixin.py
├── ubatching.py / ubatch_utils.py
├── workspace.py # Shared scratch tensors
├── block_table.py # Per-batch block table tensor builder
├── mamba_utils.py
├── cp_utils.py # Context-parallel utilities
├── dp_utils.py # Data-parallel utilities
└── utils.pyKey abstractions
| Abstraction | File | Role |
|---|---|---|
Executor |
vllm/v1/executor/abstract.py |
Abstract base; chooses concrete class via Executor.get_class |
MultiprocExecutor |
vllm/v1/executor/multiproc_executor.py |
mp backend — spawns Python subprocesses, talks via pipes |
RayDistributedExecutor |
vllm/v1/executor/ray_executor.py |
ray backend (legacy) |
RayExecutorV2 |
vllm/v1/executor/ray_executor_v2.py |
ray backend (when VLLM_USE_RAY_V2_EXECUTOR_BACKEND=1) |
UniProcExecutor |
vllm/v1/executor/uniproc_executor.py |
uni backend — single process, no IPC |
ExecutorWithExternalLauncher |
vllm/v1/executor/uniproc_executor.py |
external_launcher — workers launched by torchrun, etc. |
WorkerBase |
vllm/v1/worker/worker_base.py |
Common worker lifecycle (init, sleep, wake, compile, KV init) |
GPUWorker |
vllm/v1/worker/gpu_worker.py |
GPU specialization |
GPUModelRunner |
vllm/v1/worker/gpu_model_runner.py |
Per-step forward orchestrator |
InputBatch |
vllm/v1/worker/gpu_input_batch.py |
Builds the actual tensors fed into the model |
KVOutputAggregator |
vllm/distributed/kv_transfer/kv_connector/utils.py |
Aggregates per-rank KV connector outputs |
Executor selection
# vllm/v1/executor/abstract.py
@staticmethod
def get_class(vllm_config):
backend = vllm_config.parallel_config.distributed_executor_backend
if backend == "ray":
return RayExecutorV2 if envs.VLLM_USE_RAY_V2_EXECUTOR_BACKEND else RayDistributedExecutor
if backend == "mp": return MultiprocExecutor
if backend == "uni": return UniProcExecutor
if backend == "external_launcher": return ExecutorWithExternalLauncher
if isinstance(backend, str): return resolve_obj_by_qualname(backend)
if isinstance(backend, type) and issubclass(backend, Executor): return backendThe default is mp for single-host TP, ray for multi-host, uni when world_size == 1, and external_launcher when the user owns process spawning.
Forward pass
sequenceDiagram
participant Sch as Scheduler
participant E as Executor
participant W as WorkerBase (per rank)
participant MR as GPUModelRunner
participant M as Model
participant Smp as Sampler
Sch->>E: execute_model(SchedulerOutput)
E->>W: collective_rpc("execute_model", ...)
W->>MR: execute_model(scheduler_output)
MR->>MR: build InputBatch (block tables, slot mapping, MM)
MR->>MR: enter forward_context (LoRA, MM, KV connector meta)
MR->>M: model.forward(input_ids, kv_caches, attn_metadata, ...)
M-->>MR: hidden states / logits
MR->>Smp: sample(logits, sampling_metadata)
Smp-->>MR: sampled token ids, logprobs
MR-->>W: ModelRunnerOutput
W-->>E: RPC response
E-->>Sch: ModelRunnerOutputHighlights from GPUModelRunner.execute_model (vllm/v1/worker/gpu_model_runner.py):
- InputBatch construction: pads to the right ubatch size, builds block tables, slot mappings, and attention metadata for the active backend.
- CUDA graph dispatch: depending on
CompilationConfig.cuda_graph_mode(PIECEWISE,FULL,FULL_AND_PIECEWISE,NONE), the runner replays a captured graph or runs eager. - Forward context:
vllm/forward_context.pymakes per-step state visible to layers (active LoRAs, MM features, KV connector roles). - Sampling:
vllm/v1/sample/sampler.pyruns greedy / top-k / top-p / penalties / etc.;RejectionSamplerruns spec-decode verification (rejection_sampler.py). - KV connector hooks:
kv_connector_model_runner_mixin.pycallsconnector.start_load_kvbefore forward andwait_for_saveafter. - Speculative decoding: when configured, the runner produces draft tokens (n-gram, EAGLE, MTP, etc.) for the next step.
CUDA graph mode
vllm/config/compilation.py defines:
CUDAGraphMode.NONE— eager forwardCUDAGraphMode.PIECEWISE— replays graphs for static-shape sub-batches; falls back to eager for the restCUDAGraphMode.FULL— captures the entire forwardCUDAGraphMode.FULL_AND_PIECEWISE— full graph for hot shapes plus piecewise fallback
The dispatcher (vllm/v1/cudagraph_dispatcher.py) selects which graph to replay each step. Encoder-side capture is in vllm/v1/worker/encoder_cudagraph.py.
Sleep / wake
Workers can be put to sleep to free GPU memory:
WorkerBase.sleep(level)evicts model weights and/or KV cache to host memory, depending on level.WorkerBase.wake_up(tags)brings them back. Tags identify which buffers to restore (weights,kv_cache).
This is exposed at the executor level (Executor.sleep / wake_up) and used for warm-spare workflows where a long-running process needs to free GPU memory between batches.
RPC plumbing
Executor.collective_rpc(method, args, kwargs) is the universal way to call into all workers. Implementation differs:
- MultiprocExecutor — Python pipes between the EngineCore process and worker subprocesses. Methods are named or sent as cloudpickle-serialized callables.
- RayExecutor — Ray actor calls; uses
vllm/v1/metrics/ray_wrappers.pyfor stats forwarding. - UniProc / ExternalLauncher — direct in-process calls.
collective_rpc supports non_block=True to return Futures, used by async scheduling and multi-step pipelines.
Per-platform workers
| Platform | Worker | Runner |
|---|---|---|
| CUDA | vllm/v1/worker/gpu_worker.py |
gpu_model_runner.py |
| ROCm | (CUDA worker; ROCm-specific paths in runner) | (CUDA runner with ROCm dispatch) |
| CPU | cpu_worker.py |
cpu_model_runner.py |
| XPU | xpu_worker.py |
xpu_model_runner.py |
| TPU | (plugin, out-of-tree) | uses tpu_input_batch.py from the in-tree pieces |
Entry points for modification
- New executor backend: subclass
Executor, implement_init_executor,collective_rpc,check_health. Register via thedistributed_executor_backendstring or by passing a class. - New worker capability: add a method on
WorkerBaseand call it viacollective_rpc("your_method", ...)from the executor. - Per-step state in the runner: extend
InputBatch(gpu_input_batch.py) and consume inGPUModelRunner.execute_model. - Custom CUDA graph behavior: tweak
vllm/v1/cudagraph_dispatcher.pyandvllm/compilation/cuda_graph.py.
For how layers and kernels are arranged, see Model executor. For how attention metadata is wired into the forward pass, see Attention backends.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.