P0 fixes (blocking usability): - FIX-01: thread-local cuBLAS handle (was creating/destroying per matmul) - FIX-16: EOS token no longer leaks into API responses - FIX-17: max_seq_len configurable via --max-seq-len (default 2048, was hardcoded 256) - FIX-18: max_tokens clamped to available seq space, prompt overflow returns 400 P1 fixes (bugs & performance): - FIX-07: CachingAllocator wired into all hot paths (to_device, embedding, rope, concat) - FIX-08: CudaDeviceProp buffer increased to 32KB for CUDA 12.9 safety - FIX-09: tokenizer byte_fallback graceful degradation (was panic) - FIX-19: causal mask uses -INFINITY instead of -1e9 (BF16 supports inf) - FIX-20: LayerNorm rewritten to numerically stable two-pass algorithm - FIX-21: min block size guard (32 threads) for LayerNorm/RMSNorm launches P2 fixes (improvements): - FIX-22: Option<GpuKVCache> + take() eliminates dummy KV cache allocations - FIX-23: RoPE cache no longer artificially capped at 8192 positions Verified on dash5 (RTX 5090): 51 tok/s batch=1, 74 tok/s 2-concurrent, 1.7-3.3x HF transformers. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
7.2 KiB
xserv — To Be Fixed (2026-05-23 审查更新)
由全面审查产出的修复清单。每项修复有明确验收标准。 优先级: P0 (阻塞可用性) > P1 (严重bug/性能) > P2 (重要改进) > P3 (设计债务)
第一批:P0 — 阻塞可用性
FIX-01: 全局 cuBLAS handle [P0-性能] ❌未修
问题: gemm.rs 中 matmul (line 146) 和 batched_matmul (line 224) 每次调用都 CublasContext::new() 创建+销毁 handle。Qwen3-8B 一次 forward ~252 次 matmul。
修复要求:
- 使用 thread-local 单例 cuBLAS handle
- handle 生命周期覆盖整个进程
matmul/batched_matmul函数体内不再有CublasContext::new()
验收标准:
grep -n "CublasContext::new" crates/xserv-kernels/src/gemm.rs只出现 1 次(thread_local 初始化处)- 编译通过,现有 gemm_test 全部通过
FIX-16: EOS token 泄漏到 API 响应 [P0-功能] ❌新发现
问题: engine.rs:218 中 emit_token 先发 GenerateEvent::Token { text: "<|im_end|>" } 再发 Done。api.rs:110-111 把所有 Token text 拼到 content 里,导致最终响应包含 <|im_end|> 乱码。
修复要求:
emit_token中,当 token 是 EOS 时,不发送 Token event(或发送空 text),直接发 Done- 或者: API 层收到 Done 时丢弃最后一个 token 的 text(如果 finish_reason == "stop")
验收标准:
- 发送请求,响应 content 不包含
<|im_end|>或其他 special token 文本 - streaming 模式下最后一个 content chunk 不是 EOS 文本
- 编译通过
FIX-17: max_seq_len 硬编码 256 [P0-功能] ❌新发现
问题: engine.rs:53 硬编码 let max_seq_len = 256,超过就 KV cache panic。
修复要求:
Engine::load接受max_seq_len参数(或从 config 读取,上限为 config.max_seq_len())main.rs中通过命令行参数或环境变量传入,默认值改为 2048- 同步更新 RoPE cache 上限(当前
qwen3.rs:45限制 8192,应与 max_seq_len 一致)
验收标准:
grep -n "let max_seq_len = 256" crates/xserv-server/返回 0 行- 启动 server 时
--max-seq-len 4096可用 - 编译通过
FIX-18: max_tokens 无上限校验 [P0-功能] ❌新发现
问题: API 不校验 max_tokens,客户端可发 max_tokens: 1000000 导致 KV cache panic。
修复要求:
api.rs中 clampmax_tokens到engine.max_seq_len - prompt_tokens.len()- 如果 prompt 已超过 max_seq_len,返回 400 错误
验收标准:
- 发送
max_tokens: 999999,不 panic,正常生成到 seq_len 上限 - 发送超长 prompt(> max_seq_len),返回 HTTP 400
- 编译通过
第二批:P1 — 严重 bug/性能
FIX-07: 使用 CachingAllocator [P1-性能] ❌未修
问题: CachingAllocator 已实现(allocator.rs)但从未使用。所有 GPU 分配直接 cudaMalloc。
修复要求:
Tensor::empty对 GPU device 使用cached_alloc而非GpuBuffer::allocGpuBuffer::Drop调用cached_dealloc归还到池(而非cudaFree)- 或者更简单:在
GpuBuffer::alloc内部接入 caching allocator(全局透明替换)
验收标准:
- 连续运行 10 次 decode step,
cudaMalloc调用次数应显著低于总分配次数 - 编译通过,现有测试通过
- 推理结果与修复前一致
FIX-08: CudaDeviceProp FFI 安全性 [P1-Bug] ❌未修
问题: ffi.rs:31 用 _pad: [u8; 4096] 猜测 cudaDeviceProp struct 大小,CUDA 12.9 可能更大。
修复要求:
- 增大 pad 到
[u8; 8192]或使用cudaDeviceGetAttribute替代 name 查询 - 可参考
device.rs中已有的cudaDeviceGetAttribute用法
验收标准:
device_info()返回正确的 device name- 编译通过
FIX-09: Tokenizer byte_fallback panic [P1-Bug] ❌未修
问题: bpe.rs:176-182 中 Qwen3 tokenizer 遇到不在 vocab 的单字节时 panic。
修复要求:
- 当
byte_fallback == true且单字节不在 vocab 时,查找<0xNN>格式 token - 如果
<0xNN>也不存在,返回 unk_token_id(而非 panic)
验收标准:
- 包含所有 256 个字节值的字符串可以 encode 不 panic
- 编译通过
FIX-19: 因果掩码 -1e9 应改为 -inf [P1-Bug] ❌新发现
问题: csrc/attention/causal_mask.cu:31 用 -1e9f 代替 -inf,注释说 "BF16 没有 -inf" 但这是错误的。
修复要求:
- BF16 路径改为
__float2bfloat16(-INFINITY) - F32 路径改为
-INFINITY(如果还没有的话)
验收标准:
- causal mask 中被遮蔽的值为
-inf(而非-1e9) - 编译通过,attention test 通过
FIX-20: LayerNorm 数值稳定性 [P1-Bug] ❌新发现
问题: csrc/normalization/layernorm.cu:19-25 注释写 "Welford online" 但实际用 E[x²] - E[x]²,大均值小方差时会灾难性抵消。
修复要求:
- 改为真正的 two-pass 或 Welford online 算法
- pass 1: 求 mean; pass 2: 求 variance = E[(x-mean)²]
验收标准:
- 对 mean=1e6, std=1e-3 的输入,layernorm 输出与 PyTorch 一致(relative error < 1e-3)
- 编译通过,现有测试通过
FIX-21: LayerNorm/RMSNorm 最小 block size [P1-Bug] ❌新发现
问题: layernorm.cu:88 和 rmsnorm.cu 对 hidden_size < 32 的输入会崩溃(block_reduce 需要至少一个完整 warp)。
修复要求:
- launch 时
block = max(min(hidden_size, 1024), 32)
验收标准:
- hidden_size=16 的 layernorm/rmsnorm 不崩溃
- 编译通过
第三批:P2 — 重要改进
FIX-22: Engine dummy KV cache 分配 [P2-性能] ❌新发现
问题: engine.rs:142-148 每次 batched decode 用 std::mem::replace 创建 dummy GpuKVCache::new(..., 1, ...) 来绕过 borrow checker,每步分配 num_layers * 2 个 GPU buffer。
修复要求:
- 将
running从Vec<Sequence>改为存储方式让 KV cache 可以独立借出 - 或使用
Option<GpuKVCache>+.take()/.insert()避免 dummy 分配
验收标准:
- batched decode 路径不再分配 dummy KV cache
- 编译通过,功能不变
FIX-23: RoPE cache 硬限 8192 [P2-功能] ❌新发现
问题: qwen3.rs:45 config.max_seq_len().min(8192) 人为截断。
修复要求:
- 去掉
.min(8192),或改为与 engine 的 max_seq_len 一致 - 确保 RoPE cache 覆盖实际使用的 max_seq_len
验收标准:
- RoPE cache 长度 >= engine max_seq_len
- 编译通过
FIX-15: GPT-2 消除 CPU round-trip [P3-性能] ❌未修
问题: GPT-2 split_qkv、merge_heads、add_bias 全在 CPU 做。优先级低(GPT-2 不是主力模型)。
修复依赖图和执行顺序
第一批 P0 (可并行):
FIX-01 (cuBLAS handle) ← 独立
FIX-16 (EOS 泄漏) ← 独立
FIX-17 (max_seq_len) ← 独立,FIX-23 依赖此
FIX-18 (max_tokens 校验) ← 依赖 FIX-17(需要知道 max_seq_len)
第二批 P1 (可并行):
FIX-07 (caching allocator) ← 独立
FIX-08 (CudaDeviceProp) ← 独立
FIX-09 (byte_fallback) ← 独立
FIX-19 (causal mask -inf) ← 独立
FIX-20 (layernorm 稳定性) ← 独立
FIX-21 (min block size) ← 独立
第三批 P2:
FIX-22 (dummy KV cache) ← 独立
FIX-23 (RoPE cache) ← 依赖 FIX-17