[CLEANUP] Remove 13 dead patch scripts — only 1 remains (transformers registration)

Removed (replaced by full-file cp in patch_ops.sh):
  - patch_model_runner.py → replaced by model_runner.py (1932 lines)
  - patch_xformers_sdpa_seq.py → replaced by xformers.py (901 lines)
  - patch_xformers_sdpa_seq_kernel.py → was unused
  - patch_xformers_sdpa_batch.py → was unused
  - patch_xformers_sdpa_batch_kernel.py → was unused
  - patch_vllm_qwen3_5.py → replaced by registry.py (455 lines)
  - patch_vllm_tool_parser.py → replaced by tool_parsers_init.py
  - patch_enable_triton.py → was unused
  - patch_head256_triton.py → was unused
  - patch_ixformer_native.py → was unused
  - patch_paged_attention_v2.py → was unused
  - patch_triton_tuning.py → was unused
  - patch_vectorized_decode.py → was unused

Remaining: patch_transformers_qwen3_5.py (1 script, unavoidable — modifies
pip-installed transformers which is version-specific)

Architecture: 13 blind string-replace scripts → 0. All base modifications
are now full-file replacements with complete source context.
This commit is contained in:
dylanyunlon
2026-08-05 08:39:54 +00:00
parent b902090fb2
commit 821c59500d
13 changed files with 0 additions and 2154 deletions

View File

@@ -1,195 +0,0 @@
"""
patch_enable_triton.py — Enable Triton kernels on BI-V100 with safety fallback
================================================================================
The baseline disables Triton entirely (HAS_TRITON = False) because the default
kernel configuration hangs BI-V100. But Triton 2.3.1 IS installed in the image.
Strategy:
1. Set HAS_TRITON = True so prefix_prefill.py is imported
2. Patch prefix_prefill.py with conservative tile sizes (BLOCK=64, NUM_WARPS=4)
3. Add a timeout-protected first-call test in forward_prefix:
- Try Triton kernel with 1-second timeout
- If it hangs or errors, permanently fall back to PyTorch path
- Log the result so we know which path is active
This is the key performance unlock:
PyTorch fallback: Python for-loop, ~20 tokens/sec on prefill
Triton kernel: GPU-parallel Flash Attention, potentially 10-50x faster
Risk mitigation:
- If Triton still hangs at BLOCK=64/NUM_WARPS=4, the timeout catches it
- All functional tests still pass (same math, different implementation)
- The fallback is the exact same _forward_prefix_pytorch from baseline
Deploy: python3 qwen3_6_scripts/patch_enable_triton.py
Must run AFTER patch_ops.sh (which deploys paged_attn.py)
Must run AFTER patch_triton_tuning.py (which sets BLOCK=64, NUM_WARPS=4)
"""
import os
# --- 1. Enable HAS_TRITON ---
TRITON_IMPORT_PATH = "/usr/local/corex/lib/python3/dist-packages/vllm/triton_utils/importing.py"
TRITON_IMPORT_PATHS = [
TRITON_IMPORT_PATH,
"/usr/local/corex/lib64/python3/dist-packages/vllm/triton_utils/importing.py",
]
OLD_TRITON = "HAS_TRITON = False"
NEW_TRITON = """\
# BI-V100: Triton 2.3.1 is present. Enable it with conservative tile sizes.
# If Triton kernels hang, the timeout in paged_attn.py will catch it.
try:
import triton
HAS_TRITON = True
except ImportError:
HAS_TRITON = False"""
def patch_triton_import():
for path in TRITON_IMPORT_PATHS:
if not os.path.exists(path):
continue
with open(path, "r") as f:
content = f.read()
if "HAS_TRITON = True" in content:
print(f" [skip] {path}: HAS_TRITON already True")
return True
if OLD_TRITON in content:
content = content.replace(OLD_TRITON, NEW_TRITON, 1)
with open(path, "w") as f:
f.write(content)
print(f" [ok] {path}: HAS_TRITON = False → True (with import guard)")
return True
print(" [error] importing.py not found")
return False
# --- 2. Patch paged_attn.py forward_prefix to try Triton with fallback ---
PAGED_ATTN_PATH = "/usr/local/corex/lib/python3/dist-packages/vllm/attention/ops/paged_attn.py"
# The patched paged_attn.py (from patch_ops.sh) has:
# def forward_prefix(...):
# return PagedAttention._forward_prefix_pytorch(...)
#
# We replace it with a try-Triton-first version:
OLD_FORWARD_PREFIX = """\
@staticmethod
def forward_prefix(
query: torch.Tensor,
key: torch.Tensor,
value: torch.Tensor,
kv_cache_dtype: str,
key_cache: torch.Tensor,
value_cache: torch.Tensor,
block_tables: torch.Tensor,
query_start_loc: torch.Tensor,
seq_lens_tensor: torch.Tensor,
context_lens: torch.Tensor,
max_query_len: int,
alibi_slopes: Optional[torch.Tensor],
sliding_window: Optional[int],
k_scale: float,
v_scale: float,
) -> torch.Tensor:
# NOTE: The Triton context_attention_fwd kernel hangs on Iluvatar
# BI-V100 hardware (same class of issue as cudnnFlashAttnForward).
# Use a pure-PyTorch fallback that reads the paged KV cache directly.
return PagedAttention._forward_prefix_pytorch(
query, key, value,
key_cache, value_cache,
block_tables, query_start_loc,
seq_lens_tensor, context_lens,
)"""
NEW_FORWARD_PREFIX = """\
# Triton prefill: try once, fall back permanently if it fails
_triton_prefill_ok = None # None=untested, True=works, False=failed
@staticmethod
def forward_prefix(
query: torch.Tensor,
key: torch.Tensor,
value: torch.Tensor,
kv_cache_dtype: str,
key_cache: torch.Tensor,
value_cache: torch.Tensor,
block_tables: torch.Tensor,
query_start_loc: torch.Tensor,
seq_lens_tensor: torch.Tensor,
context_lens: torch.Tensor,
max_query_len: int,
alibi_slopes: Optional[torch.Tensor],
sliding_window: Optional[int],
k_scale: float,
v_scale: float,
) -> torch.Tensor:
# Try Triton kernel if available and not known to fail
if PagedAttention._triton_prefill_ok is not False:
try:
from vllm.triton_utils import HAS_TRITON
if HAS_TRITON:
from vllm.attention.ops.prefix_prefill import context_attention_fwd
output = torch.empty_like(query)
context_attention_fwd(
query, key, value, output, kv_cache_dtype,
key_cache, value_cache, block_tables,
query_start_loc[:-1], seq_lens_tensor, context_lens,
max_query_len, k_scale, v_scale,
alibi_slopes, sliding_window,
)
if PagedAttention._triton_prefill_ok is None:
print("[paged_attn] Triton prefill kernel: SUCCESS", flush=True)
PagedAttention._triton_prefill_ok = True
return output
except Exception as e:
print(f"[paged_attn] Triton prefill failed: {type(e).__name__}: {e}",
flush=True)
print("[paged_attn] Falling back to PyTorch prefill permanently", flush=True)
PagedAttention._triton_prefill_ok = False
# PyTorch fallback (same as baseline)
return PagedAttention._forward_prefix_pytorch(
query, key, value,
key_cache, value_cache,
block_tables, query_start_loc,
seq_lens_tensor, context_lens,
)"""
def patch_paged_attn():
if not os.path.exists(PAGED_ATTN_PATH):
print(f" [error] {PAGED_ATTN_PATH} not found")
return False
with open(PAGED_ATTN_PATH, "r") as f:
content = f.read()
if "_triton_prefill_ok" in content:
print(f" [skip] {PAGED_ATTN_PATH}: already has Triton try/fallback")
return True
if OLD_FORWARD_PREFIX in content:
content = content.replace(OLD_FORWARD_PREFIX, NEW_FORWARD_PREFIX, 1)
with open(PAGED_ATTN_PATH, "w") as f:
f.write(content)
print(f" [ok] {PAGED_ATTN_PATH}: added Triton try/fallback in forward_prefix")
return True
print(f" [warn] {PAGED_ATTN_PATH}: forward_prefix anchor not found")
return False
def main():
print("=== patch_enable_triton: Enable Triton with safety fallback ===")
print("\n--- Step 1: Enable HAS_TRITON ---")
patch_triton_import()
print("\n--- Step 2: Triton try/fallback in forward_prefix ---")
patch_paged_attn()
print("\nDone. On first prefill request:")
print(" - If Triton works at BLOCK=64/NUM_WARPS=4 → 10-50x prefill speedup")
print(" - If Triton hangs/errors → auto-fallback to PyTorch (same as baseline)")
if __name__ == "__main__":
main()

View File

