Repository navigation
feat(racer): GPUDirect reads into client host and GPU memory #989
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Jordan Olshevski (jveski)
wants to merge
87
commits into
main
Choose a base branch
from
racer-gpudirect-reads
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
87 commits
Select commit
Hold shift + click to select a range
010a08a
docs(racer): design GPUDirect reads
jveski e87c899
feat(racer): add pure Python Racer client with host-read and gpu-read
jveski 3ae47c0
feat(racer): add racer-vllm loader plugin, test target and CI job
jveski ada9fc7
feat(racer): inject NVIDIA driver libraries into the dataplane
jveski aa185e5
feat(racer): mount the client socket into labeled app containers
jveski d35c08c
docs(racer): document GPU reads, client socket mount, and vLLM loading
jveski 3d9b68c
docs(racer): match vLLM guide to the racer-vllm fetch flow
jveski 3ec5c32
docs(racer): align GPUDirect design with client fallback and NVIDIA knob
jveski f2fd0b4
feat(racer): expose physical extent translation on Slab
jveski de531aa
feat(racer): parse record prefixes and locate aligned ciphertext
jveski 7121212
feat(racer): floor the page record unit at 4 KiB for direct reads
jveski 78b8dfd
feat(racer): expose zeroizing XAES-256-GCM page subkey derivation
jveski b1c794f
feat(racer): add racer-gpu crate with C AES-GCM and CRC64 decrypt core
jveski e8f4cbe
feat(racer): add CUDA driver shim and GPU decrypt kernels
jveski 3d7c602
feat(racer): add racer-gpu safe API, shim loader, and simulation backend
jveski c3f662a
feat(racer): add cuda feature enabling the racer-gpu native shim
jveski 98d55e1
build(racer): add RACER_NATIVE_CUDA image build argument
jveski bb37e75
ci(racer): build the CUDA shim, emulate GPU kernels, and test racer-gpu
jveski 24e83a3
docs(racer): clarify GPU AES timing note
jveski 64241c6
feat(racer): add io_uring RecvMsg with SCM_RIGHTS and shared position…
jveski 270f015
feat(racer): parse host-read and gpu-read requests and build direct h…
jveski 83dffba
feat(racer): collect SCM_RIGHTS descriptors while reading request heads
jveski 3a232ab
feat(racer): serve host-read and add the gpu-read sink seam
jveski 9c566bc
test(racer): cover host-read and gpu-read over the client socket
jveski 911dade
test(racer): wait for direct-read connections to close on a deadline
jveski 881384a
fix(racer): do not mark host-read unsupported when the destination is…
jveski 1a2be97
test(racer): align fake server with dataplane and add golden wire tests
jveski 6239141
feat(racer): parse RACER_ENABLE_GPU and RACER_GPU_STAGING_BYTES
jveski ace6881
feat(racer): serve gpu-read through a process-wide GPU helper thread
jveski 44d23d3
feat(racer): test gpu-read end to end on simulated GPUs
jveski a78ec79
feat(racer): document gpu-read and GPU configuration
jveski 0fce9d9
feat(racer): simulate cuFile reads of O_DIRECT files
jveski 4702141
feat(racer): locate v2 disk ciphertext for direct reads and derive GP…
jveski 9cf7e03
feat(racer): deliver sealed pages to gpu-read and decrypt them on the…
jveski 8ab58e3
test(racer): cover GPU decryption of sealed gpu-read pages
jveski 8d20b24
chore(racer): satisfy clippy for sealed gpu-read pages
jveski 8fc1743
docs(racer): describe GPU decryption of gpu-read pages
jveski aab1fff
test(racer): close pyproject.toml file handle in entry point test
jveski d7115b1
test(racer): assert vLLM loader register() registers only once
jveski 54e97e1
test(racer): use a single import style for the racer module
jveski 1b8b30e
test(racer): use consistent returns in fake server dispatch
jveski 4bc4c32
refactor(racer): close vLLM loader clients with context managers
jveski 903a1e3
chore(racer): add cc to NOTICE
jveski ca32343
fix(racer): explain ignored errors in Python CUDA finalizers
jveski 9504276
test(racer): explain ignored wakeup errors in the fake server
jveski 18594b5
fix(racer): record vLLM staging events on the target device stream
jveski 59f3055
fix(racer): fence GPU work before releasing vLLM staging targets
jveski 81c5031
fix(racer): bind the allocation's CUDA context for host copies
jveski 19e4615
fix(racer): cap descriptors received across a request head
jveski 394b6bb
fix(racer): close vLLM weights iterator before closing the client
jveski 0dd0544
fix(racer): release dedicated GPU targets as soon as they are consumed
jveski 15d57bc
fix(racer): retain failed GPU verdicts the owner mailbox cannot carry
jveski bbae30c
fix(racer): ship cuFile library in CUDA dataplane image
jveski 71450b0
feat(racer): add dma-buf device page pools to rdma-verbs
jveski 4cb3673
feat(racer): back simulated GPU staging with a memfd a dma-buf export…
jveski f1ad90b
feat(racer): GPU landing buffers for RDMA and decrypt from them
jveski aa9f79f
feat(racer): register GPU landing buffers on RDMA rails and grant int…
jveski fdf0b57
feat(racer): receive a requested peer page into a GPU landing buffer …
jveski c6b7522
feat(racer): land gpu-read peer pages in GPU memory through the fill …
jveski f2f5183
test(racer): cover gpu-read peer landing through the fill path
jveski 501a2b7
test(racer): land a native peer page in GPU memory over simulated RDMA
jveski 7d8d677
docs(racer): describe GPUDirect RDMA landing and rdma-verbs device pools
jveski e1fdda5
chore(racer): satisfy clippy for GPU landing receive
jveski d966c26
fix(racer): settle failed landed GPU verdicts on the reading worker
jveski abfd8cb
docs(racer): describe RACER_GPU_STAGING_BYTES as per-GPU read staging
jveski 7110a4d
refactor(racer): drop dead assignment in vLLM weight stream cleanup
jveski db672ba
fix(racer): stage torch tensor targets on their own GPU
jveski 8bdbf05
fix(racer): retain host copy sources until GPU stream sync
jveski 8f3e796
fix(racer): reject non-integer safetensors shapes and offsets
jveski 032f4e3
fix(racer): release host registration exports when pinning fails
jveski 41f8f6e
refactor(racer): drop the host registration ctypes export explicitly
jveski 6b20bc8
fix(racer): attach received descriptors to the request head they arri…
jveski aada0b3
refactor(racer): build the derived GCM nonce from the XAES nonce dire…
jveski 2fce902
fix(racer): reopen slab descriptors without O_NOFOLLOW for cuFile
jveski 70f6c27
fix(racer): turn off cuFile compatibility mode by default
jveski 64a8fac
fix(racer): give cuFile the udev database and libnuma
jveski 7730f3e
feat(racer): count GPUDirect disk reads
jveski 613331f
fix(racer): do not retry a failed cuFile driver open
jveski 11a50eb
fix(racer): decrypt cached ciphertext on the GPU for gpu-read
jveski ac56411
docs(racer): describe gpu-read with a ciphertext-only memory cache
jveski 0bb9061
feat(racer): run cuFile reads off the GPU helper thread
jveski 7a543eb
perf(racer): read slabs into GPU staging on four reader threads
jveski ee71612
perf(racer): open the cuFile driver when the GPU helper starts
jveski 5f979a8
perf(racer): end vLLM loader chunks on Racer page boundaries
jveski d0a8472
docs(racer): describe cuFile reader threads and the eager driver open
jveski f0a94a1
perf(racer): keep four vLLM loader reads in flight by default
jveski 682c723
perf(racer): fill a GPU read's whole window directly
jveski File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
There are no files selected for viewing
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
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
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| __pycache__/ | ||
| *.egg-info/ | ||
| .pytest_cache/ | ||
| build/ | ||
| dist/ |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,83 @@ | ||
| # racer-vllm | ||
|
|
||
| A vLLM plugin that loads model weights from a Racer cache. It adds the load | ||
| format `racer`. It uses the `racer-client` package. | ||
|
|
||
| ## Install | ||
|
|
||
| ``` | ||
| pip install ./clients/python/racer ./clients/python/racer-vllm | ||
| ``` | ||
|
|
||
| vLLM finds the plugin through the `vllm.general_plugins` entry point. It was | ||
| written for vLLM v0.25.x. | ||
|
|
||
| ## Model location | ||
|
|
||
| A model lives at `racer://<cache>/<bucket>/<prefix>`. The plugin reads | ||
| objects through `/run/racer/<cache>/client/socket`. Set `RACER_SOCKET` to use | ||
| another socket. Object keys follow racer-object, so `<bucket>/<prefix>/<file>` | ||
| names the S3 object. | ||
|
|
||
| ## Use | ||
|
|
||
| vLLM reads the model config and tokenizer before it loads weights, so these | ||
| small files must be on local disk first. Fetch them, for example in an init | ||
| container: | ||
|
|
||
| ``` | ||
| racer-vllm-fetch racer://models/my-bucket/llama-3-8b /models/llama-3-8b | ||
| ``` | ||
|
|
||
| This copies `config.json`, tokenizer files, `model.safetensors.index.json` | ||
| and similar files, plus small files named in the index metadata. It also | ||
| writes `racer-model.json`, which tells the loader where the weights are. | ||
|
|
||
| Then start vLLM on that directory: | ||
|
|
||
| ``` | ||
| vllm serve /models/llama-3-8b --load-format racer \ | ||
| --model-loader-extra-config '{"staging_bytes": 268435456, "concurrency": 4}' | ||
| ``` | ||
|
|
||
| Extra config keys, all optional: | ||
|
|
||
| | Key | Default | Meaning | | ||
| |---|---|---| | ||
| | `uri` | from `racer-model.json` | The `racer://` model location. | | ||
| | `staging_bytes` | 256 MiB | Size of each GPU staging buffer. | | ||
| | `concurrency` | 4 | Staging buffers, and reads in flight. | | ||
| | `clone` | true | Copy each tensor out of staging before handing it to vLLM. | | ||
| | `socket` | none | Client socket path. | | ||
| | `use_vmm` | true | Allow exportable `cuMemCreate` staging, needed for `gpu-read`. | | ||
|
|
||
| On fast local disks, more reads in flight help only when the dataplane also | ||
| fetches further ahead. Set `concurrency` to 8 together with a larger | ||
| `RACER_RANGE_WINDOW_PAGES` and `RACER_GPU_STAGING_BYTES` on the dataplane. See | ||
| the GPU reads section of the Racer reference. | ||
|
|
||
| ## How it loads | ||
|
|
||
| 1. Find the safetensors files from `model.safetensors.index.json`, or use | ||
| `model.safetensors`. | ||
| 2. For each file, read the 8-byte header length and the JSON header. | ||
| 3. Group tensors into ranges that fit a staging buffer. Each range ends on a | ||
| Racer page boundary (16 MiB), so no page is read twice. A tensor that | ||
| crosses the boundary is split across two ranges and joined again before vLLM | ||
| sees it. Read each range into | ||
| GPU memory with `gpu-read`. If the dataplane cannot do that, use | ||
| `host-read` into pinned memory and copy, else the normal stream. | ||
| 4. Give each tensor (right dtype and shape) to `model.load_weights`. While | ||
| vLLM uses one buffer, the next range is read into another. | ||
|
|
||
| A tensor bigger than a staging buffer gets its own GPU allocation. | ||
|
|
||
| With `clone` set to false, tensors are views into staging memory. They stay | ||
| valid only until vLLM asks for the next tensors after the buffer's last one. | ||
| Only use this when the model copies each weight at once. | ||
|
|
||
| ## Tests | ||
|
|
||
| ``` | ||
| make racer-python-test | ||
| ``` |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| [build-system] | ||
| requires = ["setuptools>=64"] | ||
| build-backend = "setuptools.build_meta" | ||
|
|
||
| [project] | ||
| name = "racer-vllm" | ||
| version = "0.1.0" | ||
| description = "vLLM model loader that reads weights from a Racer cache" | ||
| readme = "README.md" | ||
| license = { text = "Apache-2.0" } | ||
| requires-python = ">=3.10" | ||
| dependencies = ["racer-client"] | ||
|
|
||
| [project.optional-dependencies] | ||
| test = ["pytest"] | ||
|
|
||
| [project.entry-points."vllm.general_plugins"] | ||
| racer = "racer_vllm:register" | ||
|
|
||
| [project.scripts] | ||
| racer-vllm-fetch = "racer_vllm.model:main" | ||
|
|
||
| [tool.setuptools.packages.find] | ||
| where = ["src"] | ||
|
|
||
| [tool.pytest.ini_options] | ||
| testpaths = ["tests"] |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| # Copyright (c) Microsoft Corporation. | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
| """vLLM plugin that loads model weights from a Racer cache.""" | ||
|
|
||
| from .loader import LOAD_FORMAT, register | ||
| from .model import ModelURI, fetch, parse_uri | ||
|
|
||
| __all__ = ["LOAD_FORMAT", "ModelURI", "fetch", "parse_uri", "register"] |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| # Copyright (c) Microsoft Corporation. | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
| """vLLM model loader for --load-format racer. | ||
|
|
||
| Targets vLLM v0.25.x. The loader interface used is | ||
| vllm/model_executor/model_loader/base_loader.py (BaseModelLoader with | ||
| download_model(model_config) and load_weights(model, model_config)), and | ||
| registration uses register_model_loader from | ||
| vllm/model_executor/model_loader/__init__.py. Recent main adds optional hooks | ||
| (get_external_weight_memory, create_model) with defaults, so this loader | ||
| works there too. | ||
| """ | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import logging | ||
|
|
||
| from .model import client_for, resolve_uri, weight_files | ||
| from .weights import DEFAULT_DEPTH, DEFAULT_STAGING_BYTES, TorchBackend, iter_weights | ||
|
|
||
| log = logging.getLogger(__name__) | ||
|
|
||
| LOAD_FORMAT = "racer" | ||
| _registered = False | ||
|
|
||
|
|
||
| def _settings(extra: dict) -> dict: | ||
| return { | ||
| "staging_bytes": int(extra.get("staging_bytes", DEFAULT_STAGING_BYTES)), | ||
| "concurrency": int(extra.get("concurrency", DEFAULT_DEPTH)), | ||
| "clone": bool(extra.get("clone", True)), | ||
| "socket": extra.get("socket"), | ||
| "use_vmm": bool(extra.get("use_vmm", True)), | ||
| } | ||
|
|
||
|
|
||
| def make_loader_class(base): | ||
| class RacerModelLoader(base): | ||
| """Loads safetensors weights from a Racer cache. | ||
|
|
||
| model_loader_extra_config keys: | ||
| uri racer://<cache>/<bucket>/<prefix>; optional when the | ||
| model directory has racer-model.json (racer-vllm-fetch) | ||
| staging_bytes size of each GPU staging buffer (default 256 MiB) | ||
| concurrency number of staging buffers and reads in flight (default 4) | ||
| clone copy each tensor out of staging (default true) | ||
| socket client socket path (else RACER_SOCKET, else the cache) | ||
| use_vmm allow exportable cuMemCreate staging (default true) | ||
| """ | ||
|
|
||
| def __init__(self, load_config): | ||
| super().__init__(load_config) | ||
| self.extra = dict(getattr(load_config, "model_loader_extra_config", None) or {}) | ||
| self.settings = _settings(self.extra) | ||
|
|
||
| def _client(self, model_config): | ||
| uri = resolve_uri(str(model_config.model), self.extra) | ||
| client = client_for(uri, self.settings["socket"], staging_bytes=self.settings["staging_bytes"]) | ||
| return uri, client | ||
|
|
||
| def download_model(self, model_config) -> None: | ||
| # Nothing to download; check the weights are reachable. | ||
| uri, client = self._client(model_config) | ||
| with client: | ||
| weight_files(client, uri) | ||
|
|
||
| def load_weights(self, model, model_config) -> None: | ||
| uri, client = self._client(model_config) | ||
| s = self.settings | ||
| with client: | ||
| backend = TorchBackend(clone=s["clone"], use_vmm=s["use_vmm"]) | ||
| weights = iter_weights(client, uri, backend, s["staging_bytes"], s["concurrency"]) | ||
| try: | ||
| loaded = model.load_weights(weights) | ||
| finally: | ||
| # Run the generator's cleanup (executor shutdown and GPU | ||
| # fences) before the client closes, even if load_weights | ||
| # raised or returned without exhausting the iterator. | ||
| weights.close() | ||
| if loaded is not None and getattr(model_config, "quantization", None) is None: | ||
| missing = {n for n, _ in model.named_parameters()} - set(loaded) | ||
| if missing: | ||
| raise ValueError(f"racer loader: weights not loaded: {sorted(missing)[:10]}") | ||
|
|
||
| return RacerModelLoader | ||
|
|
||
|
|
||
| def register() -> None: | ||
| """vllm.general_plugins entry point. Safe to call more than once.""" | ||
| global _registered | ||
| if _registered: | ||
| return | ||
| from vllm.model_executor.model_loader import register_model_loader | ||
| from vllm.model_executor.model_loader.base_loader import BaseModelLoader | ||
|
|
||
| register_model_loader(LOAD_FORMAT)(make_loader_class(BaseModelLoader)) | ||
| _registered = True | ||
Oops, something went wrong.
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.