diff --git a/docs/TO-BE-FIXED.md b/docs/TO-BE-FIXED.md deleted file mode 100644 index c2fc668..0000000 --- a/docs/TO-BE-FIXED.md +++ /dev/null @@ -1,214 +0,0 @@ -# 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()` - -**验收标准**: -1. `grep -n "CublasContext::new" crates/xserv-kernels/src/gemm.rs` 只出现 1 次(thread_local 初始化处) -2. 编译通过,现有 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") - -**验收标准**: -1. 发送请求,响应 content 不包含 `<|im_end|>` 或其他 special token 文本 -2. streaming 模式下最后一个 content chunk 不是 EOS 文本 -3. 编译通过 - ---- - -### 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 一致) - -**验收标准**: -1. `grep -n "let max_seq_len = 256" crates/xserv-server/` 返回 0 行 -2. 启动 server 时 `--max-seq-len 4096` 可用 -3. 编译通过 - ---- - -### FIX-18: max_tokens 无上限校验 [P0-功能] ❌新发现 - -**问题**: API 不校验 `max_tokens`,客户端可发 `max_tokens: 1000000` 导致 KV cache panic。 - -**修复要求**: -- `api.rs` 中 clamp `max_tokens` 到 `engine.max_seq_len - prompt_tokens.len()` -- 如果 prompt 已超过 max_seq_len,返回 400 错误 - -**验收标准**: -1. 发送 `max_tokens: 999999`,不 panic,正常生成到 seq_len 上限 -2. 发送超长 prompt(> max_seq_len),返回 HTTP 400 -3. 编译通过 - ---- - -## 第二批:P1 — 严重 bug/性能 - -### FIX-07: 使用 CachingAllocator [P1-性能] ❌未修 - -**问题**: `CachingAllocator` 已实现(`allocator.rs`)但从未使用。所有 GPU 分配直接 `cudaMalloc`。 - -**修复要求**: -- `Tensor::empty` 对 GPU device 使用 `cached_alloc` 而非 `GpuBuffer::alloc` -- `GpuBuffer::Drop` 调用 `cached_dealloc` 归还到池(而非 `cudaFree`) -- 或者更简单:在 `GpuBuffer::alloc` 内部接入 caching allocator(全局透明替换) - -**验收标准**: -1. 连续运行 10 次 decode step,`cudaMalloc` 调用次数应显著低于总分配次数 -2. 编译通过,现有测试通过 -3. 推理结果与修复前一致 - ---- - -### 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` 用法 - -**验收标准**: -1. `device_info()` 返回正确的 device name -2. 编译通过 - ---- - -### 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) - -**验收标准**: -1. 包含所有 256 个字节值的字符串可以 encode 不 panic -2. 编译通过 - ---- - -### FIX-19: 因果掩码 -1e9 应改为 -inf [P1-Bug] ❌新发现 - -**问题**: `csrc/attention/causal_mask.cu:31` 用 `-1e9f` 代替 `-inf`,注释说 "BF16 没有 -inf" 但这是错误的。 - -**修复要求**: -- BF16 路径改为 `__float2bfloat16(-INFINITY)` -- F32 路径改为 `-INFINITY`(如果还没有的话) - -**验收标准**: -1. causal mask 中被遮蔽的值为 `-inf`(而非 `-1e9`) -2. 编译通过,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)²] - -**验收标准**: -1. 对 mean=1e6, std=1e-3 的输入,layernorm 输出与 PyTorch 一致(relative error < 1e-3) -2. 编译通过,现有测试通过 - ---- - -### 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)` - -**验收标准**: -1. hidden_size=16 的 layernorm/rmsnorm 不崩溃 -2. 编译通过 - ---- - -## 第三批: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` 改为存储方式让 KV cache 可以独立借出 -- 或使用 `Option` + `.take()` / `.insert()` 避免 dummy 分配 - -**验收标准**: -1. batched decode 路径不再分配 dummy KV cache -2. 编译通过,功能不变 - ---- - -### 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 - -**验收标准**: -1. RoPE cache 长度 >= engine max_seq_len -2. 编译通过 - ---- - -### 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 -```