@@ -1,182 +0,0 @@
"""
patch_head256_triton.py — Enable Triton prefill for head_dim=256
=================================================================
Qwen3.6-35B-A3B uses head_dim=256 (confirmed: text_cfg.head_dim=256,
num_heads=24, num_kv_heads=4, GQA ratio=6).
Current problem:
prefix_prefill.py: BLOCK = 64 (set by patch_triton_tuning.py)
SMEM needed: BLOCK_N × head_dim × sizeof(fp16) × 2 (K+V tiles)
= 64 × 256 × 2 × 2 = 64KB > 48KB (BI-V100 SMEM limit)
→ Triton kernel CANNOT launch at BLOCK=64 for head_dim=256.
xformers.py: _run_sdpa_fallback triggers for head_size > 128.
This is a Python for-loop with Q-tiling — orders of magnitude slower.
Fix:
1. In prefix_prefill.py launcher, use BLOCK based on head_dim:
head_dim ≤ 128: BLOCK = 64, NUM_WARPS = 4 (32KB SMEM, fits)
head_dim = 256: BLOCK = 32, NUM_WARPS = 4 (32KB SMEM, fits)
head_dim > 256: BLOCK = 16, NUM_WARPS = 2 (16KB SMEM, fits)
2. Triton BLOCK=32 means more kernel launches per sequence but
each launch uses only 32KB SMEM — well within 48KB limit.
32×256×2×2 = 32KB ≤ 48KB ✓
3. Also optimize _run_sdpa_fallback _Q_CHUNK:
Current: 256 (same for all head dims)
For head_dim=256: memory = _Q_CHUNK × seq_len × H × 4 bytes (float32)
At Q_CHUNK=256, seq_len=100K, H=24: 256×100K×24×4 ≈ 2.3GB
Better: _Q_CHUNK=128 for head_dim=256 → 1.2GB (safer for OOM)
Deploy: python3 qwen3_6_scripts/patch_head256_triton.py
Must run AFTER patch_ops.sh and patch_triton_tuning.py
"""
import os
PREFIX_PREFILL_PATHS = [
"/usr/local/corex/lib/python3/dist-packages/vllm/attention/ops/prefix_prefill.py",
"/usr/local/corex/lib64/python3/dist-packages/vllm/attention/ops/prefix_prefill.py",
]
XFORMERS_PATHS = [
"/usr/local/corex/lib/python3/dist-packages/vllm/attention/backends/xformers.py",
"/usr/local/corex/lib64/python3/dist-packages/vllm/attention/backends/xformers.py",
]
# --- Patch 1: prefix_prefill.py BLOCK selection based on head_dim ---
# The current patch_triton_tuning.py sets:
# BLOCK = 64
# NUM_WARPS = 4
# We need to make BLOCK depend on head_dim:
OLD_BLOCK_SETTING = """\
# BI-V100 optimization (patch_triton_tuning.py):
# BLOCK=64: SMEM constrains BLOCK_N≤64 for head_dim=128
# NUM_WARPS=4: fewer warps → more blocks/SM → better occupancy
BLOCK = 64
NUM_WARPS = 4"""
NEW_BLOCK_SETTING = """\
# BI-V100: BLOCK must fit in 48KB SMEM.
# SMEM = BLOCK_N × head_dim × sizeof(fp16) × 2 (K+V tiles)
# head_dim=128: BLOCK=64 → 64×128×2×2 = 32KB ✓
# head_dim=256: BLOCK=32 → 32×256×2×2 = 32KB ✓ (BLOCK=64 → 64KB overflow!)
# head_dim>256: BLOCK=16 → fallback
Lk = q.shape[-1]
if Lk <= 128:
BLOCK = 64
NUM_WARPS = 4
elif Lk <= 256:
BLOCK = 32
NUM_WARPS = 4
else:
BLOCK = 16
NUM_WARPS = 2"""
# Alternative: if patch_triton_tuning.py hasn't run yet, patch the original
OLD_BLOCK_ORIGINAL = """\
BLOCK = 128 if current_platform.has_device_capability(80) else 64
NUM_WARPS = 8"""
NEW_BLOCK_FROM_ORIGINAL = NEW_BLOCK_SETTING
# --- Patch 2: _run_sdpa_fallback Q_CHUNK for head_dim=256 ---
OLD_Q_CHUNK = " _Q_CHUNK = 256"
NEW_Q_CHUNK = """\
# Adapt Q chunk size to head_dim to control memory:
# head_dim=128: 256 × seq_len × H × 4B → manageable
# head_dim=256: halve to 128 to avoid OOM on long sequences
_Q_CHUNK = 128 if self.head_size > 128 else 256"""
def patch_prefix_prefill():
for path in PREFIX_PREFILL_PATHS:
if not os.path.exists(path):
continue
with open(path, "r") as f:
content = f.read()
changed = False
if "head_dim=256: BLOCK=32" in content:
print(f" [skip] {path}: head_dim-aware BLOCK already present")
return True
if OLD_BLOCK_SETTING in content:
content = content.replace(OLD_BLOCK_SETTING, NEW_BLOCK_SETTING, 1)
changed = True
print(f" [ok] Replaced fixed BLOCK=64 with head_dim-dependent selection")
elif OLD_BLOCK_ORIGINAL in content:
content = content.replace(OLD_BLOCK_ORIGINAL, NEW_BLOCK_FROM_ORIGINAL, 1)
changed = True
print(f" [ok] Replaced original BLOCK selection with head_dim-dependent version")
else:
print(f" [warn] Neither BLOCK anchor found in {path}")
# Also need to move Lk computation before BLOCK selection
# Currently Lk is computed AFTER BLOCK is set (line ~750)
# We need it before. Check if Lk is already available:
if "Lk = q.shape[-1]" in content and "Lk, Lk, Lv" in content:
# Lk is computed later — we duplicate the computation for BLOCK selection
# This is safe because q.shape[-1] doesn't change
print(f" [note] Lk computed early for BLOCK selection + later for kernel args")
if changed:
with open(path, "w") as f:
f.write(content)
print(f" Written: {path}")
return changed
print(" [error] prefix_prefill.py not found")
return False
def patch_xformers_sdpa():
for path in XFORMERS_PATHS:
if not os.path.exists(path):
continue
with open(path, "r") as f:
content = f.read()
if "head_size > 128 else 256" in content:
print(f" [skip] {path}: adaptive Q_CHUNK already present")
return True
if OLD_Q_CHUNK in content:
content = content.replace(OLD_Q_CHUNK, NEW_Q_CHUNK, 1)
with open(path, "w") as f:
f.write(content)
print(f" [ok] {path}: _Q_CHUNK now adapts to head_dim")
return True
else:
print(f" [warn] _Q_CHUNK anchor not found in {path}")
print(" [error] xformers.py not found")
return False
def main():
print("=== patch_head256_triton: Enable Triton for head_dim=256 ===")
print(f" Qwen3.6: head_dim=256, num_heads=24, num_kv_heads=4, GQA=6")
print()
print("--- Patch 1: prefix_prefill.py BLOCK selection ---")
patch_prefix_prefill()
print("\n--- Patch 2: xformers Q_CHUNK for head_dim=256 ---")
patch_xformers_sdpa()
print("\nSMEM budget at BLOCK=32, head_dim=256:")
print(f" K tile: 32 × 256 × 2B = 16KB")
print(f" V tile: 32 × 256 × 2B = 16KB")
print(f" Total: 32KB ≤ 48KB ✓")
print("\nDone.")
if __name__ == "__main__":
main()

View File

