Ling-3.0-tiny-RKNN / docs /KV_CACHE_BENCHMARK_20260922.md
Sariel00's picture
Publish Ling-3.0-tiny RKNN engine and model
3fd1a35 verified
|
Raw History Blame Contribute Delete
6.38 kB

RK3588 KV 缓存容量与长输入测试(2026-09-22)

后续 16K/64 复测、CPU/NPU 曲线和优化分析见 占用分析。

测试设备:NanoPi M6(RK3588,物理内存 15959 MiB,RKNPU 驱动 0.9.8),Ling3 v6 混合 W4A8/W8A8 权重。只比较 BF16 MLA KV 和 INT8 MLA KV(批量注意力采用原生 NPU INT8 矩阵乘,即此处的 W8A8 KV 路径);其他模型权重、KDA FP32 状态不变。FP16 仍保留为可选实验模式,不参与此表。

测试期间暂时停止 MindNano WebUI、OpenAI 适配服务、安卓视频桥、redroid 和 GNOME 会话;CPU 小核 1.8 GHz、大核 2.304/2.352 GHz,NPU 1 GHz,DMC 2.4 GHz,均固定最高频率。测试后恢复此前服务与调频设置。每一容量档独立启动服务,关闭历史会话快照和前缀检查点,以隔离模型及常驻 KV 内存。

上下文容量

RSS 是服务完成模型加载和预热时的进程常驻内存,已经包含该档 KV 容量;不是后续还要加上 KV 容量。KV 是按 6 层 MLA、每层 16 头计算的缓存字节数,BF16 每元素 2 字节,INT8 每元素 1 字节另加每 token/head 两个 FP32 scale。余量 是测量时系统的 MemAvailable,不是可供该服务独占的配额;RSS 也包含 NPU 共享映射。

上下文 BF16 KV BF16 启动 RSS BF16 余量 INT8 KV INT8 启动 RSS INT8 余量
8K 480 MiB 5750 MiB 7006 MiB 246 MiB 5512 MiB 7411 MiB
16K 960 MiB 6230 MiB 6482 MiB 492 MiB 5760 MiB 7116 MiB
32K 1920 MiB 7192 MiB 5473 MiB 984 MiB 6253 MiB 6570 MiB
64K 3840 MiB 9115 MiB 3642 MiB 1968 MiB 7242 MiB 5583 MiB
128K 7680 MiB 12457 MiB 334 MiB 3936 MiB 9217 MiB 3605 MiB
256K 15360 MiB 约 19–21 GiB(推测,未启动) 不适用 7872 MiB 12745 MiB 95 MiB

BF16 128K 和 INT8 256K 虽完成初始化,但内存余量过低并观察到少量系统 swap 增长,不适合作为此设备的可靠工作档位;BF16 256K 明显超过 16GB,因此没有强行启动。INT8 256K 只是初始化成功,位置大于模型原生 128K 尚未验证质量,不能算作 256K 长文本可用。

容量阶段原始 JSONL 中早期脚本产生的 predicted_filled_rss_mib 错误地在启动 RSS 上再次叠加 KV,不要使用;表中的 rss_mib、kv_capacity_mib 和 mem_available_mib 是实测值或直接按布局计算的值。现有脚本已改为根据 8K 基线只外推容量的增量。

输入与输出性能

请求的目标是 输入 token/最多输出 64 token,相同模型与 ASCII 数字输入,各请求独立、缓存命中 0、关闭 thinking。小于等于 4096 输入使用 8K 上下文;8K、16K、32K、64K 输入分别使用 16K、32K、64K、128K 上下文,以给输出留足空间。下表填写实际 tokenizer 与 API 返回的 token 数、TTFT 和解码计算速度;每档运行一次,无法代表统计分布。初次短输入可能由于自动调度未达到 NPU 注意力启用门槛,不能归因于 INT8 矩阵乘。

输入/输出 BF16 TTFT (s) INT8 TTFT (s) INT8 TTFT 改善 BF16 解码 tok/s INT8 解码 tok/s
128/64 0.778 0.808 -4% 18.38 18.53
256/64 1.487 1.485 0% 18.18 18.19
512/64 2.834 2.755 3% 17.15 17.71
1024/64 5.902 5.559 6% 16.08 16.16
2048/64 13.217 12.111 8% 13.45 13.51
4096/64 32.731 28.834 12% 10.64 10.22
8192/64 90.562 76.846 15% 7.11 7.11
16384/64 285.410 231.574 19% 4.28 4.45
32768/64 986.870 772.622 22% 2.32 2.53
65536/64 内存余量不足 2780.517 不可直接比较 不适用 1.35

BF16 128K 上下文启动时只剩约 334 MiB 可用,本次速度测试启动后最低仅约 65 MiB,故未请求 65536/64;不能把该项写成实测性能。短输入 128 token 两格式均为 mla_npu_calls=0;256 输入起逐步进入 NPU 路径,32K 输入为 1530 次 NPU MLA 调用、390 次 CPU MLA 调用(主要对应解码)。长输入下 TTFT 增长显著快于输入长度,32K 后解码仅约 2.3–2.5 token/s。单次结果用于观察趋势,不能据此断言百分比差异的统计显著性。

32K 输入时 MLA NPU 路径记录的耗时约 BF16 840 秒、INT8 624 秒,已占 TTFT 的 81%~85%;64K INT8 约 2484 秒,占 TTFT 的 89%。此字段包含排布、内存同步及 attention 计算,并非纯 NPU 矩阵乘耗时。这说明继续优化长输入,应优先检查 MLA 的历史 KV 搬运与批量注意力,而不仅是启动内存。

INT8 的 65536/64 在 128K 上下文中完整返回:输入、输出 token 数均准确,finish_reason=length,3066 次 NPU MLA 调用、0 回退,进程峰值 RSS 9225 MiB、进程 VmSwap=0,系统测试时余量约 3593 MiB。TTFT 约 46 分 21 秒,64-token 解码约 47 秒,证明这档能运行但不适合交互响应。INT8 256K 只是启动成功且仅余 95 MiB,超过模型原生 128K 位置范围,不做 128K 以上实际输入测试。

在本机同时考虑内存余量时,可先以 BF16 64K、INT8 128K 作为有余量的初始化档位;这不是交互可用长度的建议。即便 INT8,4096 输入的 TTFT 也约 29 秒,32K 达约 13 分钟;对低延迟应用仍需压缩输入或改进长预填充。这里只测速度和内存,不代表两种 KV 格式的长文本回答质量相同。

脚本:kv_context_benchmark.py;板端原始 JSONL 和逐请求引擎日志保存在测试设备的独立运行目录,未纳入仓库。FP16 独立实验仍可用相同脚本指定 --mode fp16 重测,旧版 mla_int8_api_benchmark.py 与既有 FP16 数据保留在板端 run/;本次正式结论不混入 FP16。

复现时在仓库根目录执行 python3 engine/source/tests/kv_context_benchmark.py --help,分别对 --mode bf16 和 --mode int8 运行 --phase capacity 与 --phase perf,使用相对路径传入 --binary、--model 和 --output-dir 即可。脚本只停止自己启动的测试服务,不会自动暂停其他系统服务或调整频率;测试前应检查可用内存与电源、散热状态。