ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Day 10·1 长上下文的O(n²)困境:稀疏注意力降到O(n·k)

Day 10·1 长上下文的O(n²)困境:稀疏注意力降到O(n·k) 真机实测通过本文实验已在 RK3588 板端实测完成2026-09方法学与原始记录见仓库 docs 与《实验脚本》目录。一句话导读长上下文为什么必须稀疏decode 每词耗时约等于常数 GEMM 加线性增长的注意力用板端三档实测看注意力占比从约 18% 涨到 71%讲清块稀疏把每词成本压成与 n 无关的平台。关键词长上下文、稀疏注意力、sparse-attn、O(n²)、RK35889-2 说 decode 是“单 q 单遍扫全 KV”9-3 给了它精度背书——可这个“全”字有个代价上下文每涨一倍每个新词都要多扫一倍的 KV。今天先不写代码把账算清楚长上下文里为什么全量注意力注定扛不住、而稀疏sparse是唯一划算的解。1. 知识点decode 的 GEMM 是常数注意力是线性——占比会一路涨引擎一次 decode生成一个词干两件事GEMM 部分qkv 投影 / O / gateup / down / lm_head读一遍权重矩阵与上下文长度无关。本板实测“每词总耗时 − 注意力”恒为 ~43ms第 3 节表 B 可复算即 GEMM 是常数注意力部分query 要对历史每一个 token算 Q·K^T、再按权重累加 V9-2 的“单遍扫全 KV”。历史有 n 个 token就扫 n 个——随 n 线性增长。于是每个词的耗时 ≈C_GEMM C_attn × n。n 越大注意力占比越高下表就是本板实测的 decode 注意力占比数据见第 3 节表 B上下文 n每词注意力精确每词总耗时精确注意力占比5069.7 ms52.5 ms~18%低204648.9 ms92.3 ms~53%中4059110.7 ms155.2 ms~71%高这不是数学题是内存带宽题q8 KV 每个 (token, 层) 要 2112 字节9-1n8K 时每层一次全扫就是 ~17MB28 层 ~480MB还要按头重复读。所以同样的模型短上下文跑得动长上下文被注意力拖垮——这正是本项目基准报告里 llama.cpp 的实测走势RK3588 同板decode 从 1K 档 98.6ms/词涨到 8K 档 411.6ms/词RK3588_性能基准报告.md。稀疏思路注意力矩阵里绝大多数 token 的权重接近 0——大部分历史对当前词的判断没用。与其扫全量不如把历史 KV 切成块block默认 32 token/块先用极廉价的“探针”找出最该看的前 k 块只对选中的 k 块做全量精确注意力。这样每词注意力成本从O(n)变成O(n/block k×block)第一项是找块的探针每块只算几个点积很便宜第二项是与 n 无关的常数k32、block32 → 视野固定在 ~1024 token。上下文再长每词注意力都封顶在这个常数里。prefill 侧同理一批 nb 个 token 对全历史的分数矩阵是 O(nb×n)块稀疏后只看 O(nb×k×block)。2. 对应代码稀疏开关藏在哪、什么时候生效先看全局默认与开关vllm_safetensors.c 第 235–238 行int g_sparse_attn 0; /* 0 exact attention (default) */ int g_sparse_k 32; /* top KV blocks kept per head ... */ int g_sparse_block 32; /* positions per KV block */ int g_sparse_probe 8; /* probe samples per block (max-dot fusion) */默认 OFF——不传参数时引擎跑的是精确注意力与前 9 天所有实验同口径。要打开用--sparse-attnmain.c 第 4872 行起解析--sparse-attn / --sparse-k / --sparse-block / --sparse-probe。真正的门控在两条主路径上条件一模一样decodevllm_safetensors.c 第 9176 行if (g_sparse_attn !st-use_kv_q4 c-seq_len g_sparse_block * 2) { /* block-probe top-k 选择 有界注意力sparse_attn_head10-2 拆 */prefill第 10805 行if (g_sparse_attn seq_len g_sparse_block * 2) { st_attn_batched_packed_sparse(...); /* 块稀疏批量注意力 */ } else { st_attn_batched_packed(...); /* 精确批量注意力 */ }两个要点seq_len 2×block即 64 token才进入稀疏。短于 64 token 时没得省直接走精确路径——这个“边界”是 10-3 的关键材料decode 侧多一个!use_kv_q4KV 被压成 q4--kv-q4时稀疏不可用10-3 细讲。开关、默认参数与门控都汇总在 优化配置与边界说明.md 第 23、31 行的统一口径里。3. 改动后果同一台板子稀疏开/关的实测差口径RK3588Orange Pi 5 Plus/ Qwen3-VL-2B-Instruct / 2026-09 / serve 进程内直测 //v1/completions贪婪 /VLLM_DEC_PROF1引擎逐词计时 / 默认 dual 权重 / 默认 4 线程 / 进程串行、无并发。精确exact与稀疏--sparse-attn --sparse-k 32用完全相同的 prompt 文本各跑一遍只有开关不同。上下文 n 取引擎日志[PREFILL-TIMING] n...的真实值。表 AprefillTTFT 的注意力部分上下文 n精确 prefill稀疏 prefillk32精确 GEMM/ATTN稀疏 GEMM/ATTN总时长变化50613.09 s13.65 s87.8% / 9.0%85.8% / 11.1%4.3%负优化204651.84 s51.50 s61.2% / 35.9%61.8% / 34.9%−0.6%无差别4059148.50 s106.11 s39.1% / 58.9%54.3% / 42.6%−28.6%表 Bdecode每词中位数精确 q8 KV 单遍全扫稀疏 sparse_attn_head上下文 n精确 attn ms/词稀疏(k32) attn ms/词精确每词总耗时稀疏(k32)每词总耗时5069.713.452.5 ms56.6 ms204648.939.192.3 ms82.3 ms4059110.755.4155.2 ms100.0 ms怎么读这两张表判读比数字重要decode 的 GEMM 真的是常数把“每词总耗时 − attn”算出来——精确三档分别是 52.5−9.742.8ms、92.3−48.943.4ms、155.2−110.744.5ms≈43ms 恒定。变的全是注意力精确的注意力随上下文线性涨n 从 506 涨到 4059×8精确 attn 从 9.7ms 涨到 110.7ms×11——q8 KV 单遍扫全量9-2也救不了“全量”本身稀疏把注意力压成一个平台k32 时 n 从 2046 到 4059 只让 attn 从 39.1→55.4ms涨的是探针扫的块数不是全量 KV到 8K/16K 这条曲线会继续摊平10-3 有边界说明但总时长里 GEMM ~43ms 是地板n506 时精确 attn 才 9.7ms、sparse 反而 13.4ms——短上下文开稀疏是负优化10-3 细讲。测量纪律说明表 B 的“每词中位数”来自引擎[DEC-SINGLE]逐词日志VLLM_DEC_PROF1模型生成长度因配置而异11–32 词不等提前撞 EOS 即停中位数不受词数影响。全部为同机串行单轮运行板子长时间满载有热节流风险仓库文档已注明可到 ~1.4×跨时间段绝对值仅供参考同条件对照结论有效——这也是每张表都用“同一 prompt、只切开关”的原因。开源仓库Kestrel-LLM (Gitee)源码可得双许可学习 / 学术研究免费标签稀疏注意力长上下文O(n²)计算优化上一篇Day 9·3 q8 KV 精度对照——量化进注意力输出差多少下一篇第 10-2 篇sparse top-k 块选择——“只看该看的“到底怎么
返回列表