@@ -1,304 +0,0 @@
"""
patch_ixformer_native.py — Enable ixformer native kernels on BI-V100
=====================================================================
Hardware-verified fixes (2026-07-31):
1. V1 paged_attention: head_mapping must be Tensor, not int.
Verified: V1 with Tensor head_mapping matches manual attention (diff < 0.001).
Performance: 0.034ms (256 tok), 0.272ms (8192 tok).
2. V2 paged_attention: native kernel EXISTS (vllm_single_query_cached_kv_attention_v2)
but produces INCORRECT output (diff=1.28 vs V1, norm mismatch).
The native V2 kernel expects different cache layout [B,H,bs,d] and even with
correct conversion, the output doesn't match V1 on the same data.
STATUS: Keep Python V2 fallback (paged_attention_v2_pytorch.py) for seq > 8192.
TODO: Investigate V2 native kernel parameter semantics.
3. flash_attn_func: WORKS with head_dim=256, GQA.
ixf_F.flash_attn_func(q, k, v, causal=True) produces correct output.
This should replace the Python _run_sdpa_fallback for prefill.
4. Triton: installed but vllm can't find it (path mismatch).
Fix: symlink + sys.path insertion.
"""
import os
import sys
import shutil
VLLM_ROOT = "/usr/local/corex/lib64/python3/dist-packages/vllm"
CUSTOM_OPS_PATH = os.path.join(VLLM_ROOT, "_custom_ops.py")
def patch_v1_head_mapping():
"""Fix V1: convert head_mapping from int to Tensor."""
with open(CUSTOM_OPS_PATH, "r") as f:
content = f.read()
old_v1 = '''def paged_attention_v1(
output,
query,
key_cache,
value_cache,
head_mapping,
scale,
block_tables,
context_lens,
block_size,
max_context_len,
alibi_slopes=None,
kv_cache_dtype=None,
):
return ixf_F.vllm_single_query_cached_kv_attention(
output,
query,
key_cache,
value_cache,
head_mapping,
scale,
block_tables,
context_lens,
block_size,
max_context_len,
alibi_slopes,
)'''
new_v1 = '''def paged_attention_v1(
output,
query,
key_cache,
value_cache,
head_mapping,
scale,
block_tables,
context_lens,
block_size,
max_context_len,
alibi_slopes=None,
kv_cache_dtype=None,
):
# BI-V100: ixformer requires head_mapping as Tensor, not int.
# Verified: V1 with Tensor matches manual attention (max diff < 0.001).
if isinstance(head_mapping, int):
num_kv_heads = head_mapping
num_heads = query.shape[1]
num_queries_per_kv = num_heads // num_kv_heads
head_mapping = torch.repeat_interleave(
torch.arange(num_kv_heads, dtype=torch.int32, device=query.device),
num_queries_per_kv)
return ixf_F.vllm_single_query_cached_kv_attention(
output,
query,
key_cache,
value_cache,
head_mapping,
scale,
block_tables,
context_lens,
block_size,
max_context_len,
alibi_slopes,
)'''
if "isinstance(head_mapping, int)" in content:
print(" [skip] V1 head_mapping fix already applied")
return True
if old_v1 in content:
content = content.replace(old_v1, new_v1, 1)
with open(CUSTOM_OPS_PATH, "w") as f:
f.write(content)
print(" [ok] V1: Added int→Tensor conversion for head_mapping")
return True
else:
print(" [warn] V1 function body not found — check manually")
return False
def patch_v2_python_fallback():
"""V2: Replace NotImplementedError with Python V2 fallback.
The native V2 kernel exists but produces incorrect output.
Use paged_attention_v2_pytorch.py instead.
"""
with open(CUSTOM_OPS_PATH, "r") as f:
content = f.read()
# Check if V2 is still NotImplementedError
if "raise NotImplementedError()" not in content:
print(" [skip] V2 NotImplementedError already replaced")
return True
# Add import for Python V2
import_line = "from vllm.paged_attention_v2_pytorch import paged_attention_v2_pytorch"
if import_line not in content:
anchor = "import ixformer.functions as ixf_F"
if anchor in content:
content = content.replace(anchor, anchor + "\n" + import_line, 1)
print(" [ok] Added Python V2 import")
# Replace NotImplementedError with Python V2 call
old_v2_end = """ blocksparse_block_size: int = 64,
blocksparse_head_sliding_step: int = 0,
) -> None:
raise NotImplementedError()"""
new_v2_end = """ blocksparse_block_size: int = 64,
blocksparse_head_sliding_step: int = 0,
) -> None:
# BI-V100: Native V2 kernel exists but has correctness issues.
# Using Python V2 (single-bmm + GQA broadcast) as fallback.
paged_attention_v2_pytorch(
out, exp_sum, max_logits, tmp_out,
query, key_cache, value_cache,
num_kv_heads, scale, block_tables, seq_lens,
block_size, max_seq_len, alibi_slopes,
kv_cache_dtype, k_scale, v_scale,
)"""
if old_v2_end in content:
content = content.replace(old_v2_end, new_v2_end, 1)
with open(CUSTOM_OPS_PATH, "w") as f:
f.write(content)
print(" [ok] V2: Replaced NotImplementedError with Python V2 fallback")
return True
else:
print(" [warn] V2 NotImplementedError block not found")
return False
def deploy_v2_module():
"""Copy Python V2 module into vllm package."""
src = "/workspace/paged_attention_v2_pytorch.py"
dst = os.path.join(VLLM_ROOT, "paged_attention_v2_pytorch.py")
if os.path.exists(dst):
print(f" [skip] {dst} already exists")
return True
if os.path.exists(src):
shutil.copy2(src, dst)
print(f" [ok] Copied paged_attention_v2_pytorch.py → vllm/")
return True
else:
print(f" [warn] {src} not found — V2 fallback won't work")
return False
def patch_triton_path():
"""Fix Triton import path."""
triton_src = "/usr/local/lib/python3.10/site-packages/triton"
triton_dst = "/usr/local/corex/lib64/python3/dist-packages/triton"
if os.path.exists(triton_src) and not os.path.exists(triton_dst):
try:
os.symlink(triton_src, triton_dst)
print(f" [ok] Symlinked triton → corex dist-packages")
except Exception as e:
print(f" [warn] Symlink failed: {e}")
else:
print(" [skip] Triton symlink already exists or source not found")
# Also symlink triton's dependencies
for dep in ["triton"]:
src = f"/usr/local/lib/python3.10/site-packages/{dep}"
dst = f"/usr/local/corex/lib64/python3/dist-packages/{dep}"
if os.path.exists(src) and not os.path.exists(dst):
try:
os.symlink(src, dst)
except:
pass
def patch_flash_attn_prefill():
"""Enable ixformer flash_attn for prefill instead of Python fallback.
The xformers backend's _run_sdpa_fallback is used when head_dim > 128.
With ixformer.flash_attn_func confirmed working at head_dim=256,
we can replace the fallback with a call to the native kernel.
"""
xformers_path = os.path.join(VLLM_ROOT, "attention/backends/xformers.py")
if not os.path.exists(xformers_path):
print(" [warn] xformers.py not found")
return False
with open(xformers_path, "r") as f:
content = f.read()
if "ixf_F.flash_attn_func" in content:
print(" [skip] flash_attn already patched into xformers.py")
return True
# Find the _run_sdpa_fallback method and add flash_attn as first attempt
marker = "def _run_sdpa_fallback"
if marker not in content:
print(" [warn] _run_sdpa_fallback not found in xformers.py")
return False
# Add import at top
if "import ixformer.functions as ixf_F" not in content:
content = "import ixformer.functions as ixf_F\n" + content
# Insert flash_attn attempt at the start of _run_sdpa_fallback
old_def = " def _run_sdpa_fallback("
new_def = """ def _run_sdpa_flash_attn(
self,
output: torch.Tensor,
query: torch.Tensor,
key: torch.Tensor,
value: torch.Tensor,
seq_lens: List[int],
is_prefill: bool,
) -> torch.Tensor:
\"\"\"Try ixformer flash_attn_func first (native kernel, head_dim=256 OK).\"\"\"
try:
# flash_attn expects [batch, seqlen, nheads, headdim]
# Our inputs are [num_tokens, num_heads, head_size]
# Need to reshape per sequence
if is_prefill and len(seq_lens) == 1:
sq = seq_lens[0]
q = query[:sq].unsqueeze(0).transpose(1, 2) # [1, sq, H, d]
# Wait — flash_attn expects [B, S, H, D] not [B, H, S, D]
# query is [num_tokens, num_heads, head_size]
q = query[:sq].unsqueeze(0) # [1, sq, H, d]
k = key[:sq].unsqueeze(0) # [1, sq, kv_H, d]
v = value[:sq].unsqueeze(0) # [1, sq, kv_H, d]
out = ixf_F.flash_attn_func(q, k, v, causal=True)
output[:sq] = out.squeeze(0)
return output
except Exception:
pass
return self._run_sdpa_fallback(output, query, key, value, seq_lens, is_prefill)
def _run_sdpa_fallback("""
content = content.replace(old_def, new_def, 1)
with open(xformers_path, "w") as f:
f.write(content)
print(" [ok] Added flash_attn prefill path before _run_sdpa_fallback")
return True
def main():
print("=== patch_ixformer_native: Hardware-verified kernel fixes ===\n")
print("--- 1. V1 head_mapping int→Tensor ---")
patch_v1_head_mapping()
print("\n--- 2. V2 Python fallback (native V2 has correctness issues) ---")
deploy_v2_module()
patch_v2_python_fallback()
print("\n--- 3. Triton path fix ---")
patch_triton_path()
print("\n--- 4. flash_attn for prefill ---")
patch_flash_attn_prefill()
print("\n=== Summary ===")
print(" V1 decode (seq ≤ 8192): ixformer native kernel ✓ (0.03-0.27ms)")
print(" V2 decode (seq > 8192): Python V2 fallback (native V2 incorrect)")
print(" Prefill: ixformer flash_attn_func ✓ (head_dim=256 confirmed)")
print(" Triton: symlinked for import resolution")
if __name__ == "__main__":
main()

View File

@@ -1,78 +0,0 @@
"""
Fix: prefix_cache_hit stays True for chunked-prefill chunk 2+ even when past cache.
Root cause:
model_runner.py _compute_for_prefix_cache_hit has three cases:
Case 1: prefix_cache_len <= context_len → "already past cache, do normal"
Case 2: context_len < prefix_cache_len < seq_len → partial hit, correct
Case 3: seq_len <= prefix_cache_len → full hit, reduce to 1 token
Case 1 does nothing (leaves prefix_cache_hit = True). Then in utils.py:
if inter_data.prefix_cache_hit:
block_table = computed_block_nums ← ONLY the original prefix blocks!
But context_len > prefix_cache_len means chunk 1 tokens (between prefix_cache_len
and context_len) are ALSO in KV cache and need to be in block_table.
block_table = computed_block_nums misses all chunk-1 blocks.
In _forward_prefix_pytorch:
num_ctx_blocks = ceil(context_len / block_size) # e.g. 268
block_tables.shape[1] = len(computed_block_nums) # e.g. 12 <-- too small!
At tile_blk >= 12: blk_ids is empty → k_t shape [..., 0] → amax crash.
Fix:
Set prefix_cache_hit = False for Case 1, so utils.py falls through to:
elif chunked_prefill_enabled:
block_table = block_tables[seq_id] ← full block table (prefix + chunk1)
"""
import re
import sys
CANDIDATE_PATHS = [
"/usr/local/corex/lib64/python3/dist-packages/vllm/worker/model_runner.py",
"/usr/local/corex/lib/python3/dist-packages/vllm/worker/model_runner.py",
]
OLD_BLOCK = """\
if prefix_cache_len <= context_len:
# We already passed the cache hit region,
# so do normal computation.
pass"""
NEW_BLOCK = """\
if prefix_cache_len <= context_len:
# We already passed the cache hit region,
# so do normal computation.
# Must clear prefix_cache_hit so _add_seq_group uses the full
# block_tables (prefix + previous-chunk blocks) instead of only
# computed_block_nums (prefix only). Without this, block_tables
# passed to _forward_prefix_pytorch is too narrow for context_len,
# causing an empty blk_ids slice and a zero-dim amax() crash.
inter_data.prefix_cache_hit = False"""
import os
patched = False
for path in CANDIDATE_PATHS:
if not os.path.exists(path):
continue
with open(path, "r") as f:
src = f.read()
if OLD_BLOCK not in src:
if NEW_BLOCK in src:
print(f"[patch_model_runner] already patched: {path}")
patched = True
break
print(f"[patch_model_runner] WARNING: expected block not found in {path}, skipping")
continue
patched_src = src.replace(OLD_BLOCK, NEW_BLOCK, 1)
with open(path, "w") as f:
f.write(patched_src)
print(f"[patch_model_runner] patched Case-1 prefix_cache_hit fix in: {path}")
patched = True
break
if not patched:
print("[patch_model_runner] ERROR: could not find model_runner.py at any known path", file=sys.stderr)
sys.exit(1)

View File

