[gen_patch] fix 3 critical bugs: reduce struct selection, topk bits_per_pass injection, transform/batch_memcpy extraction

Bug 1: reduce struct selection — gen_patch selected bi100_plus_accum1_o4 (int8
path, items=32) instead of bi100_plus_float32_o4 (fp32 score accumulator,
items=24). vllm paged_attention always uses fp32 for score accumulation, so
the wrong struct was injecting items=32 into the 83%-weight hot path.

Fix: preference-ordered struct selection — float32 > accum2 > first non-default.
Now correctly selects bi100_plus_float32_o4 → NUM_ITEMS_PER_THREAD=24.

Bug 2: topk bits_per_pass not injected — gen_patch only extracted threads=512
from topk inline policy_selector, missing calc_bits_per_pass(key_size).
For Qwen3.6 float32 logits (key_size=4), bits_per_pass=11 (not 8).

Fix: topk-specific extraction that parses calc_bits_per_pass and returns
bits_per_pass=11. Now generates RADIX_BITS=11 patch for sampling_kernels.cu.

Bug 3: transform/batch_memcpy extraction failed — these headers use
policy struct naming, not bi100_* naming, so extract_bi100_structs was empty.

Fix: algorithm-specific fallback extraction for transform (reads
bi100_bytes_in_flight constexpr) and batch_memcpy (reads threads from
policy_selector return).

Validation: gen_patch 7 patches (was 6), test_smem_safety 191/191 safe
This commit is contained in:
project_6
2026-08-05 03:15:12 +00:00
parent 2c43eb524f
commit db0df78580

View File

@@ -162,14 +162,73 @@ def extract_hardcoded_values(filepath):
"""Fallback: extract key values from policy_selector return statements.
For algorithms where bi100_* structs don't exist (values computed inline).
Extracts the threads_per_block from the first return in the iluvatar branch.
Handles multiple patterns:
- topk: return {threads, items, load_algo, scan_algo, bits}
- transform: constexpr int bi100_bytes_in_flight = N;
- batch_memcpy: return {threads, items, ...}
- generic: first integer in return {} is threads_per_block
"""
with open(filepath, 'r') as f:
content = f.read()
algo = algo_from_filename(filepath)
# Find iluvatar branch
# --- topk special case: extract bits_per_pass from calc_bits_per_pass ---
if algo == 'topk':
# Extract the return statement: return {threads, items, ..., bits};
iluvatar_match = re.search(
r'hw\.at_least\(.*iluvatar.*?\)\s*\{(.*?)return\s*\{([^}]+)\}',
content, re.DOTALL
)
if iluvatar_match:
return_args = iluvatar_match.group(2).strip()
# Pattern: {512, items, BLOCK_LOAD_VECTORIZE, BLOCK_SCAN_WARP_SCANS, calc_bits_per_pass(key_size)}
parts = [p.strip() for p in return_args.split(',')]
fields = {}
if len(parts) >= 1 and parts[0].isdigit():
fields['threads'] = int(parts[0])
# calc_bits_per_pass for float32 (key_size=4) = 11
bits_match = re.search(r'calc_bits_per_pass', return_args)
if bits_match:
fields['bits_per_pass'] = 11 # key_size=4 for float32 logits
return [('__inline_topk__', fields)]
# --- transform special case: extract bytes_in_flight + thread config ---
if algo == 'transform':
fields = {}
bif_match = re.search(r'bi100_bytes_in_flight\s*=\s*(\d+)', content)
if bif_match:
fields['bytes_in_flight'] = int(bif_match.group(1))
# Look for thread count in vectorized policy or return statement
vec_threads = re.search(
r'VectorizedPolicy\s*\{?\s*(\d+)\s*,\s*(\d+)',
content
)
if vec_threads:
fields['threads'] = int(vec_threads.group(1))
fields['items'] = int(vec_threads.group(2))
elif not fields:
# Fallback: find any constexpr threads
t_match = re.search(r'threads_per_block\s*=?\s*(\d+)', content)
if t_match:
fields['threads'] = int(t_match.group(1))
if fields:
return [('__inline_transform__', fields)]
# --- batch_memcpy special case ---
if algo == 'batch_memcpy':
fields = {}
t_match = re.search(r'threads_per_block\s*[=:]\s*(\d+)', content)
if t_match:
fields['threads'] = int(t_match.group(1))
if not fields:
t_match = re.search(r'return\s*\{?\s*(\d+)', content)
if t_match:
fields['threads'] = int(t_match.group(1))
if fields:
return [('__inline_batch_memcpy__', fields)]
# --- Generic fallback: find iluvatar branch return value ---
iluvatar_match = re.search(
r'hw\.at_least\(.*iluvatar.*?\)\s*\{(.*?)(?=\n\s{2,4}\})',
content, re.DOTALL
@@ -209,13 +268,24 @@ def generate_patches(header_dir):
summary.append(f"SKIP {algo}: no bi100_* structs and no inline values found")
continue
# Use the first non-default struct as the primary tuning
# (default is fallback; prefer the type-specific ones)
# Select the most relevant struct for vllm's primary data path.
# vllm's paged_attention score accumulator is always float32 (4 bytes),
# so we prefer bi100_*float32* or bi100_*accum4* structs.
# Fallback priority: float32 > accum2 (fp16 KV) > first non-default > first.
primary = None
for name, fields in structs:
if 'default' not in name:
primary = (name, fields)
preference_order = ['float32', 'accum4', 'accum2', 'int32']
for pref in preference_order:
for name, fields in structs:
if pref in name and 'det' not in name and 'default' not in name:
primary = (name, fields)
break
if primary:
break
if primary is None:
for name, fields in structs:
if 'default' not in name and 'det' not in name:
primary = (name, fields)
break
if primary is None:
primary = structs[0]