qwen部署
环境与部署
1 | conda create -n vllm-qwen python=3.11 -y |
这个模型必须通过两卡 + pipeline parallel + 降并发的方式部署,否则会因为显存不足而报错。
1 | CUDA_VISIBLE_DEVICES=0,1 vllm serve /home/share/HDstorage/xyc/qwen/models/qwen14b \ |
部署成功后,可以通过以下方式测试:
1 | curl http://127.0.0.1:8000/v1/chat/completions \ |
关闭服务:
1 | pkill -f "vllm serve /home/share/HDstorage/xyc/qwen/models/qwen14b" |
任务目标
这次工作分三段:
- 基于 vLLM 的 BitsAndBytes 量化支持,跑通小型 4-bit 模型推理。
- 分析 NF4 权重恢复在量化推理链路中的位置与开销。
- 基于 vLLM 的自定义量化扩展机制,设计实验性的 NF4 恢复/融合路径,并在 decode-like 小 batch 场景下评估其收益。
结论先说
这轮实验已经回答了几个关键问题:
- vLLM 跑 pre-quantized BitsAndBytes 4-bit 模型时,普通 dense 层并不是“先把 NF4 权重整层反量化到显存再计算”,而是尽量直接消费 packed weight 和
QuantState。 - 显式反量化成整块 dense bf16 权重的路径,主要瓶颈不是 LUT 查表本身,而是中间 dense weight 的显存写回和后续再读取。
- 如果把 bitsandbytes 小 batch fast path 的 GEMV/GEMM 组织方式迁到自定义 kernel,再把其中的 NF4 解码部分换成自己的版本,确实可以在 decode-like 小 batch 上得到端到端正收益。
- 只替换
q_proj时,真实 vLLM 服务端到端吞吐大约提升1.6%;扩到q/k/v/o后,提升大约2.5%;单独替down_proj基本没有端到端收益。
我是怎么做的
1. 跑通 vLLM + BitsAndBytes 4-bit 推理
实际运行环境在服务器 deep。模型为 unsloth/Qwen2.5-14B-Instruct-bnb-4bit 的本地副本。服务通过两卡、pipeline-parallel-size=2、max-num-seqs=1 成功启动并返回结果。
这里需要特别说明:真正跑通的是 PP=2,不是 TP=2。vLLM 日志里显示:
- rank 0:
PP rank 0, TP rank 0 - rank 1:
PP rank 1, TP rank 0
这是因为 vLLM 对 pre-quantized BitsAndBytes checkpoint 不支持 tensor parallel,但允许 pipeline parallel。
2. 梳理 NF4 在 vLLM 链路中的位置
我直接读了 vLLM 安装目录里的 BitsAndBytes 相关代码,重点看了:
vllm/model_executor/model_loader/bitsandbytes_loader.pyvllm/model_executor/layers/quantization/bitsandbytes.py
结论如下:
- loader 会先把 checkpoint 中的量化元数据组装成
QuantState。 - pre-quantized checkpoint 的
weight.absmax、weight.quant_map、weight.nested_absmax、weight.nested_quant_map等元数据,会在加载阶段被收集并重建。 _bind_quant_states_to_params()会把 double-quant 的 scale 元数据恢复出来,也就是把 nested 的absmax展开成 float32。- 但 packed 的 4-bit 权重本体仍然保留为
uint8,不会在普通 dense 路径中提前整层展开成 bf16/fp16 常驻显存。 - dense 线性层热路径最终走的是
bitsandbytes.matmul_4bit(x, packed_weight.T, quant_state)。 - MoE 那条路径更接近“先
dequantize_4bit(...),再交给后续算子”。
所以更准确地说,NF4 “恢复”发生在两个层面:
- 加载阶段:恢复
QuantState和 double-quant 的 scale 元数据。 - 推理阶段:普通 dense 层尽量直接消费 packed weight;如果走显式
dequantize_4bit路径,才会生成完整 dense weight。
3. 先做服务 benchmark
我写了一个 20 请求的流式 benchmark,统计:
- 首 token 延迟 TTFT
- 平均端到端耗时
- completion 吞吐
- 不同 prompt 长度下的变化
基线结果是:
- overall avg TTFT:
0.0858 s - p50/p95 TTFT:
0.0791 s / 0.1154 s - overall completion throughput:
70.76 tok/s
分组后大致如下:
- short, 约
88prompt tokens:0.0793 s,70.24 tok/s - medium, 约
363prompt tokens:0.0861 s,71.47 tok/s - long, 约
963prompt tokens:0.0986 s,69.70 tok/s
结论是:低并发条件下,prompt 变长会推高 TTFT,但 decode 吞吐整体比较稳定。
显式反量化路径的实验
实验设计
我没有直接改 bitsandbytes 本体,而是在自己的仓库里做了一套实验性扩展:
fs_plugins/custom_ops/nf4_ikko.cppfs_plugins/custom_ops/nf4_ikko.cufs_plugins/custom_ops/nf4_ikko.pybenchmark_nf4_ikko.py
第一版的目标不是替生产实现,而是把成本拆开量化。具体做法是:
- 从 checkpoint 读取 packed 4-bit 权重和量化元数据。
- 自己实现 NF4 restore kernel,把 packed weight 恢复成 dense bf16 权重。
- 再调用
F.linear。 - 与默认
bitsandbytes.matmul_4bit做单层对照。
恢复 kernel 中迁入的优化
后续我又把 mainla.cu 里的关键优化迁到了 restore kernel:
- shared LUT
- warp 内广播 scale
- pair write
- tail 处理
单层结论
我选了两个代表层做对照:
model.layers.0.self_attn.q_proj.weightmodel.layers.0.mlp.down_proj.weight
结果很一致:restore kernel 本身可以通过优化做快,但“先恢复整层 dense 权重再算”这条路径整体仍然打不过默认 bnb。
down_proj 上,优化后:
- restore+decode:
0.2600 ms -> 0.2424 ms - decode only:
0.2407 ms -> 0.2120 ms
但 restore+linear 总成本仍高于 bnb:
- batch 1: bnb
0.0314 ms,显式 restore0.3227 ms - batch 8: bnb
0.3399 ms,显式 restore0.3591 ms
q_proj 上,优化后:
- restore+decode:
0.1087 ms -> 0.0746 ms - decode only:
0.0749 ms -> 0.0415 ms
但总路径依然落后于 bnb:
- batch 1: bnb
0.0283 ms,显式 restore0.0596 ms - batch 8: bnb
0.0612 ms,显式 restore0.0686 ms
这里真正慢在哪
结论很明确:显式反量化路径的主要损失不在 LUT 或 scale 恢复,而在:
- 把整块 dense bf16 权重写回显存。
- 后续 GEMM 又要把这块 dense weight 从显存读一遍。
也就是说,显式 restore 的结构性问题是中间写回开销,而不是 NF4 解码逻辑本身。
从显式 restore 转向 fused decode + matmul
为什么转向 fused
既然问题出在“恢复成整块 dense 再写回显存”,那合理方向就是不再落地 dense weight,而是在 kernel 内边解码边做乘法。
第一版 fused 原型
我先做了一个朴素 fused 原型:
- 输入
x - 输入 packed
uint8weight - 输入
quant_state.absmax - 在 kernel 里边解码边累加
结果说明方向是对的,但朴素 per-output 累加写法太慢,打不过 bnb。
借 bitsandbytes kernel 组织方式做第二轮迭代
后面我参考了 bitsandbytes 源码里的 kernels.cu,特别是 kgemm_4bit_inference_naive 这类小 batch fast path 的组织方式,把 warp/GEMV 风格迁到了自己的 kernel 里,再把其中的 NF4 解码部分改成自己的实现。
这个版本的核心点是:
- 仍然直接消费 packed weight。
- 不生成整块 dense weight。
- 改成更接近 bnb fast path 的 warp 级输出组织。
修正 packed weight 行偏移错误之后,这条 fused 路径稳定跑通。
单层 fused 对比
对 q_proj 的 microbenchmark,结果如下:
- batch 1
- bnb:
0.0159 ms - 显式 restore:
0.0595 ms - 新 fused:
0.0129 ms
- bnb:
- batch 2
- bnb:
0.0601 ms - 显式 restore:
0.0668 ms - 新 fused:
0.0227 ms
- bnb:
- batch 4
- bnb:
0.0597 ms - 显式 restore:
0.0670 ms - 新 fused:
0.0413 ms
- bnb:
- batch 8
- bnb:
0.0620 ms - 显式 restore:
0.0689 ms - 新 fused:
0.0787 ms
- bnb:
这说明 fused 方向在 decode-like 小 batch 场景上是成立的:batch 1/2/4 可以赢,batch 8 开始退化。
嵌入 vLLM 真实推理流程
接入方式
我没有直接改 vLLM 安装包,而是用了 monkeypatch:
sitecustomize.pyvllm_ikko_sitecustomize.py
启动 vLLM 时通过环境变量控制:
PYTHONPATH=/home/xyc/PCFG-NATVLLM_IKKO_ENABLE=1VLLM_IKKO_MODE=<mode>
patch 的是 BitsAndBytesLinearMethod._apply_4bit_weight,即只替换指定层的 4-bit 线性层计算路径,其他层仍走默认 bnb。
支持的模式
目前已经做了这几类:
qproj: 只替换shape == (5120, 5120)的q_projattn: 替换 attention 线相关投影(5120, 5120):q_proj/o_proj(1024, 5120):k_proj/v_proj
down_proj: 只替换shape == (5120, 13824)的down_proj
端到端结果
所有端到端对比都使用同一组服务参数:
- 两卡
PP=2 --gpu-memory-utilization 0.6--max-num-seqs 1- 20 个串行流式请求
- 相同 benchmark 脚本和 prompt 组
1. 基线服务
默认 bitsandbytes 路径:
- avg TTFT:
0.0994 s - avg e2e:
0.5774 s - throughput:
68.58 tok/s
2. 只替 q_proj
- avg TTFT:
0.0896 s - avg e2e:
0.5684 s - throughput:
69.67 tok/s
相对基线:
- throughput 提升约
+1.6% - avg e2e 降低约
-1.6%
3. 扩到 q/k/v/o 四个 attention 投影
- avg TTFT:
0.0884 s - avg e2e:
0.5634 s - throughput:
70.28 tok/s
相对基线:
- throughput 提升约
+2.5% - avg e2e 降低约
-2.4%
这说明 attention 线整体替换比只替 q_proj 更有效。
4. 只替 down_proj
我又额外跑了一轮 down_proj 的端到端对比:
- avg TTFT:
0.0894 s - avg e2e:
0.5681 s - throughput:
69.70 tok/s
相对基线:
- throughput 提升约
+1.6% - avg e2e 降低约
-1.6%
这个数值和只替 q_proj 很接近,但没有超过 attention 全替换版本。结合前面的单层实验,可以更稳妥地理解为:
down_proj单层上并不是最适合 decode-like 优化的层。- 端到端里出现的小幅改善,更多是“局部替换仍然能成立”,但收益上限不如 attention 线。
- 真正最值得继续扩的仍然是
q/k/v/o这类更贴近 decode 热路径的层。
补充 Benchmark:固定 Prompt + 5 Repeats
前面的端到端结果是单轮 benchmark,能说明方向,但还不够干净。为了收尾,我又补了一轮更严格的对照:
- 固定 20 条请求,不再在不同模式之间重新生成 prompt。
- 只保留三组最有代表性的模式:
- baseline
q_projattn(q/k/v/o)
- 每组都跑 5 轮,统计
mean / std / min / max。
这轮 benchmark 使用的是同一份固定 prompt 集 fixed_qwen_prompts.json,每一轮都按完全相同的请求顺序执行。
结果汇总
| mode | avg TTFT mean ± std (s) | avg e2e mean ± std (s) | throughput mean ± std (tok/s) | throughput min/max |
|---|---|---|---|---|
| baseline | 0.0800 ± 0.0004 |
0.5605 ± 0.0005 |
71.01 ± 0.07 |
70.91 / 71.08 |
| q_proj | 0.0811 ± 0.0040 |
0.5620 ± 0.0029 |
70.75 ± 0.52 |
69.84 / 71.04 |
| attn | 0.0810 ± 0.0038 |
0.5619 ± 0.0039 |
70.76 ± 0.64 |
69.62 / 71.07 |
这一轮更干净 benchmark 的结论
这组结果和前面的单轮结果相比,更适合拿来做最终结论。因为它把 prompt 集和重复波动都控制住了。
从这轮数据看:
- baseline 的平均吞吐反而略高于
q_proj和attn两个自定义路径。 - 三组模式之间的差距已经缩到
0.2~0.3 tok/s量级,远小于q_proj/attn自身跨轮波动。 q_proj和attn的std明显高于 baseline,说明自定义路径当前还没有展现出更稳定的端到端收益。
所以如果只看这轮“固定 prompt + 5 repeats”的正式 benchmark,应该更保守地下结论:
- 自定义 fused 路径已经可以正确嵌入 vLLM 真实推理流程。
- 它在单层 microbenchmark 上能在 decode-like 小 batch 场景取得优势。
- 但在当前实现水平下,这个优势还没有稳定转化成端到端、统计上更有说服力的收益。
换句话说,前面那组 +1.6% / +2.5% 更适合被理解成“单轮观测到的正向信号”,而不是已经被重复实验充分证实的稳定收益。
Profiling 与结果解释
为了把现象解释清楚,我又补了一轮 profiling。原本想直接用 ncu / nsys 拿硬件计数器,但这台服务器上的 CUDA driver 和 Nsight CLI 版本有兼容性问题,硬件计数器和标准 report 导出都不稳定,所以最后采用的是 torch.profiler 做算子级 CUDA 时间统计。这个方法拿不到 occupancy、L2 hit rate 这类硬件计数器,但足够回答下面三个问题:
batch 8的退化是不是发生在 fused kernel 本体。- 退化更像 warp 利用率问题,还是更像访存/数据复用问题。
- attention 线为什么比
down_proj更值得继续优化。
1. batch 8 为什么开始退化
先看 fused kernel 本体的 CUDA 时间。下面的数字是 torch.profiler 对 20 次调用统计出的平均单次 CUDA 时间:
q_proj, batch 1:12.354 usq_proj, batch 8:77.877 usdown_proj, batch 1:29.637 usdown_proj, batch 8:205.781 us
这个结果说明两点:
- 退化确实集中在 fused kernel 本身,不是 Python 调度、
aten::t、cudaLaunchKernel之类的外围开销。profiler 里最主要的 CUDA 时间全部落在nf4_fused_matmul_absmax_*上。 - 当前 kernel 在
batch 1/2/4的工作点上是合适的,但到batch 8已经开始从“decode-like 小 batch GEMV”向“小 GEMM”过渡了。现在这版实现仍然沿用偏小 batch 的 warp/GEMV 组织,所以当M增大时,对输入激活x的复用不够好,batch 维上的工作没有像真正 tiled GEMM 那样被高效摊开。
更直白一点说:batch 8 慢下来,不是 NF4 解码突然出了问题,而是当前 fused kernel 的最优工作区间本来就偏 batch 1/2/4。当 batch 增长后,kernel mapping 开始偏离最优点。
2. 更像 warp 利用率问题,还是寄存器/访存平衡问题
从这轮 profiling 和现有 kernel 结构看,主因更像“访存/数据复用不足”,其次才可能是寄存器压力,而不是单纯的 warp 利用率问题。
依据主要有三条:
- 当前 fused kernel 在
q_proj的batch 1/2/4上已经能赢 bitsandbytes,这说明 warp 级输出组织本身不是根本错误。如果 warp 利用率一开始就很差,它不会在这些工作点上取得正收益。 batch 8的退化是随着M增大逐步出现的,更符合“输入x和量化权重/scale 的流式读取量增加,而 shared-memory / tile 复用不够”的模式。- profiler 看到的主要增长来自 kernel CUDA 时间本体,而不是 launch 次数或额外算子堆积,这说明问题在 kernel 内部的数据流,而不是外层调度。
可以把当前 fused kernel 理解成:它已经完成了“边解码边乘”的第一步,但还没有做到真正 GEMM-style 的 tile 化。于是它的瓶颈更像:
- packed weight 和
absmax仍然偏 streaming 访问 x在 batch 维增大后没有被充分复用- 算术强度不够高,更多表现为内存流量随 batch 增大而放大
所以这部分更适合写成:
- 现阶段更像“访存/数据复用与 kernel mapping 问题”
- 不是先把锅甩给寄存器或 occupancy
当然,要最终把这个判断坐实,后面还是应该补一轮真正的硬件 profiling,目标指标包括:
- achieved occupancy
- registers per thread
- local memory spill
- dram throughput
- L2 hit rate
- warp stall reason
3. 为什么 attention 线比 down_proj 更值得优化
这部分既能从端到端结果看,也能从 profiling 里看。
先看端到端:
- 只替
q_proj:68.58 -> 69.67 tok/s - 替
q/k/v/o:68.58 -> 70.28 tok/s - 只替
down_proj:68.58 -> 69.70 tok/s
attention 线整体替换的收益最高。
再看 profiler 中 fused kernel 的单次 CUDA 时间:
q_proj, batch 1:12.354 usq_proj, batch 8:77.877 usdown_proj, batch 1:29.637 usdown_proj, batch 8:205.781 us
当前 kernel 对 attention 线更友好,主要有三个原因:
attention 投影更贴近 decode 热路径
在自回归 decode 里,每一步都会频繁经过q/k/v/o这些投影,而且输入 batch 通常很小,这正好落在当前 fused kernel 最擅长的工作区间。attention 投影的形状更匹配当前 warp/GEMV 风格实现
当前已经验证过的 attention 形状主要是:(5120, 5120):q_proj/o_proj(1024, 5120):k_proj/v_proj
这些形状更接近当前 kernel 的小 batch 目标场景。
down_proj更宽,更容易暴露当前 kernel 的访存问题down_proj的形状是(5120, 13824),输出更宽,意味着每次调用要流过更多 packed weight 和 scale 数据。由于当前 fused kernel 还没有做真正的 shared-memory tile 复用,这类更宽的矩阵更容易变成内存流量主导。
所以当前阶段最合理的结论不是“down_proj 没价值”,而是:
down_proj也可以接,而且局部路径是能跑通的。- 但它对 kernel 组织的要求更接近通用 GEMM,而不是 decode-like GEMV。
- 在现有实现水平下,attention 线更容易把 fused 路径的优势转成真实端到端收益。
最终判断
NF4 恢复到底发生在哪
- 加载阶段恢复的是
QuantState和 double-quant scale 元数据。 - 普通 dense 路径不会先把整层 NF4 权重恢复成 dense bf16 常驻显存。
- 如果走显式
dequantize_4bit路径,才会发生整层 dense weight 的中间写回。
显式反量化值不值得
对 decode-like 小 batch 场景,不值得直接采用“restore -> dense -> linear”这条路径。即使 restore kernel 局部优化有效,中间写回开销仍然会吞掉大部分收益。
什么方向值得继续
当前最值得继续的方向不是进一步优化显式 restore,而是:
- 继续做直接消费 packed weight 的 fused decode + matmul。
- 优先替 attention 线上的更多层。
- 重点服务 decode-like 小 batch,而不是追求一开始就做通用大 GEMM。
这次改了哪些东西
本地仓库中,本轮核心修改集中在这些文件:
bench_vllm_qwen.pybenchmark_nf4_ikko.pyfs_plugins/custom_ops/nf4_ikko.cppfs_plugins/custom_ops/nf4_ikko.cufs_plugins/custom_ops/nf4_ikko.pysitecustomize.pyvllm_ikko_sitecustomize.pyvllm_bnb_benchmark_notes.mdoutputs/reports/nf4_ikko_experiments.md
其中:
nf4_ikko.cu是 restore kernel 和 fused kernel 的主体。nf4_ikko.py负责 PyTorch 扩展加载与接口封装。vllm_ikko_sitecustomize.py负责把自定义 fused 路径嵌到 vLLM 的 BitsAndBytes 线性层计算中。
小结
这轮实验最大的收获不是“证明 NF4 restore kernel 能更快”,而是把问题切清楚了:
- 默认 bnb 路径真正占优的关键,不只是 LUT 或解码实现,而是它避免了中间 dense weight 写回。
- 只优化 restore 本身,端到端收益有限。
- 直接消费 packed weight 的 fused 路径,在真实 vLLM 服务里已经能拿到小幅但稳定的正收益。
- 目前最值得继续扩展的是 attention 线,而不是单纯围绕显式反量化做更多微优化。
真实命中点排查
在继续做端到端对比之前,我专门做了一轮“真实调用点排查”。原因是前面我尝试在 BitsAndBytesLinearMethod._apply_4bit_weight 这一层做 timing hook,但 hook 没有命中真实 serve 热路径,所以那一层不适合作为最终 profiling 入口。
排查顺序按下面三层往下走:
- vLLM 自己的 BitsAndBytes 包装层
apply_bnb_4bitbitsandbytes.matmul_4bit
源码链路
从 vLLM 安装目录的源码可以直接确认这条链:
_apply_4bit_weightapply_bnb_4bit_apply_bnb_4bitbitsandbytes.matmul_4bit
其中:
apply_bnb_4bit不是普通 Python 函数,而是torch.ops.vllm.apply_bnb_4bit- 它是由 vLLM 把
_apply_bnb_4bit注册成 custom op 得到的
真实请求命中结果
我直接在安装包源码里给这两层加了最粗粒度探针,只记录:
- 有没有被调用
- 输入 shape
- quantized weight 的 shard shape
- 在
matmul_4bit里最终走的是gemv_4bit还是MatMul4Bit.apply
真实请求之后,两个日志文件都稳定命中:
/home/xyc/vllm_apply_bnb_4bit_hits.log/home/xyc/bnb_matmul_4bit_hits.log
关键结论如下:
apply_bnb_4bit确实是vllm serve真实 decode 流程中的热路径。- decode 阶段的
A=(1, 5120)/A=(1, 13824)最终落到了bitsandbytes.matmul_4bit。 - 对低 batch decode,
matmul_4bit最终走的是gemv_4bitfast path,不是MatMul4Bit.apply慢路径。 - prefill 大 shape(例如
A=(2048, 5120))更接近MatMul4Bit.apply这条通用路径。
也就是说,真实 decode 热路径并不是:
- “先
dequantize_4bit再通用linear”
而是:
apply_bnb_4bit -> bitsandbytes.matmul_4bit -> gemv_4bit
q/k/v/o 的真实 shard 顺序
进一步把 _apply_bnb_4bit 里 quant_states[i] 的真实循环顺序打出来之后,可以确认 attention 线在 decode 阶段的三分支顺序是:
q_projk_projv_proj
对应的实际 shard shape 是:
q_proj:(5120, 5120)k_proj:(1024, 5120)v_proj:(1024, 5120)
而独立的 o_proj 则是单独的:
o_proj:(5120, 5120)
这一步很重要,因为它说明后续如果要做 q/k/v/o 的逐项 timing,就应该把上下文从 _apply_bnb_4bit 传到 bitsandbytes.matmul_4bit,而不是继续在更上层的错误入口做统计。
Attention Projection 时间对比
在确认真实命中点之后,我又做了一轮更直接的实验:
- 把
q/k/v/o的上下文从 vLLM 的_apply_bnb_4bit传到bitsandbytes.matmul_4bit - 在
matmul_4bit这个真实热路径上聚合 decode 阶段的 CUDA 时间 - 做两组对比:
- baseline
- attn patch
为什么这里用了辅助配置
这里有一个必须说明的点。
如果直接在生产态配置下给 matmul_4bit 内联插入 CUDA event timing,vLLM 的 torch.compile / cudagraph / capture 流程会在 warmup 阶段报错。因此:
- 调用链的确认,是在生产态配置下完成的
- 时间聚合,则放到辅助观测配置下完成:
--enforce-eager
这个配置不适合拿来代表最终 e2e 性能,但非常适合回答一个更具体的问题:
- “真实命中点上的 attention projection 时间,到底降了没有?”
baseline vs attn patch
两边都使用同一份固定 prompt 集,并只统计 decode 阶段命中的 q/k/v/o 投影时间。
结果如下,单位都是 ms_per_token_row:
| projection | baseline | attn patch |
|---|---|---|
q_proj |
0.03298 ms |
0.01560 ms |
k_proj |
0.01964 ms |
0.01088 ms |
v_proj |
0.02114 ms |
0.01024 ms |
o_proj |
0.03620 ms |
0.01151 ms |
把四项加总得到:
- baseline per-token projection time:
0.10997 ms - attn patch per-token projection time:
0.04824 ms
也就是:
- 每 token 的 attention projection 总时间下降了
0.06173 ms - 降幅约
56.1%
这说明什么
这个结果非常关键,因为它回答了一个此前没有分清的问题:
- attention projection 这一项本身,确实明显下降了。
- 所以问题不在“kernel 根本没接进去”。
- 如果 e2e benchmark 仍然没有稳定改善,那么更大的问题就在系统别处,而不是 attention projection 本身。
换句话说,当前结论应该分两层:
kernel / projection 子系统层面:优化是成立的,而且降幅很明显serve 端到端层面:收益还没有稳定穿透整条系统链路
这也把后续方向收窄了:
- 继续提升 attention 覆盖率是合理的,但不是唯一问题。
- 需要进一步量化非 attention 层、框架调度、prefill 路径以及其他 decode 固定开销。
- 如果 projection 已经降了 50% 以上而 e2e 没明显跟上,那真正拖慢整体的部分已经不再是 attention projection 本身。
- Title: qwen部署
- Author: Ikko
- Created at : 2026-03-29 17:23:18
- Updated at : 2026-04-07 20:10:07
- Link: http://ikko-debug.github.io/2026/03/29/qwen/
- License: This work is licensed under CC BY-NC-SA 4.0.