@@ -1,192 +0,0 @@
"""
patch_paged_attention_v2.py — Enable PagedAttention V2 on BI-V100
==================================================================
The baseline has paged_attention_v2 = raise NotImplementedError().
paged_attn.py hardcodes use_v1=True to avoid calling it.
This patch:
1. Copies paged_attention_v2_pytorch.py into the vllm package
2. Patches _custom_ops.py to call the PyTorch V2 implementation
3. Patches paged_attn.py to enable V2 for long sequences (>8192 tokens)
Performance impact:
V1 processes the entire KV sequence in a single kernel launch per (seq, head).
When seq_len > 8192, the single-block V1 kernel is memory-bandwidth-limited.
V2 splits the sequence into PARTITION_SIZE=512 chunks, processes them in
parallel, then reduces. For seq_len=100K: 195 parallel partitions vs 1.
Expected improvement: 30-50% on Output TPS for long-context decode.
This matches the competition's advanced (30%) and special (50%) award tiers.
Deploy:
cp paged_attention_v2_pytorch.py /usr/local/corex/lib/python3/dist-packages/vllm/
python3 qwen3_6_scripts/patch_paged_attention_v2.py
"""
import os
import shutil
VLLM_ROOTS = [
"/usr/local/corex/lib/python3/dist-packages/vllm",
"/usr/local/corex/lib64/python3/dist-packages/vllm",
]
V2_MODULE_PYTORCH = "paged_attention_v2_pytorch.py"
V2_MODULE_TRITON = "paged_attention_v2_triton.py"
def find_vllm_root():
for root in VLLM_ROOTS:
if os.path.exists(os.path.join(root, "_custom_ops.py")):
return root
return None
def patch_custom_ops(vllm_root):
"""Replace paged_attention_v2 NotImplementedError with PyTorch implementation."""
path = os.path.join(vllm_root, "_custom_ops.py")
with open(path, "r") as f:
content = f.read()
# Add import at the top (after existing imports)
import_line = "# Try Triton V2 (single-launch, GPU-parallel) first; PyTorch V2 as fallback
try:
from vllm.paged_attention_v2_triton import paged_attention_v2_triton as _v2_impl
_V2_BACKEND = "triton"
except Exception:
from vllm.paged_attention_v2_pytorch import paged_attention_v2_pytorch as _v2_impl
_V2_BACKEND = "pytorch"
import logging
logging.getLogger("vllm").info(f"PagedAttention V2 backend: {_V2_BACKEND}")"
if import_line in content:
print(" [skip] V2 import already present")
else:
# Insert after the last import line
anchor = "from vllm.platforms import current_platform"
if anchor in content:
content = content.replace(
anchor,
anchor + "\n" + import_line,
1
)
print(" [ok] Added V2 import")
else:
print(" [warn] Import anchor not found")
return False
# Replace the NotImplementedError body
old_v2 = ''' blocksparse_block_size: int = 64,
blocksparse_head_sliding_step: int = 0,
) -> None:
raise NotImplementedError()'''
new_v2 = ''' blocksparse_block_size: int = 64,
blocksparse_head_sliding_step: int = 0,
) -> None:
# BI-V100: PyTorch V2 implementation (replaces NotImplementedError)
_v2_impl(
out, exp_sum, max_logits, tmp_out,
query, key_cache, value_cache,
num_kv_heads, scale, block_tables, seq_lens,
block_size, max_seq_len, alibi_slopes,
kv_cache_dtype, k_scale, v_scale,
tp_rank, blocksparse_local_blocks,
blocksparse_vert_stride, blocksparse_block_size,
blocksparse_head_sliding_step,
)'''
if "paged_attention_v2_pytorch(" in content:
print(" [skip] V2 body already patched")
elif old_v2 in content:
content = content.replace(old_v2, new_v2, 1)
print(" [ok] Replaced V2 NotImplementedError with PyTorch implementation")
else:
print(" [warn] V2 function body not found as expected")
return False
with open(path, "w") as f:
f.write(content)
print(f" Written: {path}")
return True
def patch_paged_attn(vllm_root):
"""Enable V2 for long sequences instead of forcing V1."""
path = os.path.join(vllm_root, "attention/ops/paged_attn.py")
with open(path, "r") as f:
content = f.read()
# The baseline has:
# use_v1 = (max_seq_len <= 8192 and ...)
# use_v1 = True # <-- hardcoded override
# We want to remove the hardcoded override so V2 is used for long sequences.
old_heuristic = " use_v1 = True"
new_heuristic = " # use_v1 = True # Removed: V2 now works on BI-V100 (paged_attention_v2_pytorch)"
if "V2 now works" in content:
print(" [skip] V1 override already removed")
elif old_heuristic in content:
content = content.replace(old_heuristic, new_heuristic, 1)
print(" [ok] Removed use_v1=True hardcode — V2 enabled for seq_len > 8192")
else:
print(" [warn] use_v1=True line not found")
return False
with open(path, "w") as f:
f.write(content)
print(f" Written: {path}")
return True
def deploy_v2_module(vllm_root):
"""Copy the V2 PyTorch module into the vllm package."""
src = os.path.join(os.path.dirname(__file__), "..", V2_MODULE_PYTORCH)
if not os.path.exists(src):
src = os.path.join("/workspace", V2_MODULE_PYTORCH)
if not os.path.exists(src):
# Try relative to this script
src = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", V2_MODULE_PYTORCH)
dst = os.path.join(vllm_root, V2_MODULE_PYTORCH)
if os.path.exists(dst):
print(f" [skip] V2 module {dst} already exists")
return True
if not os.path.exists(src):
print(f" [error] V2 module not found at {src}")
return False
shutil.copy2(src, dst)
print(f" [ok] Copied {V2_MODULE_PYTORCH}{dst}")
return True
def main():
print("=== patch_paged_attention_v2: Enable V2 on BI-V100 ===\n")
vllm_root = find_vllm_root()
if not vllm_root:
print("[error] vllm package not found")
return
print(f"vllm root: {vllm_root}\n")
print("Step 1: Deploy V2 PyTorch module")
deploy_v2_module(vllm_root)
print("\nStep 2: Patch _custom_ops.py")
patch_custom_ops(vllm_root)
print("\nStep 3: Patch paged_attn.py (enable V2 for long sequences)")
patch_paged_attn(vllm_root)
print("\nDone. V2 is now enabled for seq_len > 8192.")
if __name__ == "__main__":
main()

View File

@@ -1,91 +0,0 @@
"""
patch_triton_tuning.py — BI-V100 Triton kernel parameter optimization
======================================================================
Patches prefix_prefill.py to use BI-V100-optimal BLOCK and NUM_WARPS values.
Hardware derivation:
BI-V100 SMEM = 48KB. Triton Flash Attention needs K+V tiles in SMEM:
SMEM = BLOCK_N × head_dim × sizeof(fp16) × 2
BLOCK_N=64, head_dim=128 → 32KB ≤ 48KB ✓ (current, correct)
BLOCK_N=128, head_dim=128 → 64KB > 48KB ✗ (would crash)
→ BLOCK must stay at 64.
NUM_WARPS derivation:
At BLOCK=64, each block does 64 query positions.
8 warps = 256 threads → each thread handles 32 elements from Q tile.
4 warps = 128 threads → each thread handles 64 elements.
With 16 SMs (confirmed) and typical grid of 37K+ blocks:
At 8 warps + 32KB SMEM: 1 block per SM (SMEM-limited)
At 4 warps + 32KB SMEM: potentially 2 blocks per SM
BI-V100 is bandwidth-limited (900 GB/s), not latency-limited.
Fewer warps hiding latency matters less; more blocks = better.
→ NUM_WARPS = 4
Deploy: python3 qwen3_6_scripts/patch_triton_tuning.py
"""
import os
PREFIX_PREFILL_PATHS = [
"/usr/local/corex/lib/python3/dist-packages/vllm/attention/ops/prefix_prefill.py",
"/usr/local/corex/lib64/python3/dist-packages/vllm/attention/ops/prefix_prefill.py",
]
# Original line (baseline):
OLD_BLOCK = " BLOCK = 128 if current_platform.has_device_capability(80) else 64\n NUM_WARPS = 8"
# Optimized for BI-V100:
NEW_BLOCK = """\
# BI-V100 optimization (patch_triton_tuning.py):
# BLOCK=64: SMEM constraint — BLOCK_N=128 overflows 48KB at head_dim=128
# NUM_WARPS=4: bandwidth-limited GPU benefits from more blocks/SM over more warps
# Derivation: 4 warps at BLOCK=64 allows 2 concurrent blocks per SM,
# doubling occupancy vs 8 warps (which is SMEM-limited to 1 block/SM).
BLOCK = 64
NUM_WARPS = 4"""
def patch():
for path in PREFIX_PREFILL_PATHS:
if not os.path.exists(path):
continue
with open(path, "r") as f:
content = f.read()
if "NUM_WARPS = 4" in content:
print(f" [skip] {path}: already patched")
return
if OLD_BLOCK not in content:
# Try the alternative: maybe it's already using BLOCK=64 hardcoded
alt_old = " BLOCK = 64\n NUM_WARPS = 8"
if alt_old in content:
content = content.replace(alt_old, NEW_BLOCK, 1)
with open(path, "w") as f:
f.write(content)
print(f" [ok] {path}: patched NUM_WARPS 8→4 (BLOCK was already 64)")
return
print(f" [warn] {path}: original block not found, manual check needed")
return
content = content.replace(OLD_BLOCK, NEW_BLOCK, 1)
with open(path, "w") as f:
f.write(content)
print(f" [ok] {path}: patched BLOCK=64, NUM_WARPS=4")
return
print(" [error] prefix_prefill.py not found at any expected path")
def main():
print("=== patch_triton_tuning: BI-V100 Triton kernel optimization ===")
patch()
print("Done.")
if __name__ == "__main__":
main()

View File

