vllm.v1.worker.gpu.spec_decode.xpress.kernels
¶
Triton kernels for one XPress Jacobi pass.
A pass over a batch of N blocks (B slots each, slot 0 = the verified anchor) is:
latent u[n, b, :] = causal_mix(hcache[n, b] + embed(prev[n, b]) @ W_lat)
x[n, b, :] = u[n, b, :] + SwiGLU_MLP(u[n, b, :])
readout logits[n, b, :] = base[n, b, :] + x[n, b, :] @ W2 (a matmul)
argmax blk[n, b] = argmax_v logits[n, b, v]
Only the latent and argmax stages are custom. The latent stage is a dozen tiny ops per block that torch launches separately; fusing them removes the launch floor, which is what bounds a small head. The argmax stage would otherwise materialize [N * (B - 1), V] bf16 logits per pass (~146 MB at N = 32, V = 152k) only to reduce them; fusing the add into the reduction never writes them.
Functions:
-
fused_add_argmax_to_blk–blk[n, 1 + b] = argmax_v(base[r, v] + bias[r, v])for row r = n * (B-1) + b. -
fused_topc_argmax_to_blk–Run
_xpress_topc_argmax_to_blk_kernel; see it for shapes. -
xpress_latent_pass–Run
_xpress_latent_kernelover N blocks; see its docstring for shapes.
_xpress_add_argmax_partial_kernel(base_ptr, bias_ptr, out_val_ptr, out_idx_ptr, V, stride_base_r, stride_bias_r, stride_ov_r, stride_oi_r, BLOCK_V)
¶
Stage 1 of the fused argmax: per (row, vocab chunk) max and its index.
Grid: (ceil(V / BLOCK_V), rows). Each program sums one BLOCK_V-wide chunk of
base[row] + bias[row] in registers and reduces it, so the full [rows, V]
sum is never stored.
Parameters:
-
(base_ptr¶tensor) –[rows, V] base logits (bf16), row stride
stride_base_r. -
(bias_ptr¶tensor) –[rows, V] refiner bias, same layout.
-
(out_val_ptr¶tensor) –[rows, num_chunks] fp32 chunk maxima (written).
-
(out_idx_ptr¶tensor) –[rows, num_chunks] int64 chunk argmax, in vocab ids (written).
-
(V¶int) –vocabulary size; the last chunk is masked to it.
-
(stride_base_r¶int) –row stride of
base_ptr. -
(stride_bias_r¶int) –row stride of
bias_ptr. -
(stride_ov_r¶int) –row stride of
out_val_ptr. -
(stride_oi_r¶int) –row stride of
out_idx_ptr. -
(BLOCK_V¶constexpr) –vocab chunk width per program.
Source code in vllm/v1/worker/gpu/spec_decode/xpress/kernels.py
_xpress_add_argmax_reduce_to_blk_kernel(out_val_ptr, out_idx_ptr, blk_ptr, N, Bm1, stride_ov_r, stride_oi_r, BLOCK_N)
¶
Stage 2 of the fused argmax: reduce one row's chunk maxima into blk.
Grid: (rows,). Row r is slot 1 + r % (B - 1) of block r // (B - 1);
the winner is written to blk[n, slot] so the next pass reads it in place.
Source code in vllm/v1/worker/gpu/spec_decode/xpress/kernels.py
_xpress_latent_kernel(blk_ptr, am1_ptr, xh_ptr, lat_out_ptr, w1_ptr, wlat_ptr, mixl_ptr, wg_ptr, wu_ptr, wd_ptr, B, R, H, BP, KC, RC, HT, DO_MLP)
¶
The latent stage of one Jacobi pass for one block, fused end to end.
Grid: (N, R // RC), one program per block and RC latent channels. For every
slot b it gathers the previous slot's token embedding (slot 0 reads the
anchor's predecessor am1), adds the pass-invariant hidden cache, applies
the folded causal mixer
u[b] = sum_{j <= b} L[b, j, :] * x[j], then a SwiGLU MLP with a
residual, and writes the latent for slots 1..B-1 (slot 0 is the anchor and
is never read out).
Parameters:
-
(blk_ptr¶tensor) –[N, B] int64 current block tokens; slot b reads
blk[b - 1]. -
(am1_ptr¶tensor) –[N] int64 token preceding each block's anchor.
-
(xh_ptr¶tensor) –[N, B, R] hidden cache,
down_h(h) ++ down_g(mean h)projected to R, constant across passes. -
(lat_out_ptr¶tensor) –[N, B - 1, R] latent output (written).
-
(w1_ptr¶tensor) –[V, R] token embedding.
-
(wlat_ptr¶tensor) –[R, R] embedding-to-latent projection (transposed).
-
(mixl_ptr¶tensor) –[B, B, R] folded mixer
L * tril + I, per channel. -
(wg_ptr¶tensor) –[R, H] SwiGLU gate (transposed).
-
(wu_ptr¶tensor) –[R, H] SwiGLU up (transposed).
-
(wd_ptr¶tensor) –[H, R] SwiGLU down (transposed).
-
(B¶constexpr) –block size.
-
(R¶constexpr) –latent rank.
-
(H¶constexpr) –MLP hidden size.
-
(BP¶constexpr) –B rounded up to a power of two (tile size).
-
(KC¶constexpr) –rank chunk of the embedding projection; R holds all of W_lat at once.
-
(RC¶constexpr) –latent channels per program. Below R the MLP cannot run here, and the mixer becomes one [BP, BP, RC] product, which only fits registers when RC is small.
-
(HT¶constexpr) –MLP hidden tile size.
-
(DO_MLP¶constexpr) –whether to run the SwiGLU MLP here. False stops after the mixer and hands its output back, so
xpress_latent_passcan run the SwiGLU as GEMMs instead -- see the comment at the MLP loop for why that can be faster.
Source code in vllm/v1/worker/gpu/spec_decode/xpress/kernels.py
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 | |
_xpress_topc_argmax_to_blk_kernel(base_ptr, bias_ptr, cand_ptr, blk_ptr, C, B, BLOCK_C)
¶
Argmax over a slot's C candidates, written straight back as a token id.
Grid: (N * (B - 1),), one program per draft slot. The candidate axis fits in one block at the C values this path uses, so unlike the full-vocab epilogue there is no partial/reduce split; the gather from candidate index to vocab id happens in the same program, which removes a second kernel and its round trip.
Parameters:
-
(base_ptr¶tensor) –[rows, C] base logits gathered at the candidates (bf16).
-
(bias_ptr¶tensor) –[rows, C] refiner bias on the same candidates.
-
(cand_ptr¶tensor) –[rows, C] int64 vocab ids of the candidates.
-
(blk_ptr¶tensor) –[N, B] int64 block tokens; slot j of row n is written at j + 1.
-
(C¶int) –candidates per slot.
-
(B¶constexpr) –block size, to map a row back to its (block, slot).
-
(BLOCK_C¶constexpr) –C rounded up to a power of two.
Source code in vllm/v1/worker/gpu/spec_decode/xpress/kernels.py
fused_add_argmax_to_blk(base, bias, out_val, out_idx, blk, block_v=4096)
¶
blk[n, 1 + b] = argmax_v(base[r, v] + bias[r, v]) for row r = n * (B-1) + b.
The [rows, V] sum is never materialized: stage 1 reduces it chunk-wise in registers, stage 2 reduces the chunk maxima and scatters the winner into the block buffer, so one pass stays at three launches inside the captured graph.
Parameters:
-
(base¶Tensor) –[N * (B - 1), V] base logits for the draft slots.
-
(bias¶Tensor) –[N * (B - 1), V] refiner bias for the same rows.
-
(out_val¶Tensor) –[rows, ceil(V / block_v)] fp32 scratch for the chunk maxima.
-
(out_idx¶Tensor) –[rows, ceil(V / block_v)] int64 scratch for the chunk argmax.
-
(blk¶Tensor) –[N, B] int64 block buffer; slots 1..B-1 are written.
-
(block_v¶int, default:4096) –vocab chunk width for stage 1; sets the scratch width.
Source code in vllm/v1/worker/gpu/spec_decode/xpress/kernels.py
fused_topc_argmax_to_blk(base_c, bias_c, cand, blk)
¶
Run _xpress_topc_argmax_to_blk_kernel; see it for shapes.
Source code in vllm/v1/worker/gpu/spec_decode/xpress/kernels.py
xpress_latent_pass(blk, tok_am1, xh, lat_out, w1_weight, wlat_t, mix_kjc, wg_t, wu_t, wd_t, wgu_t=None, do_mlp=None, kc=None, rc=None, ht=64, num_stages=None)
¶
Run _xpress_latent_kernel over N blocks; see its docstring for shapes.
The caller follows this with the readout matmul lat_out @ W2 and one of
the fused argmax entry points below. do_mlp runs the SwiGLU MLP in the
kernel, else as GEMMs after it on wgu_t ([R, 2 * H], wg_t and wu_t
side by side). kc, rc, ht and num_stages set the kernel's
KC, RC, HT and pipelining depth; None picks the platform default.
Source code in vllm/v1/worker/gpu/spec_decode/xpress/kernels.py
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 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 | |