1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
"""Engine-agnostic VMM arena lifecycle helpers.
`maybe_enter_vmm_arena(ctx)` is the integration seam each engine adapter
calls around its weight-load envelope. The arena core
(``modelexpress.vmm.arena``, ``backend``, ``hook``, ``_alloc_ext``) only
requires PyTorch's ``CUDAPluggableAllocator`` interface; this module
adds the env-var handling, per-device arena lifetime tracking, and the
``use_arena`` wrapping that every PyTorch-based engine adapter would
otherwise re-implement. Engine modules call into this; this module does
not call back into engine code.
``log_arena_post_load(ctx)`` is the corresponding diagnostic hook that
runs after the load body returns.
"""
=
# Active VmmArenas keyed by device_id. Held at module scope so the
# arenas (and their physical CUDA mappings) outlive the load envelope
# and survive for the lifetime of the loaded model. Cleanup happens at
# process exit; see arena.close() handling in tests for explicit
# teardown.
: =
"""If ``MX_VMM_ARENA=1`` is set, install a VmmArena hook around the
load envelope. Otherwise yield without installing anything.
Engine integration seam. The caller is the engine adapter (vLLM,
SGLang, TRT-LLM, ...) that owns the load lifecycle; this helper
handles every step that does not depend on engine internals:
- reads the ``MX_VMM_ARENA`` env var and warns on stale
``MX_VMM_ARENA_BYTES`` / ``MX_VMM_ARENA_CHUNK_BYTES``;
- probes ``ARENA_AVAILABLE`` (whether the optional
``modelexpress.vmm._alloc_ext`` C extension built at install time)
and yields a no-op fallback when it is unavailable;
- constructs ``CudaVmmBackend`` and ``VmmArena`` inside
``ctx.target_device`` so the backend sees the right CUDA context
on multi-GPU workers;
- manages the module-level ``_vmm_arenas`` dict keyed by
``ctx.device_id`` and publishes the arena after the wrapped body
completes successfully;
- wraps ``vmm.use_arena`` around the yielded scope so allocations
issued by the engine inside the ``with`` land in the arena's
bump range.
The fields read off ``ctx`` (``device_id``, ``target_device``,
``global_rank``, ``vmm_arena``) are populated by the engine
adapter, so this function does not need to know which engine
invoked it.
Tunable:
MX_VMM_ARENA=1 enable. The only knob.
The arena reserves 16 TiB of VA per device (VA only, no physical
commit until cuMemMap). Each ``mx_malloc(size)`` becomes one
``cuMemCreate(size_aligned)`` + ``cuMemMap(next_va)`` +
``cuMemSetAccess`` at the bump pointer; ``mx_free(va)`` does
``cuMemUnmap + cuMemRelease`` on the matching allocation. No
chunked sub-allocation. PyTorch's caching allocator amortizes
tensor allocations into pool segments before reaching us, so one
plugin call == one physical handle.
End-of-load registration goes through
``NixlTransferManager.register_arena``, which calls
``cuMemGetHandleForAddressRange`` + ``ibv_reg_dmabuf_mr`` over the
full bump range and produces one MR for the entire arena.
``MX_POOL_REG=1`` remains compatible but is no longer required for
the single-MR property.
Lifecycle and failure handling:
- The arena is constructed inside ``ctx.target_device`` so the
backend sees the right CUDA context on multi-GPU workers.
- The arena is published to the module-level ``_vmm_arenas`` dict
only after the wrapped body completes successfully. If the body
raises, the freshly created arena is closed and not retained.
- If ``_vmm_arenas`` already has an arena for this device (second
load on the same worker), the prior arena is closed before
installing a new one. Silently corrupts the prior model's tensors
if any are still in use; we log a WARNING. Hot-swap-safe arena
lifetime tied to engine teardown is a TODO.
- PyTorch's caching allocator may not return every freed segment
to us during load. The bump pointer therefore tracks cumulative
allocation, not peak live size. With 16 TiB reserved, this is
bounded only by HBM (we never exhaust VA).
"""
# p2p_enabled is False for the speculative draft's second load;
# skip the arena so it does not replace the target model's.
yield
return
yield
return
# The previous chunked-arena design accepted MX_VMM_ARENA_BYTES and
# MX_VMM_ARENA_CHUNK_BYTES env vars. The current design ignores
# both (16 TiB VA reserve is unconditional, no chunked
# sub-allocation). Warn on first entry so an operator who carried
# the old env vars forward from a pre-refactor manifest sees one
# clear message rather than silent behavior change.
# The modelexpress.vmm._alloc_ext C extension is built optional (see
# setup.py). If a working compiler wasn't available at install time,
# the .so is absent and the arena machinery cannot be installed.
# Fall back to the non-arena path with a clear warning rather than
# crashing the load.
yield
return
# Lazy imports - keep the cuda-python dependency optional for users
# who don't enable the arena. Import from submodules directly so
# test monkeypatches on `modelexpress.vmm.{backend,hook}.X` are
# picked up.
# Pre-existing arena from a prior load on the same worker.
# Replacing it silently corrupts any still-live tensors that
# point into the old arena's VA range; the typical engine
# lifecycle only re-enters the load path when the prior model
# has been torn down, but there is no programmatic guarantee.
# Log the replacement so an audit can catch a
# hot-swap-while-serving situation.
=
# CudaVmmBackend requires a CUDA context on the calling thread. On
# multi-GPU workers the current device may not match ctx.device_id
# until the engine enters ctx.target_device, so we enter it here
# just for backend construction. The caller is expected to enter
# ctx.target_device again around the body; torch device contexts
# are reentrant so this is fine.
=
=
# Only publish the arena into the module-level dict AFTER the body
# completes successfully. On exception the arena gets closed and is
# not retained, so a retry-on-different-strategy or upstream
# error-handling starts from a clean state.
#
# Also stash the arena on ctx.vmm_arena so the strategy chain's
# register_tensors can pass it to NixlTransferManager.register_arena
# for single-MR-via-dmabuf registration over the full bump range.
=
= False
yield
=
= True
= None
"""Log arena state after the load envelope returns.
The single-MR registration via ``cuMemGetHandleForAddressRange`` +
``ibv_reg_dmabuf_mr`` over ``[base, base+used_bytes)`` already ran
inside ``LoadStrategyChain`` via
``NixlTransferManager.register_arena``; this hook is purely
diagnostic. Validated on the dmabuf/IB path, Blackwell + ConnectX
over InfiniBand: the registration succeeds over a VA range with
mid-range holes from prior ``cuMemUnmap`` calls, and the dmabuf pin
keeps live tensor pages addressable to the HCA. On ``cuda_ipc`` a
multi-allocation arena cannot be covered by one MR at all, and
``register_arena`` falls back to per-tensor registration.
"""
=
return
, =