@@ -1,72 +0,0 @@
"""
patch_vectorized_decode.py — Vectorize the decode PyTorch fallback
===================================================================
Current _forward_decode_pytorch (used when seq_len > 32768):
for i in range(num_seqs): ← Python for-loop
k_t = key_cache[blk_ids]... ← per-sequence gather
attn_w = torch.matmul(q, k_t) ← per-sequence matmul
output[i] = ...
Problem: When num_seqs=1 (competition config), this loop runs once.
But the inner operations do seq_len worth of gather+matmul in Python.
The real bottleneck is the .permute().contiguous().view() chain on K/V,
which creates multiple intermediate tensors.
Optimization: Fuse the gather and reduce steps:
1. Use torch.index_select instead of fancy indexing for K/V gather
2. Pre-compute the scale factor into Q
3. Avoid the .float() → .to(orig_dtype) round-trip where possible
4. Use torch.baddbmm for fused scale+matmul
This won't change the asymptotic complexity, but reduces Python overhead
and intermediate tensor allocations. The real fix is making paged_attention_v1
work at seq_len > 32768 (raise the threshold or fix the kernel).
Deploy: python3 qwen3_6_scripts/patch_vectorized_decode.py
"""
import os
PAGED_ATTN_PATH = "/usr/local/corex/lib/python3/dist-packages/vllm/attention/ops/paged_attn.py"
# Raise the threshold: try letting ixf_F.paged_attention_v1 handle longer sequences
# The baseline sets it to 32768 because v1 "fails for long contexts"
# But this might be a conservative limit — let's try 65536 first
# If it crashes, the user can lower it back
OLD_THRESHOLD = " _PYTORCH_DECODE_THRESHOLD = 32768"
NEW_THRESHOLD = """\
# BI-V100: Try higher threshold for compiled v1 kernel.
# Baseline: 32768 (conservative). We try 65536 — the v1 kernel is
# orders of magnitude faster than the Python fallback.
# If v1 crashes at higher seq_lens, lower this back to 32768.
_PYTORCH_DECODE_THRESHOLD = 65536"""
def patch():
if not os.path.exists(PAGED_ATTN_PATH):
print(f" [error] {PAGED_ATTN_PATH} not found")
return
with open(PAGED_ATTN_PATH, "r") as f:
content = f.read()
if "PYTORCH_DECODE_THRESHOLD = 65536" in content:
print(f" [skip] already patched to 65536")
return
if OLD_THRESHOLD in content:
content = content.replace(OLD_THRESHOLD, NEW_THRESHOLD, 1)
with open(PAGED_ATTN_PATH, "w") as f:
f.write(content)
print(f" [ok] _PYTORCH_DECODE_THRESHOLD: 32768 → 65536")
else:
print(f" [warn] threshold anchor not found")
def main():
print("=== patch_vectorized_decode: raise decode threshold ===")
patch()
print("Done.")
if __name__ == "__main__":
main()

View File

@@ -1,76 +0,0 @@
"""
Patches the vLLM model registry and deploys the Qwen3_5 model file.
Deploy steps on the remote machine:
1. cp modified_scripts/qwen3_5.py \
/usr/local/corex/lib64/python3/dist-packages/vllm/model_executor/models/qwen3_5.py
2. python3 modified_scripts/patch_vllm_qwen3_5.py
Also edit your model config.json to set:
"architectures": ["Qwen3_5ForCausalLM"]
Target: vLLM at /usr/local/corex/lib64/python3/dist-packages/vllm/
"""
VLLM_ROOT = "/usr/local/corex/lib64/python3/dist-packages/vllm"
REGISTRY = f"{VLLM_ROOT}/model_executor/models/registry.py"
def patch_file(path, replacements):
with open(path, "r") as f:
content = f.read()
patched = False
for old, new in replacements:
if new in content:
print(f" [skip] already patched: {repr(new[:70])}")
continue
if old not in content:
print(f" [warn] anchor not found: {repr(old[:70])}")
continue
content = content.replace(old, new, 1)
patched = True
print(f" [ok] patched after: {repr(old[:70])}")
if patched:
with open(path, "w") as f:
f.write(content)
def main():
print(f"=== Patching {REGISTRY} ===")
patch_file(REGISTRY, [
(
' "Qwen3ForCausalLM": ("qwen3", "Qwen3ForCausalLM"),\n'
' "Qwen3MoeForCausalLM": ("qwen3_moe", "Qwen3MoeForCausalLM"),',
' "Qwen3ForCausalLM": ("qwen3", "Qwen3ForCausalLM"),\n'
' "Qwen3MoeForCausalLM": ("qwen3_moe", "Qwen3MoeForCausalLM"),\n'
' "Qwen3_5ForCausalLM": ("qwen3_5", "Qwen3_5ForCausalLM"),\n'
' "Qwen3_5MoeForCausalLM": ("qwen3_5", "Qwen3_5MoeForCausalLM"),',
),
])
print("\n=== Verification ===")
try:
import importlib.util
spec = importlib.util.spec_from_file_location(
"qwen3_5",
f"{VLLM_ROOT}/model_executor/models/qwen3_5.py",
)
mod = importlib.util.module_from_spec(spec)
# Quick check: does the class exist?
spec.loader.exec_module(mod)
cls = mod.Qwen3_5ForCausalLM
print(f" Qwen3_5ForCausalLM found: {cls}")
cls_moe = mod.Qwen3_5MoeForCausalLM
print(f" Qwen3_5MoeForCausalLM found: {cls_moe}")
except Exception as e:
print(f" [warn] verification failed (may be OK at runtime): {e}")
print("\nDone. Remember to:")
print(" 1. Set config.json 'architectures': ['Qwen3_5ForCausalLM'] or ['Qwen3_5MoEForCausalLM']")
print(" 2. Run patch_transformers_qwen3_5.py if not already done")
if __name__ == "__main__":
main()

View File

@@ -1,79 +0,0 @@
"""
Patches vLLM 0.6.3 to register Qwen3CoderToolParser under the name "qwen3_coder".
Deploy steps on the remote machine (already called by patch_ops.sh):
1. cp qwen3coder_tool_parser.py \
/usr/local/corex/lib/python3/dist-packages/vllm/entrypoints/openai/tool_parsers/
2. python3 patch_vllm_tool_parser.py
Usage after patching:
--tool-call-parser qwen3_coder --enable-auto-tool-choice
"""
import os
VLLM_ROOT = "/usr/local/corex/lib/python3/dist-packages/vllm"
TOOL_PARSERS_DIR = f"{VLLM_ROOT}/entrypoints/openai/tool_parsers"
INIT_FILE = f"{TOOL_PARSERS_DIR}/__init__.py"
def patch_file(path, replacements):
with open(path, "r") as f:
content = f.read()
patched = False
for old, new in replacements:
if new in content:
print(f" [skip] already patched: {repr(new[:70])}")
continue
if old not in content:
print(f" [warn] anchor not found: {repr(old[:70])}")
continue
content = content.replace(old, new, 1)
patched = True
print(f" [ok] patched: {repr(old[:50])} -> {repr(new[:50])}")
if patched:
with open(path, "w") as f:
f.write(content)
def main():
if not os.path.isdir(TOOL_PARSERS_DIR):
raise FileNotFoundError(
f"Tool parsers directory not found: {TOOL_PARSERS_DIR}\n"
"Verify the vLLM installation path.")
print(f"=== Patching {INIT_FILE} ===")
patch_file(INIT_FILE, [
(
"from .mistral_tool_parser import MistralToolParser",
"from .mistral_tool_parser import MistralToolParser\n"
"from .qwen3coder_tool_parser import Qwen3CoderToolParser",
),
(
'"MistralToolParser", "Internlm2ToolParser", "Llama3JsonToolParser"\n]',
'"MistralToolParser", "Internlm2ToolParser", "Llama3JsonToolParser",\n'
' "Qwen3CoderToolParser"\n]',
),
])
print("\n=== Verification ===")
try:
import importlib.util
spec = importlib.util.spec_from_file_location(
"qwen3coder_tool_parser",
f"{TOOL_PARSERS_DIR}/qwen3coder_tool_parser.py",
)
mod = importlib.util.module_from_spec(spec)
print(f" Module spec loaded: {spec.name}")
print(" (full import requires torch/vllm runtime — skipping exec)")
except Exception as e:
print(f" [warn] spec check failed: {e}")
print("\nDone. Start vLLM server with:")
print(" --tool-call-parser qwen3_coder --enable-auto-tool-choice")
if __name__ == "__main__":
main()

View File

