computility-run.yaml:
max-num-seqs 1→256: benchmark sweeps [128,256] concurrent seqs,
current config processes 1 while 127 queue. KV cache budget:
256 seqs × 2048 tokens × 80KB/token = 41.9GB < 45GB available.
max-num-batched-tokens 8192→32768: support 256 concurrent prefills.
gpu-memory-utilization 0.9→0.95: provide KV cache headroom.
Dockerfile:
Deploy paged_attention_v2_triton.py to vllm package path so
try-triton-first logic in _custom_ops.py can find it. Falls back
to PyTorch V2 automatically if Triton V2 fails (SMEM/runtime).
muh/tuning/common.cuh:
scale_mem_bound max_smem now a parameter (default 48KB). Allows
policy_selectors to pass hw.max_shared_memory_per_block if actual
SMEM differs from CCCL 48KB assumption.
muh/tuning/tuning_transform.cuh:
bytes_in_flight 16KB→32KB. Old derivation used 900/50=18 GB/s/SM
(wrong, SM=16 confirmed). Actual per-SM BW = 56 GB/s.
32KB is estimate pending benchmark sweep.
SM count 50→16 corrections across all affected files.
V1 paged_attention (decode ≤ 8192):
Fix: head_mapping int→Tensor conversion.
VERIFIED: matches manual attention, max diff < 0.001.
Perf: 0.034ms (256 tok), 0.059ms (1K), 0.169ms (4K), 0.272ms (8K).
V2 paged_attention (decode > 8192):
Native V2 kernel EXISTS (ixf_F.vllm_single_query_cached_kv_attention_v2)
but produces INCORRECT output (diff=1.28 vs V1 on same data).
Using Python V2 fallback (paged_attention_v2_pytorch.py) for now.
The native V2 expects [B,H,bs,d] layout (confirmed) but the output
values don't match even with correct layout conversion.
Prefill (flash_attn_func):
VERIFIED: ixf_F.flash_attn_func(q, k, v, causal=True) works
with head_dim=256 and GQA (num_kv_heads=4).
Patched into xformers.py as first-attempt before _run_sdpa_fallback.
Triton: symlinked /usr/local/lib/ → /usr/local/corex/lib64/ for import.
Hardware testing confirmed:
V1: K=[blocks, kv_heads, head_dim/x, block_size, x] (5D), V=[blocks, kv_heads, head_dim, block_size] (4D) → OK
V2: K=[blocks, kv_heads, block_size, head_dim] (4D), V=[blocks, kv_heads, block_size, head_dim] (4D) → OK
V2 with V1's layout → FAIL (Expected key_cache.dim()==4, value_cache.size(3)==head_size)
V1 and V2 use DIFFERENT cache memory layouts in ixformer.
V2 patch now converts cache on the fly before calling native kernel:
K: permute(0,1,3,2,4).reshape → [B,H,bs,d]
V: permute(0,1,3,2).contiguous → [B,H,bs,d]
This is a view+reshape for K (no copy if contiguous) and a transpose+contiguous for V.
The cost is one V copy per decode step, but this enables the native compiled V2 kernel
which is 10-100x faster than the Python fallback it replaces.
Qwen3.6-35B-A3B has 256 experts × top_k=8. The baseline prefill MoE:
for eid in unique_eids: # up to 256 iterations
tokens = hidden_states[tok_ids] # SCATTERED gather
F.linear(tokens, w13[eid])
Problem: hidden_states[tok_ids] creates a non-contiguous gather for each expert.
With 16384 tokens × 256 experts, this is 256 scattered gathers per layer.
Optimization (CCCL segmented-sort pattern):
1. Flatten all token-expert pairs: (T×K,) assignments
2. Sort by expert ID: tokens for same expert become CONTIGUOUS
3. Each F.linear gets contiguous input → much better memory access
4. Activation (silu × up) computed in ONE fused op across all pairs
5. index_add_ scatter-back is one kernel call
Memory access improvement:
Before: 256 × hidden_states[random_indices] → scattered HBM reads
After: sorted_tokens[start:end] → sequential HBM reads per expert
The expert loop still exists (can't batch variable-size GEMMs with F.linear),
but each iteration reads contiguous memory instead of scattered indices.
Complete rewrite of qwen3_6_scripts/paged_attn.py with 4 optimizations:
1. _forward_prefix_pytorch: Pre-gather ALL context K/V outside tile loop
Before: each of 195 tiles does key_cache[blk_ids].permute().contiguous()
After: ONE key_cache[all_ctx_blk_ids].permute().contiguous() upfront,
tile loop just does ctx_k_t[:, :, start:end] (view, no copy)
Eliminates 194 redundant gather+permute+contiguous calls per prefill.
2. forward_decode: V2 enabled via original heuristic
Before: use_v1 = True (hardcoded, V2 was NotImplementedError)
After: V2 works (paged_attention_v2_pytorch), use vllm's heuristic:
seq_len > 8192 → V2 (partitioned, better parallelism)
seq_len <= 8192 → V1 (single-block, less overhead)
3. forward_prefix: Triton try/fallback
First call attempts Triton context_attention_fwd (if HAS_TRITON).
If it hangs/errors, permanently falls back to PyTorch.
If it works: 10-50x prefill improvement.
4. _PYTORCH_DECODE_THRESHOLD: 32768 → 65536
Keeps more decode requests on the fast compiled v1 kernel.
All changes are safe: Triton has try/except, V2 fallback exists,
threshold can be lowered back if v1 crashes at 64K.
Two optimizations that target the real bottlenecks:
1. patch_enable_triton.py: Enable Triton Flash Attention for prefill
- Sets HAS_TRITON = True (was hardcoded False)
- Adds try/except wrapper in forward_prefix: tries Triton kernel first,
permanently falls back to PyTorch if it hangs or errors
- Combined with patch_triton_tuning.py (BLOCK=64, NUM_WARPS=4),
this keeps SMEM at 32KB ≤ 48KB limit
- If Triton works: 10-50x prefill speedup (GPU-parallel Flash Attention
vs Python for-loop)
- If Triton still hangs: auto-fallback, no worse than baseline
2. patch_vectorized_decode.py: Raise _PYTORCH_DECODE_THRESHOLD 32768 → 65536
- Compiled ixf_F.paged_attention_v1 is ~100x faster than Python fallback
- Baseline conservatively falls back at 32K, may work fine at 64K
- If v1 crashes at higher seq_lens, threshold can be lowered back
Why these matter (competition scoring):
Token吞吐加权值 = Output TPS × 16.796 + Input TPS × 2.799 + Cache TPS × 0.56
Prefill (Input TPS, 14% weight): _forward_prefix_pytorch is a Python
for-loop doing matmul+softmax per tile. Triton kernel does this in a
single GPU launch with Flash Attention online softmax.
Decode (Output TPS, 83% weight): Every seq_len between 32K-65K that
stays on compiled v1 instead of falling to Python saves ~100x per token.
Deploy order in Dockerfile:
1. patch_ops.sh (baseline functional patches)
2. patch_triton_tuning.py (BLOCK=64, NUM_WARPS=4)
3. patch_enable_triton.py (HAS_TRITON=True + try/fallback)
4. patch_vectorized_decode.py (threshold 32K → 64K)
The single biggest performance bottleneck in the baseline:
paged_attention_v2 = raise NotImplementedError()
paged_attn.py: use_v1 = True (hardcoded to avoid calling V2)
V1 limitation: processes entire KV sequence in one kernel launch.
For seq_len=100K, this is a single massive attention computation.
V2: splits into PARTITION_SIZE=512 chunks, runs them in parallel,
then reduces with log-sum-exp. 195 parallel partitions vs 1.
Implementation (paged_attention_v2_pytorch.py):
Phase 1: Per-partition attention
- For each (seq, head, partition): compute QK^T, softmax, weighted V sum
- Store partial: tmp_output, exp_sums, max_logits (per partition)
Phase 2: Cross-partition reduction (log-sum-exp)
- global_max = max(max_logits across partitions)
- rescale = exp(partition_max - global_max) × partition_exp_sum
- output = Σ (rescale / total_sum) × partition_output
This is the same algorithm as vllm's paged_attention_v2_kernel.cu:
- The reduction pattern is identical to CCCL's block_reduce_warp_reductions
(combine partial statistics from independent segments)
- The online softmax tiling is the same as Flash Attention's partitioning
Integration:
- patch_paged_attention_v2.py patches _custom_ops.py and paged_attn.py
- Removes use_v1=True hardcode → V2 used for seq_len > 8192
- Dockerfile adds the patch step
This is a PyTorch implementation (no CUDA compilation needed).
Next step: if /usr/local/corex/ has ixcc or nvcc-compatible compiler,
replace with compiled CUDA kernel for further speedup.
After reading the full baseline (enginex-vllm-bi100-qwen36-main.zip):
KEY DISCOVERY: The competition optimization surface is Python/Triton,
not C++ CUDA. There is no csrc/ directory. All CUDA kernels are
precompiled in vllm._C and ixformer .so files. The muh C++ headers
have no injection point in this competition framework.
What CAN be optimized:
1. Triton kernel parameters (prefix_prefill.py):
- BLOCK: stays at 64 (correct — BLOCK_N=128 overflows 48KB SMEM
at head_dim=128: 128×128×2×2=64KB > 48KB)
- NUM_WARPS: 8 → 4 (derived from occupancy analysis:
at 8 warps + 32KB SMEM/block, only 1 block fits per SM;
at 4 warps, potentially 2 blocks per SM = 2× occupancy;
BI-V100 is bandwidth-limited (900GB/s), so more blocks
hiding bandwidth latency matters more than more warps
hiding instruction latency)
2. computility-run.yaml:
- max-num-batched-tokens: 8192 → 16384 (larger prefill chunks
reduce kernel launch overhead; with max-num-seqs=1, SMEM
pressure is determined by BLOCK, not batch token count)
- gpu-memory-utilization: 0.9 → 0.95 (model uses ~17.5GB/GPU,
KV cache for 100K tokens ≈ 1.38GB, plenty of headroom)
3. Added Dockerfile with patch_triton_tuning.py step.
4. Analysis document in optimizations/prefix_prefill_patch.py
with full SMEM/register/occupancy derivation.