导读上一篇 MiniCPM5-2B 实测留下了两个问题:核显运行究竟占了多少系统内存,OpenVINO 在 UHD 770 上领先是否与内存系统有关。补齐设备身份、三轮内存采样和稳定解码区间的 IMC 计数后,答案清楚了一些:32 GB 对本机 4K、单请求有充足余量;OpenVINO 与 llama.cpp 在同一块 UHD 770 上分别产生约 33.85 和 20.24 GB/s 的系统 DRAM 流量。它证明不同执行路径确实把同一套内存系统用到了不同位置,却还不能证明带宽已经完全跑满。
上一篇最没说清的,不是速度
上一篇文章里,我在同一台 i5-12600K 上测试了 MiniCPM5-2B。CPU 路径中,调到 6 线程的 llama.cpp 比 OpenVINO 快约 10%;换到 UHD 770,OpenVINO 又以 20.31 token/s 对 13.37 token/s 领先约 52%。
图片说明:上一篇实测中,两套后端在 UHD 770 上的生成吞吐;计时路径与量化格式并不完全相同。图片来源:作者上一篇实测
OpenVINO 跑 MiniCPM5-2B:CPU 没赢,集显却快了约 52%
速度结果有了,解释还欠两笔账:运行时到底需要多少内存;两套后端是否真的把共享内存带宽跑满。
这次补测后的核心结论不是“某个后端已经碰到硬件绝对上限”,而是更适合部署决策的一句话:相同硬件不等于相同性能,后端架构、模型格式和执行路径能否有效调动内存系统,同样决定端侧模型最终能跑多快。
先把“内存占用”拆成三本账
第一本账是模型文件。OpenVINO INT4 权重约 1.64 GiB,llama.cpp 的 Q4_K_M GGUF 约 1.45 GiB。它们只是磁盘上的静态权重,不包含运行时库、执行图、临时缓冲和 KV Cache。
第二本账是进程树 RSS,也就是操作系统看到的驻留物理页。它包含匿名内存和已驻留的文件映射页,但不等于“应用独占新增了多少物理内存”;进程间共享页还可能在求和时被重复计算。
第三本账是整机的 MemAvailable。它估计系统在不触发 Swap 的情况下还能提供多少内存。对 UHD 770 这类没有独立显存的集显尤其重要,因为驱动管理的共享系统内存不一定完整记在 Python 或 llama.cpp 进程的 RSS 里。
所以,模型文件适合估静态权重,RSS 适合观察进程驻留峰值,MemAvailable 适合观察整机压力。三者互相校验,不能互相替代。
CPU 三轮补测:RSS 峰值 3.07 GiB 对 1.95 GiB
我把上下文固定为 4096,使用同一个短中文提示词,最多生成 256 个 token。采样器每 50 ms 统计一次进程树 RSS 和整机 MemAvailable,两套 CPU 后端各跑三轮。
OpenVINO CPU 的进程树峰值 RSS 平均约 3.071 GiB;llama.cpp CPU 稳定在约 1.945 GiB。至少在当前模型格式、运行时和参数下,OpenVINO 的 CPU RSS 峰值高约 1.13 GiB。
同三轮中,整机 MemAvailable 最大下降均值分别约为 1.63 GiB 和 0.98 GiB,明显小于 RSS。文件映射页、页缓存和可回收内存都会造成这种差异。
因此,这组数据只能写成:本机单请求测试里,OpenVINO CPU 的进程树 RSS 峰值更高。它不能直接改写成“OpenVINO 多占了 1.13 GiB 独享内存”,也不能外推到长上下文和并发服务。
到了集显,只看 RSS 会低估整机压力
这次我重新固定了设备身份。OpenVINO 的 GPU.0 明确记录为 Intel(R) UHD Graphics 770 (iGPU);llama.cpp 使用的 Vulkan0 也对应 UHD 770。测试不再只相信编号,而是同时保存设备全名、UUID、驱动路径和吞吐量级。
三轮内存采样中,OpenVINO 集显路径的进程树 RSS 平均约 1.125 GiB,但整机 MemAvailable 最大下降约 3.908 GiB。llama.cpp Vulkan 路径的两个数字分别约为 0.215 GiB 和 1.763 GiB。
这不意味着 OpenVINO “独占了 3.908 GiB”,也不能把 RSS 与 MemAvailable 的差额全部算给核显。后者还会受页缓存、可回收页和后台活动影响。它能确定的是:集显部署若只盯着用户进程 RSS,会明显低估本轮运行伴随的整机内存压力。
在这台 32 GB 机器上,两条 4K、单请求路径仍有充分余量;但如果目标是 8 GB 设备、128K 上下文或多并发,就必须按目标配置重测,不能从这里直接类推。
为了不再测错,我把测试拆成三道校验
第一道是设备校验。GPU 编号会随驱动和设备节点变化,GPU.0 或 Vulkan0 不是永久硬件身份证。每轮都要保存设备全名、UUID 和总线映射;一旦生成时间从二十秒突然缩到两三秒,也要先排查是否误跑到独显。
第二道是内存校验。benchmarks/measure_memory.py 每 50 ms 同时采集进程树 RSS 与 /proc/meminfo 中的 MemAvailable。前者观察进程,后者观察整机,两种口径分开报告。
第三道是稳态校验。最初把预热和正式运行放在不同进程里,运行时每轮都重新加载,集显 IMC 数据波动很大。最终方案改为同一进程加载一次、预热一次,再连续执行 5 次正式生成;这样的会话重复 3 轮。生成上限设为 256 token,OpenVINO 每轮因遇到 EOS 实际输出 237 token。OpenVINO 三轮带宽中位数为 33.81、33.85、33.87 GB/s,变异系数约 0.09%;llama.cpp 为 20.23、20.25、20.24 GB/s,变异系数约 0.05%。前面的高波动样本不进入结论。
这套方法仍不是实验室级内存分析器,但已经能避免两类最危险的误判:把错误设备当成核显,以及把加载、预热和稳定解码混成一个带宽数字。
4K 上下文不贵,128K 就是另一回事
权重之外,最容易随使用方式增长的是 KV Cache。它保存各层注意力已经计算过的 Key 和 Value,避免生成下一个 token 时重算全部前文。
MiniCPM5-2B 的本地配置为 42 层、2 个 KV heads、每个 head 128 维。llama.cpp 本轮的 K/V 类型都是 FP16,因此单序列每个 token 的理论缓存量为:
42 层 × 2 个 KV heads × 128 维 × 2 份 K/V × 2 字节 = 43,008 字节
也就是每 token 约 42 KiB。按单序列完整配置缓存容量计算,4K 上下文约为 168 MiB,128K 上下文约为 5.25 GiB。这里还没算权重、运行时、临时缓冲和并发序列;OpenVINO 的实际缓存精度、分配和复用也不能从这个公式直接推出。
所以,“2B 模型的权重只有 1.64 GiB,8 GB 内存肯定够”并不可靠。4K 单请求可能轻松,长上下文、批处理或并发会把问题完全改写。
先建立同机参考,再讨论有没有跑满
带宽测试里,dmidecode 用来确认内存容量和速率,stress-ng --stream 用来制造持续的混合读写压力,perf stat 则读取 CPU 内存控制器的 IMC 事件。简单说,stress-ng 负责造负载,perf 负责观察硬件实际发生了多少 DRAM 读写。
本机是 4×8 GB DDR4-3200,i5-12600K 支持 2 个内存通道,因此理论带宽是:
3200 MT/s × 8 字节 × 2 通道 = 51.2 GB/s
理论值不等于应用能持续得到的带宽。我用同一组 IMC 计数器包住 stress-ng --stream,10 线程三轮分别得到 42.49、42.44、42.50 GB/s,中位数约 42.49 GB/s。它约为理论值的 83.0%,可以作为这台机器、这次启动状态和这类混合读写负载下的压力参考,但不是所有工作负载通用的硬上限。
同一轮测试里,llama.cpp CPU 6 线程稳定解码区间约为 39.88 GB/s,达到压力参考的 93.9%;16 线程约为 36.65 GB/s,只占 86.3%。线程增加后流量反而下降,说明它不是简单地“线程越多,直到撞上同一道带宽墙”。调度、同步、缓存和共享资源竞争都可能参与,现有数据无法指定唯一原因。
同一块 UHD 770,也能跑出两种带宽位置
稳定重测中,OpenVINO 在 UHD 770 上的系统 DRAM 读写总流量中位数为 33.85 GB/s,约为同机压力参考的 79.7%,也约为理论带宽的 66.1%。llama.cpp Vulkan 路径为 20.24 GB/s,分别约占 47.6% 和 39.5%。两者都没有顶在 42.49 GB/s 的同一平台上。
吞吐与这个差异方向一致。OpenVINO 三个会话平均约 20.06 token/s,首 token 延迟约 135 ms;llama.cpp 约 13.59 token/s。OpenVINO 的吞吐高约 47.6%,同时系统 DRAM 流量高约 67.2%。
这组数据支持“执行路径不同,调动内存系统的程度不同”,却不能把全部差异归因于某一个名为“架构”的变量。两条路径还同时存在模型格式、量化实现、算子内核、运行时和驱动差异;IMC 又是整机计数器,记录的不是某个进程独占流量。
它同样没有证明 UHD 770 的共享内存已经跑满。OpenVINO 距同机 stress-ng 参考仍有约 8.64 GB/s,而不同负载本就不应机械对齐。若要证明严格的带宽因果,还需要降低内存频率、改单/双通道或使用更可控的工作负载做对照。
但从部署角度,结论已经够用了:在这台 32 GB 机器、4K 上下文、单请求条件下,OpenVINO 能在 UHD 770 上稳定达到约 20 token/s,首 token 约 135 ms,整机可用内存最大下降约 3.9 GiB。用于本地对话、开发验证和轻量单用户部署是可行的。这不是并发生产服务承诺,却已经超出“只能跑起来”的水平。
这篇文章最终回答了什么
第一,模型文件大小不是运行内存需求。CPU 要看进程树 RSS,集显还要同时看整机 MemAvailable,长上下文则必须把 KV Cache 单独算进去。
第二,32 GB 对本机 4K、单请求运行 MiniCPM5-2B 4-bit 有充足余量;8 GB、128K 和多并发不在这个结论范围内。
第三,CPU 6 线程已经达到同机混合读写压力参考的约 93.9%,说明访存压力很高,但“接近参考值”仍不等于“任何负载都已 100% 跑满”。
最后,也是这次补测最有价值的结果:相同 UHD 770 上,OpenVINO 与 llama.cpp 分别跑在 33.85 和 20.24 GB/s 的系统 DRAM 流量位置,对应约 20.06 和 13.59 token/s。相同硬件不会自动带来相同性能,后端如何组织计算、搬运和缓存,同样决定最终速度。
到这里,上一篇留下的容量和带宽问题,已经足以收束为一个工程判断:OpenVINO 的集显路径表现不错,可以正常部署使用;我们证明的是它在当前配置下稳定、可用且更能调动内存系统,不是它已经触及 UHD 770 的绝对上限。
参考资料
MiniCPM5-2B OpenVINO 与 llama.cpp 实测:https://mp.weixin.qq.com/s/9x1uPjk6u6h8K7vc3QZGUQ
MiniCPM5-2B 官方模型卡:https://huggingface.co/openbmb/MiniCPM5-2B
MiniCPM5-2B 官方 GGUF 权重:https://huggingface.co/openbmb/MiniCPM5-2B-GGUF
Intel Core i5-12600K 官方规格(DDR4-3200、2 个内存通道):https://www.intel.com/content/www/us/en/products/sku/134589/intel-core-i512600k-processor-20m-cache-up-to-4-90-ghz/specifications.html
英特尔集显共享系统内存说明:https://www.intel.com/content/www/us/en/support/articles/000020962/graphics.html
— THE END —
文章仅做学术分享,如有侵权请联系删除,非常感谢!