@@ -1,192 +0,0 @@
"""
策略批量block-diagonalfallback — 纯 PyTorch 数学实现
=============================================================
构建块对角 causal mask对整批序列一次 matmul + softmax
完全绕开所有硬件 flash attention kernel。
背景:
ixformer flshattF: head_dim > 128 报错拒绝
cudnnFlashAttnForward: 接受 head_dim=256但数值结果错误输出全"!"
两者大概率是同一硬件单元ixformer 提前拦截了硬件不支持的配置。
纯 matmul 路径完全绕开硬件 flash attention数值正确。
优点:
数值正确。
并发请求 prefill attention 在 GPU 上真正并行(一次大 matmul
缺点:
峰值显存 = total_tokens² × H × dtype_size
total_tokens 受 --max-num-batched-tokens 控制max-model-len 控制不住。
内存参考fp16H_local=6--max-num-batched-tokens=T
T=2048 → 峰值 ~50 MB
T=4096 → 峰值 ~200 MB
T=8192 → 峰值 ~800 MB
T=16384 → 峰值 ~3.2 GB
Deploy:
python3 modified_scripts/patch_xformers_sdpa_batch.py
"""
XFORMERS_PATH = (
"/usr/local/corex/lib64/python3/dist-packages/"
"vllm/attention/backends/xformers.py"
)
FALLBACK_METHOD = '''
def _run_sdpa_fallback(
self,
query: torch.Tensor,
key: torch.Tensor,
value: torch.Tensor,
attn_metadata: "XFormersMetadata",
) -> torch.Tensor:
"""批量纯数学 attention fallback。
构建块对角 causal mask等价于 ixformer BlockDiagonalCausalMask
对整批序列一次 matmul + softmaxGPU 并行处理所有序列。
块对角 mask 结构seq1 len=3seq2 len=2
s1,0 s1,1 s1,2 s2,0 s2,1
s1,0 [ 0 -inf -inf -inf -inf ]
s1,1 [ 0 0 -inf -inf -inf ]
s1,2 [ 0 0 0 -inf -inf ]
s2,0 [-inf -inf -inf 0 -inf ]
s2,1 [-inf -inf -inf 0 0 ]
softmax 在 float32 下计算防止 float16 溢出,结果转回原始 dtype。
Args:
query : [1, total_prefill_tokens, num_heads, head_dim]
key : [1, total_prefill_tokens, num_kv_heads, head_dim]
value : [1, total_prefill_tokens, num_kv_heads, head_dim]
Returns:
[1, total_prefill_tokens, num_heads, head_dim]
"""
assert attn_metadata.seq_lens is not None
orig_dtype = query.dtype
total_tokens = query.shape[1]
# ── 构建块对角 causal mask [T, T] ────────────────────────────────
# 全部初始化为 -inf再对每条序列的对角块填入下三角 0
mask = torch.full(
(total_tokens, total_tokens),
float("-inf"),
dtype=torch.float32,
device=query.device,
)
start = 0
for seq_len in attn_metadata.seq_lens:
end = start + seq_len
mask[start:end, start:end] = torch.tril(
torch.zeros(seq_len, seq_len,
dtype=torch.float32, device=query.device)
)
start = end
# ── [1, H, T, D].contiguous() ──────────────────────────────────
q_all = query.squeeze(0).permute(1, 0, 2).contiguous().unsqueeze(0)
k_all = key.squeeze(0).permute(1, 0, 2).contiguous().unsqueeze(0)
v_all = value.squeeze(0).permute(1, 0, 2).contiguous().unsqueeze(0)
# ── GQA展开 KV heads ────────────────────────────────────────────
if k_all.shape[1] != q_all.shape[1]:
n = q_all.shape[1] // k_all.shape[1]
k_all = k_all.repeat_interleave(n, dim=1).contiguous()
v_all = v_all.repeat_interleave(n, dim=1).contiguous()
# ── 纯数学 attentionfloat32 防溢出)────────────────────────────
# [1, H, T, T]
attn_w = torch.matmul(q_all.float(), k_all.float().transpose(-2, -1))
attn_w = attn_w * self.scale
attn_w = attn_w + mask # 加法广播mask [T,T] → [1, H, T, T]
attn_w = torch.softmax(attn_w, dim=-1)
out = torch.matmul(attn_w, v_all.float()).to(orig_dtype)
# [1, H, T, D] → [1, T, H, D]
return out.squeeze(0).permute(1, 0, 2).contiguous().unsqueeze(0)
'''
OLD_XFORMER_BLOCK = """\
self.attn_op = xops.fmha.flash.FwOp()
if self.alibi_slopes is None:
# Add the batch dimension.
query = query.unsqueeze(0)
key = key.unsqueeze(0)
value = value.unsqueeze(0)
out = xops.memory_efficient_attention_forward(
query,
key,
value,
attn_bias=attn_bias[0],
p=0.0,
scale=self.scale,
op = self.attn_op
)
return out.view_as(original_query)\
"""
NEW_XFORMER_BLOCK = """\
self.attn_op = xops.fmha.flash.FwOp()
if self.alibi_slopes is None:
# Add the batch dimension.
query = query.unsqueeze(0)
key = key.unsqueeze(0)
value = value.unsqueeze(0)
if self.head_size > 128:
out = self._run_sdpa_fallback(query, key, value, attn_metadata)
else:
out = xops.memory_efficient_attention_forward(
query,
key,
value,
attn_bias=attn_bias[0],
p=0.0,
scale=self.scale,
op=self.attn_op,
)
return out.view_as(original_query)\
"""
INJECT_ANCHOR = " def _run_memory_efficient_xformers_forward("
def patch_file(path):
with open(path, "r") as f:
content = f.read()
changed = False
if "_run_sdpa_fallback" in content:
print(" [skip] _run_sdpa_fallback already present")
elif INJECT_ANCHOR not in content:
print(" [warn] inject anchor not found")
else:
content = content.replace(INJECT_ANCHOR, FALLBACK_METHOD + INJECT_ANCHOR, 1)
print(" [ok] injected _run_sdpa_fallback (batch, pure-math)")
changed = True
if NEW_XFORMER_BLOCK in content:
print(" [skip] dispatch block already patched")
elif OLD_XFORMER_BLOCK in content:
content = content.replace(OLD_XFORMER_BLOCK, NEW_XFORMER_BLOCK, 1)
print(" [ok] patched dispatch block")
changed = True
else:
print(" [warn] dispatch block anchor not found")
if changed:
with open(path, "w") as f:
f.write(content)
print(f" Written: {path}")
def main():
print("=== patch_xformers_sdpa_batch (batch, pure-math) ===")
print(f"Target: {XFORMERS_PATH}")
patch_file(XFORMERS_PATH)
print("\nDone.")
if __name__ == "__main__":
main()

View File

@@ -1,191 +0,0 @@
"""
策略批量block-diagonal— F.scaled_dot_product_attention可走硬件 kernel
=============================================================================
构建块对角 causal mask对整批序列一次 F.scaled_dot_product_attention。
与 patch_xformers_sdpa_batch.py纯 matmul的区别
SDPA 会根据 PyTorch/驱动能力分发到最优 kernelFlash Attention /
mem-efficient attention / math fallback而不是固定走 cublas matmul。
历史说明:
该方案最早因输出全"!"而被弃用,后续排查确认"!"由 mamba_cache.py bug
引起,与 attention 实现无关。当前恢复此方案用于性能对比测试。
已知硬件限制BI-V100
cudnnFlashAttnForward 不支持 is_causal=True报错
本实现使用 is_causal=False + 显式块对角 additive mask 规避此限制。
若 SDPA 仍分发到有问题的 kernel回退到 patch_xformers_sdpa_batch.py。
优点vs 纯 matmul
SDPA 可分发到 Flash Attention kernel → O(L) 显存、更快的 CUDA kernel。
缺点:
依赖硬件 kernel 行为,若 kernel 有 bug 则数值错误(需与 matmul 版对比验证)。
Deploy:
python3 modified_scripts/patch_xformers_sdpa_batch_kernel.py
"""
XFORMERS_PATH = (
"/usr/local/corex/lib64/python3/dist-packages/"
"vllm/attention/backends/xformers.py"
)
FALLBACK_METHOD = '''
def _run_sdpa_fallback(
self,
query: torch.Tensor,
key: torch.Tensor,
value: torch.Tensor,
attn_metadata: "XFormersMetadata",
) -> torch.Tensor:
"""批量 F.scaled_dot_product_attention fallback可走硬件 kernel
构建块对角 causal mask对整批序列一次 SDPA 调用。
SDPA 可分发到 Flash Attention / mem-efficient attention kernel。
is_causal=False + 显式 additive mask规避 cudnnFlashAttnForward
不支持 is_causal=True 的限制。
块对角 maskseq1 len=3seq2 len=2
s1,0 s1,1 s1,2 s2,0 s2,1
s1,0 [ 0 -inf -inf -inf -inf ]
s1,1 [ 0 0 -inf -inf -inf ]
s1,2 [ 0 0 0 -inf -inf ]
s2,0 [-inf -inf -inf 0 -inf ]
s2,1 [-inf -inf -inf 0 0 ]
Args:
query : [1, total_prefill_tokens, num_heads, head_dim]
key : [1, total_prefill_tokens, num_kv_heads, head_dim]
value : [1, total_prefill_tokens, num_kv_heads, head_dim]
Returns:
[1, total_prefill_tokens, num_heads, head_dim]
"""
import torch.nn.functional as F
assert attn_metadata.seq_lens is not None
orig_dtype = query.dtype
total_tokens = query.shape[1]
# ── 块对角 causal mask [T, T] ─────────────────────────────────────
mask = torch.full(
(total_tokens, total_tokens),
float("-inf"),
dtype=orig_dtype,
device=query.device,
)
start = 0
for seq_len in attn_metadata.seq_lens:
end = start + seq_len
mask[start:end, start:end] = torch.tril(
torch.zeros(seq_len, seq_len, dtype=orig_dtype, device=query.device)
)
start = end
# ── [1, H, T, D] ──────────────────────────────────────────────────
q_all = query.squeeze(0).permute(1, 0, 2).contiguous().unsqueeze(0)
k_all = key.squeeze(0).permute(1, 0, 2).contiguous().unsqueeze(0)
v_all = value.squeeze(0).permute(1, 0, 2).contiguous().unsqueeze(0)
# ── GQA展开 KV heads ────────────────────────────────────────────
if k_all.shape[1] != q_all.shape[1]:
n = q_all.shape[1] // k_all.shape[1]
k_all = k_all.repeat_interleave(n, dim=1).contiguous()
v_all = v_all.repeat_interleave(n, dim=1).contiguous()
# ── F.scaled_dot_product_attention可走硬件 kernel─────────────
# is_causal=False避免 cudnnFlashAttnForward "not support causal mode"
# attn_mask 传 additive float mask非 boolSDPA 选择 math/kernel 路径
out = F.scaled_dot_product_attention(
q_all, k_all, v_all,
attn_mask=mask,
dropout_p=0.0,
is_causal=False,
scale=self.scale,
)
# [1, H, T, D] → [1, T, H, D]
return out.squeeze(0).permute(1, 0, 2).contiguous().unsqueeze(0)
'''
OLD_XFORMER_BLOCK = """\
self.attn_op = xops.fmha.flash.FwOp()
if self.alibi_slopes is None:
# Add the batch dimension.
query = query.unsqueeze(0)
key = key.unsqueeze(0)
value = value.unsqueeze(0)
out = xops.memory_efficient_attention_forward(
query,
key,
value,
attn_bias=attn_bias[0],
p=0.0,
scale=self.scale,
op = self.attn_op
)
return out.view_as(original_query)\
"""
NEW_XFORMER_BLOCK = """\
self.attn_op = xops.fmha.flash.FwOp()
if self.alibi_slopes is None:
# Add the batch dimension.
query = query.unsqueeze(0)
key = key.unsqueeze(0)
value = value.unsqueeze(0)
if self.head_size > 128:
out = self._run_sdpa_fallback(query, key, value, attn_metadata)
else:
out = xops.memory_efficient_attention_forward(
query,
key,
value,
attn_bias=attn_bias[0],
p=0.0,
scale=self.scale,
op=self.attn_op,
)
return out.view_as(original_query)\
"""
INJECT_ANCHOR = " def _run_memory_efficient_xformers_forward("
def patch_file(path):
with open(path, "r") as f:
content = f.read()
changed = False
if "_run_sdpa_fallback" in content:
print(" [skip] _run_sdpa_fallback already present")
elif INJECT_ANCHOR not in content:
print(" [warn] inject anchor not found")
else:
content = content.replace(INJECT_ANCHOR, FALLBACK_METHOD + INJECT_ANCHOR, 1)
print(" [ok] injected _run_sdpa_fallback (batch, F.sdpa kernel)")
changed = True
if NEW_XFORMER_BLOCK in content:
print(" [skip] dispatch block already patched")
elif OLD_XFORMER_BLOCK in content:
content = content.replace(OLD_XFORMER_BLOCK, NEW_XFORMER_BLOCK, 1)
print(" [ok] patched dispatch block")
changed = True
else:
print(" [warn] dispatch block anchor not found")
if changed:
with open(path, "w") as f:
f.write(content)
print(f" Written: {path}")
def main():
print("=== patch_xformers_sdpa_batch_kernel (batch, F.sdpa + kernel dispatch) ===")
print(f"Target: {XFORMERS_PATH}")
patch_file(XFORMERS_PATH)
print("\nDone.")
if __name__ == "__main__":
main()

View File

@@ -1,321 +0,0 @@
"""
策略顺序per-sequencefallback — 纯 PyTorch 数学实现
==========================================================
逐条序列用 matmul + softmax 手写 attention完全绕开所有硬件
flash attention kernelixformer / cudnnFlashAttnForward
背景:
Iluvatar cudnnFlashAttnForward 存在两个已知问题:
1. 不支持 is_causal=True报错
2. 使用 attn_mask 路径时数值结果不正确(静默错误,输出全为"!"
与华为昇腾 910B4 上 llama.cpp --flash-attn off 修复同类问题的原理相同。
纯数学路径matmul + softmax在任何 PyTorch 后端上结果都正确。
优点:
数值正确,不依赖任何硬件特定 attention kernel。
峰值显存 = max(seq_len)² × H × dtype_size由 --max-model-len 控制。
缺点:
并发请求的 prefill attention 串行执行。
O(L²) 显存(无 flash attention 的 O(L) 优化)。
内存参考fp16H_local=6
max-model-len=4096 → 峰值 ~200 MB
max-model-len=8192 → 峰值 ~800 MB
max-model-len=16384 → 峰值 ~3.2 GB
额外 patcharg_utils.py
vllm 0.6.3 在 max_model_len > 32K 时会自动开启 chunked prefill无命令行
关闭选项),原意是防止 profiling OOM。但 _run_sdpa_fallback 已通过 Q-tiling
解决了该问题chunked prefill 反而会把推理路径从 _run_sdpa_fallback 切换到
_forward_prefix_pytorch属于不必要的行为变更因此一并禁用该自动逻辑。
Deploy:
python3 modified_scripts/patch_xformers_sdpa_seq.py
"""
XFORMERS_PATH = (
"/usr/local/corex/lib64/python3/dist-packages/"
"vllm/attention/backends/xformers.py"
)
ARG_UTILS_PATH = (
"/usr/local/corex/lib64/python3/dist-packages/"
"vllm/engine/arg_utils.py"
)
LOGITS_PROC_PATH = (
"/usr/local/corex/lib64/python3/dist-packages/"
"vllm/model_executor/layers/logits_processor.py"
)
# _apply_logits_processors crashes when seq_groups is None (intermediate
# chunked-prefill chunks on the driver rank). Add an early-return guard.
_LP_OLD_BLOCK = """\
def _apply_logits_processors(
logits: torch.Tensor,
sampling_metadata: SamplingMetadata,
) -> torch.Tensor:
found_logits_processors = False\
"""
_LP_NEW_BLOCK = """\
def _apply_logits_processors(
logits: torch.Tensor,
sampling_metadata: SamplingMetadata,
) -> torch.Tensor:
if sampling_metadata.seq_groups is None: # intermediate chunked-prefill chunk
return logits
found_logits_processors = False\
"""
# vllm 0.6.3 自动开启 chunked prefill 的原始块
_ARG_OLD_BLOCK = """\
if (is_gpu and not use_sliding_window and not use_spec_decode
and not self.enable_lora
and not self.enable_prompt_adapter):
self.enable_chunked_prefill = True
logger.warning(
"Chunked prefill is enabled by default for models with "
"max_model_len > 32K. Currently, chunked prefill might "
"not work with some features or models. If you "
"encounter any issues, please disable chunked prefill "
"by setting --enable-chunked-prefill=False.")\
"""
_ARG_NEW_BLOCK = """\
if (is_gpu and not use_sliding_window and not use_spec_decode
and not self.enable_lora
and not self.enable_prompt_adapter):
pass # skip auto-enable: Q-tiling in _run_sdpa_fallback
# handles long-context memory without chunked prefill\
"""
FALLBACK_METHOD = '''
def _run_sdpa_fallback(
self,
query: torch.Tensor,
key: torch.Tensor,
value: torch.Tensor,
attn_metadata: "XFormersMetadata",
) -> torch.Tensor:
"""纯数学 causal attention fallback带 Q-tiling 内存优化。
调用时机kv_cache.numel()==0profiling 阶段)。
此路径无 KV 缓存前缀KV 长度 == query 长度。
内存优化Q-tiling与 Flash Attention 同思路):
将 Q 分成 _Q_CHUNK 大小的子块逐块计算,每块峰值内存
O(_Q_CHUNK × q_len) 而非 O(q_len²)。
profiling 阶段序列可能达到 max_model_len如 20K tokens
不加 Q-tiling 会产生 9.6 GB 矩阵直接 OOM。
softmax 在 float32 下计算以防止 float16 溢出,结果转回原始 dtype。
Args:
query : [1, total_query_tokens, num_heads, head_dim]
key : [1, total_query_tokens, num_kv_heads, head_dim]
value : [1, total_query_tokens, num_kv_heads, head_dim]
Returns:
[1, total_query_tokens, num_heads, head_dim]
"""
_Q_CHUNK = 256 # 与 _forward_prefix_pytorch 的 _ATTN_Q_CHUNK 保持一致
assert attn_metadata.seq_lens is not None
orig_dtype = query.dtype
num_seqs = len(attn_metadata.seq_lens)
# 推导每条序列的实际 query 长度。
# 正常 prefill 时 q_len == seq_len如果将来遇到 chunked 场景,
# query_start_loc 记录的是真实 query token 数(非全序列长度)。
if (attn_metadata.query_start_loc is not None
and len(attn_metadata.query_start_loc) == num_seqs + 1):
q_lens = [
int(attn_metadata.query_start_loc[i + 1].item()) -
int(attn_metadata.query_start_loc[i].item())
for i in range(num_seqs)
]
else:
q_lens = list(attn_metadata.seq_lens)
q_flat = query.squeeze(0) # [T, H, D]
k_flat = key.squeeze(0) # [T, Hkv, D]
v_flat = value.squeeze(0)
output = torch.empty_like(q_flat)
seq_start = 0
for q_len in q_lens:
seq_end = seq_start + q_len
# 当前序列的完整 K/V此路径无前缀KV == Q
k_s = k_flat[seq_start:seq_end].permute(1, 0, 2).float() # [Hkv, q_len, D]
v_s = v_flat[seq_start:seq_end].permute(1, 0, 2).float() # [Hkv, q_len, D]
# GQA展开 KV heads 至与 query heads 一致
if k_s.shape[0] != self.num_heads:
n = self.num_heads // k_s.shape[0]
k_s = k_s.repeat_interleave(n, dim=0).contiguous()
v_s = v_s.repeat_interleave(n, dim=0).contiguous()
# k_pos 用于因果掩码
k_pos = torch.arange(q_len, device=query.device)
# Q-tiling分块处理 query峰值内存 O(_Q_CHUNK × q_len)
for qc_start in range(0, q_len, _Q_CHUNK):
qc_end = min(qc_start + _Q_CHUNK, q_len)
# [H, qc, D]
q_c = q_flat[seq_start + qc_start:seq_start + qc_end] \
.permute(1, 0, 2).float()
# [H, qc, q_len]
attn_w = torch.matmul(q_c, k_s.transpose(-2, -1)) * self.scale
# 因果掩码q_c 里位置 j 只能看 k_pos <= j相对位置
qc_q_pos = torch.arange(qc_start, qc_end, device=query.device)
mask = k_pos.unsqueeze(0) > qc_q_pos.unsqueeze(1)
attn_w = attn_w.masked_fill(mask.unsqueeze(0), float("-inf"))
attn_w = torch.softmax(attn_w, dim=-1)
out_c = torch.matmul(attn_w, v_s).to(orig_dtype) # [H, qc, D]
output[seq_start + qc_start:seq_start + qc_end] = (
out_c.permute(1, 0, 2))
seq_start = seq_end
return output.unsqueeze(0) # [1, T, H, D]
'''
OLD_XFORMER_BLOCK = """\
self.attn_op = xops.fmha.flash.FwOp()
if self.alibi_slopes is None:
# Add the batch dimension.
query = query.unsqueeze(0)
key = key.unsqueeze(0)
value = value.unsqueeze(0)
out = xops.memory_efficient_attention_forward(
query,
key,
value,
attn_bias=attn_bias[0],
p=0.0,
scale=self.scale,
op = self.attn_op
)
return out.view_as(original_query)\
"""
NEW_XFORMER_BLOCK = """\
self.attn_op = xops.fmha.flash.FwOp()
if self.alibi_slopes is None:
# Add the batch dimension.
query = query.unsqueeze(0)
key = key.unsqueeze(0)
value = value.unsqueeze(0)
if self.head_size > 128:
out = self._run_sdpa_fallback(query, key, value, attn_metadata)
else:
out = xops.memory_efficient_attention_forward(
query,
key,
value,
attn_bias=attn_bias[0],
p=0.0,
scale=self.scale,
op=self.attn_op,
)
return out.view_as(original_query)\
"""
INJECT_ANCHOR = " def _run_memory_efficient_xformers_forward("
def patch_file(path):
with open(path, "r") as f:
content = f.read()
changed = False
if "_run_sdpa_fallback" in content:
print(" [skip] _run_sdpa_fallback already present")
elif INJECT_ANCHOR not in content:
print(" [warn] inject anchor not found")
else:
content = content.replace(INJECT_ANCHOR, FALLBACK_METHOD + INJECT_ANCHOR, 1)
print(" [ok] injected _run_sdpa_fallback (sequential, pure-math)")
changed = True
if NEW_XFORMER_BLOCK in content:
print(" [skip] dispatch block already patched")
elif OLD_XFORMER_BLOCK in content:
content = content.replace(OLD_XFORMER_BLOCK, NEW_XFORMER_BLOCK, 1)
print(" [ok] patched dispatch block")
changed = True
else:
print(" [warn] dispatch block anchor not found")
if changed:
with open(path, "w") as f:
f.write(content)
print(f" Written: {path}")
def patch_arg_utils(path):
with open(path, "r") as f:
content = f.read()
changed = False
if "skip auto-enable: Q-tiling" in content:
print(" [skip] chunked-prefill auto-enable already disabled")
elif _ARG_OLD_BLOCK in content:
content = content.replace(_ARG_OLD_BLOCK, _ARG_NEW_BLOCK, 1)
print(" [ok] disabled chunked-prefill auto-enable for 32K+")
changed = True
else:
print(" [warn] target block not found — check arg_utils.py version")
if changed:
with open(path, "w") as f:
f.write(content)
print(f" Written: {path}")
def patch_logits_processor(path):
with open(path, "r") as f:
content = f.read()
changed = False
if "intermediate chunked-prefill chunk" in content:
print(" [skip] seq_groups=None guard already present")
elif _LP_OLD_BLOCK in content:
content = content.replace(_LP_OLD_BLOCK, _LP_NEW_BLOCK, 1)
print(" [ok] added seq_groups=None guard in _apply_logits_processors")
changed = True
else:
print(" [warn] target block not found — check logits_processor.py version")
if changed:
with open(path, "w") as f:
f.write(content)
print(f" Written: {path}")
def main():
print("=== patch_xformers_sdpa_seq (sequential, pure-math) ===")
print(f"Target: {XFORMERS_PATH}")
patch_file(XFORMERS_PATH)
print("\n=== patch_arg_utils (disable chunked-prefill auto-enable) ===")
print(f"Target: {ARG_UTILS_PATH}")
patch_arg_utils(ARG_UTILS_PATH)
print("\n=== patch_logits_processor (seq_groups=None guard for chunked prefill) ===")
print(f"Target: {LOGITS_PROC_PATH}")
patch_logits_processor(LOGITS_PROC_PATH)
print("\nDone.")
if __name__ == "__main__":
main()

View File

@@ -1,181 +0,0 @@
"""
策略顺序per-sequence— F.scaled_dot_product_attention可走硬件 kernel
=============================================================================
逐条序列调用 F.scaled_dot_product_attentionis_causal=False + 显式因果 mask。
与 patch_xformers_sdpa_seq.py纯 matmul的区别
SDPA 可分发到 Flash Attention / mem-efficient attention kernel
而纯 matmul 固定走 cublas。
硬件限制BI-V100
cudnnFlashAttnForward 不支持 is_causal=True直接报错
必须使用 is_causal=False + 显式 additive causal mask。
每条序列单独构造上三角 -inf maskpeak 显存 = max(seq_len)² × dtype
比 batch 版的 total_tokens² 小得多。
与 batch_kernel 的对比:
seq_kernel: 显存小peak = max_single_seq²并发 prefill 串行排队
batch_kernel: 显存大peak = total_tokens²并发 prefill 一次并行处理,
通过 --max-num-batched-tokens 控制 total_tokens 上限
Deploy:
python3 modified_scripts/patch_xformers_sdpa_seq_kernel.py
"""
XFORMERS_PATH = (
"/usr/local/corex/lib64/python3/dist-packages/"
"vllm/attention/backends/xformers.py"
)
FALLBACK_METHOD = '''
def _run_sdpa_fallback(
self,
query: torch.Tensor,
key: torch.Tensor,
value: torch.Tensor,
attn_metadata: "XFormersMetadata",
) -> torch.Tensor:
"""顺序 F.scaled_dot_product_attention fallback可走硬件 kernel
逐条序列调用 SDPAis_causal=False + 显式上三角 additive mask。
cudnnFlashAttnForward 不支持 is_causal=True必须用显式 mask。
逐序列构造 maskpeak 显存 = max(seq_len)² × dtype远小于 batch 版)。
Args:
query : [1, total_prefill_tokens, num_heads, head_dim]
key : [1, total_prefill_tokens, num_kv_heads, head_dim]
value : [1, total_prefill_tokens, num_kv_heads, head_dim]
Returns:
[1, total_prefill_tokens, num_heads, head_dim]
"""
import torch.nn.functional as F
assert attn_metadata.seq_lens is not None
orig_dtype = query.dtype
q_flat = query.squeeze(0) # [T, H, D]
k_flat = key.squeeze(0) # [T, Hkv, D]
v_flat = value.squeeze(0)
output = torch.empty_like(q_flat)
start = 0
for seq_len in attn_metadata.seq_lens:
end = start + seq_len
# [1, H, L, D]
q_s = q_flat[start:end].permute(1, 0, 2).contiguous().unsqueeze(0)
k_s = k_flat[start:end].permute(1, 0, 2).contiguous().unsqueeze(0)
v_s = v_flat[start:end].permute(1, 0, 2).contiguous().unsqueeze(0)
# GQA展开 KV heads
if k_s.shape[1] != q_s.shape[1]:
n = q_s.shape[1] // k_s.shape[1]
k_s = k_s.repeat_interleave(n, dim=1).contiguous()
v_s = v_s.repeat_interleave(n, dim=1).contiguous()
# 逐序列因果 mask [L, L],上三角 -inf
causal_mask = torch.tril(
torch.zeros(seq_len, seq_len, dtype=orig_dtype, device=q_s.device)
)
causal_mask = causal_mask.masked_fill(
torch.triu(torch.ones(seq_len, seq_len, dtype=torch.bool,
device=q_s.device), diagonal=1),
float("-inf"),
)
# is_causal=False + 显式 mask规避 cudnnFlashAttnForward 不支持 is_causal=True
out_s = F.scaled_dot_product_attention(
q_s, k_s, v_s,
attn_mask=causal_mask,
dropout_p=0.0,
is_causal=False,
scale=self.scale,
)
# [1, H, L, D] → [L, H, D]
output[start:end] = out_s.squeeze(0).permute(1, 0, 2).to(orig_dtype)
start = end
return output.unsqueeze(0) # [1, T, H, D]
'''
OLD_XFORMER_BLOCK = """\
self.attn_op = xops.fmha.flash.FwOp()
if self.alibi_slopes is None:
# Add the batch dimension.
query = query.unsqueeze(0)
key = key.unsqueeze(0)
value = value.unsqueeze(0)
out = xops.memory_efficient_attention_forward(
query,
key,
value,
attn_bias=attn_bias[0],
p=0.0,
scale=self.scale,
op = self.attn_op
)
return out.view_as(original_query)\
"""
NEW_XFORMER_BLOCK = """\
self.attn_op = xops.fmha.flash.FwOp()
if self.alibi_slopes is None:
# Add the batch dimension.
query = query.unsqueeze(0)
key = key.unsqueeze(0)
value = value.unsqueeze(0)
if self.head_size > 128:
out = self._run_sdpa_fallback(query, key, value, attn_metadata)
else:
out = xops.memory_efficient_attention_forward(
query,
key,
value,
attn_bias=attn_bias[0],
p=0.0,
scale=self.scale,
op=self.attn_op,
)
return out.view_as(original_query)\
"""
INJECT_ANCHOR = " def _run_memory_efficient_xformers_forward("
def patch_file(path):
with open(path, "r") as f:
content = f.read()
changed = False
if "_run_sdpa_fallback" in content:
print(" [skip] _run_sdpa_fallback already present")
elif INJECT_ANCHOR not in content:
print(" [warn] inject anchor not found")
else:
content = content.replace(INJECT_ANCHOR, FALLBACK_METHOD + INJECT_ANCHOR, 1)
print(" [ok] injected _run_sdpa_fallback (seq, F.sdpa kernel)")
changed = True
if NEW_XFORMER_BLOCK in content:
print(" [skip] dispatch block already patched")
elif OLD_XFORMER_BLOCK in content:
content = content.replace(OLD_XFORMER_BLOCK, NEW_XFORMER_BLOCK, 1)
print(" [ok] patched dispatch block")
changed = True
else:
print(" [warn] dispatch block anchor not found")
if changed:
with open(path, "w") as f:
f.write(content)
print(f" Written: {path}")
def main():
print("=== patch_xformers_sdpa_seq_kernel (seq, F.sdpa + kernel dispatch) ===")
print(f"Target: {XFORMERS_PATH}")
patch_file(XFORMERS_PATH)
print("\nDone.")
if __name__ == "__main__":
main()