在之前文章《Ultra X7 358H 笔记本电脑,本地部署当红炸子鸡 Qwen3.8-27B-Q4,实测生成速度 5.6 tok/s》中,Qwen3.8-27B-Q4 的上下文才 4K,远远不够用。
后来在《Ultra X7 358H 笔记本电脑,寻找本地用于编码的最强大模型,实测 21.0 tok/s,上下文 256K》中,AI 发现我这台笔记本电脑可以提升显存占比,并且能开大得多的上下文,这实在是一个惊喜!
下面是 AI 对实测过程的总结,有兴趣的可以浏览(使用 Deepseek Harness Desktop + Deepseek V4.1 Flash)。
概述
本机把 Intel iGPU 共享显存从 57% 提到 80%,成功让同一个模型能开 4 倍上下文,但几乎没能让更大的模型跑起来。
本文用实测数据说明硬件参数、提升方法,以及为什么”显存变大”≠“能跑更大的模型”。数据来源:本机 2026-10 实测(Windows 11 build 26200 / llama.cpp SYCL / torch 2.9.0+xpu)。
硬件参数(实测)
| 项目 | 实测值 | 说明 |
|---|---|---|
| CPU | Intel Core Ultra X7 358H | Panther Lake 平台 |
| GPU | Intel Arc B390(集成显卡,驱动 32.0.101.8509) | 没有独立显卡 |
| 显存 | 无独立显存,与系统内存共享 | 这是全文的前提 |
| 物理内存 | 31.55 GiB(33.9 GB) | 板载焊接,不可扩容 |
| 页面文件 | 提交上限 38.3 GiB(系统托管,C 盘) | 换页会拖垮推理速度 |
| C 盘 | 300 GB(可用 175 GB) | 页面文件所在盘 |
| D 盘 | 624 GB(可用 545 GB) | 模型与工具所在盘 |
| 操作系统 | Windows 11(build 26200) | |
| 推理引擎 | llama.cpp 0.5.0-dev,IntelLLVM 2025.3.3 编译的 SYCL 版 | 用 ggml-sycl.dll 走 GPU |
关键点:B390 是集成显卡,它的”显存”并不是一块独立的存储,而是从这 31.55 GiB 系统内存里划出来的一块。 所以”显存有多大”和”内存有多大”本质上是同一个池子里的两个额度,而不是两笔资源。
显存是怎么被”提升”的
Intel 驱动用一个注册表项控制显卡最多能提交多少比例的系统内存:
HKLM\SYSTEM\CurrentControlSet\Control\GraphicsDrivers\MemoryManager SystemPartitionCommitLimitPercentage (REG_DWORD)| 值 | 原状态 | 改后 |
|---|---|---|
| 十进制 | 57 | 80 |
| 十六进制 | 0x39 | 0x50 |
| 名义上限(31.55 GiB × 比例) | 18.0 GiB | 25.2 GiB |
操作方法(需要管理员 + 重启)
方法 A:双击注册表文件
D:\AI_Tools\igpu-shared-80.reg → 同意 UAC → 重启
(回滚用 igpu-shared-restore-57.reg)
方法 B:管理员命令行
reg add "HKLM\SYSTEM\CurrentControlSet\Control\GraphicsDrivers\MemoryManager" /v SystemPartitionCommitLimitPercentage /t REG_DWORD /d 80 /f执行后必须重启才生效。
⚠️ 不要在 80% 以上继续加。85% 时只剩 4.7 GiB 给 Windows,系统会开始换页,日常使用就卡了。 这个比例只是额度上限,不是预留——内存仍然是按需占用。
怎么确认真的生效了(两个层次,缺一不可)
只查注册表是不够的,必须做功能验证。
验证 1:注册表值
reg query "HKLM\SYSTEM\CurrentControlSet\Control\GraphicsDrivers\MemoryManager" /v SystemPartitionCommitLimitPercentage应显示 0x50。
验证 2:A/B 对照实验(最终判决)
方法:把注册表改回 57% → 重启 → 用同一模型、同一参数(q8_0 KV、-ngl 99)依次测试 16k / 32k / 64k;
再改回 80% → 重启 → 重复同一系列。
| 上下文 | 57%(旧额度 18.0 GiB) | 80%(新额度 25.2 GiB) |
|---|---|---|
| 16384 | ✅ PASS(测试时系统空闲 24.58 GiB) | ✅ PASS |
| 32768 | ❌ FAIL(测试时系统空闲 25.43 GiB) | ✅ PASS |
| 65536 | ❌ FAIL(测试时系统空闲 21.46 GiB) | ✅ PASS |
失败时报的是明确的设备内存错误,而不是系统内存不足:
level_zero backend failed with error: 39 (UR_RESULT_ERROR_OUT_OF_DEVICE_MEMORY)这条实验排除了所有替代解释:32k 失败时系统还空闲 25.43 GiB——比 80% 下成功加载 64k 时更宽松, 所以失败原因不是物理内存不足,而是显卡提交额度被打满。
结论:57% → 80% 的调整,确实是 32k / 64k 能打开的直接原因。 扣掉 15.33 GiB 权重后,留给上下文+计算缓冲的余量从约 2.7 GiB 变成约 9.9 GiB。
修正本文早期版本的一处推测:曾推测”改前只能开 4096”。实测证明 57% 下 16k 本来就能开,
只有 32k 及以上被挡住——start_qwen38.bat 里的 -c 4096 是保守设置,不是当时的硬限制。
验证 3:阶梯分配测试
用脚本 D:\AI_Tools\unsloth\scripts\check_xpu_memory.py 逐块分配显存,一直分配到失败为止:
| 改前 | 改后 | |
|---|---|---|
| torch 上报的设备上限 | 16.46 GiB | 16.46 GiB(没有变化!) |
| 实测能真正分配到 | 未测到失败点 | 20.50 GiB(被我设的系统内存安全阈值拦下,并非分配失败) |
这里有个反直觉现象:改完之后,驱动上报给 torch 的上限数字仍然是 16.46 GiB,一点没变, 但实际能分配的内存确实超过了它。也就是说:工具看到的额度和实际可用的额度不一致。 (对 Unsloth 这类按上报值做显存规划的框架,仍会按旧的 16.46 GiB 保守估计。)
为什么还是装不下更大的模型
真正的约束:物理内存,不是显存额度
内存不是两个池子,是一个池子。把显卡额度从 18 GiB 提到 25.2 GiB, 并没有给机器增加一个字节的内存,只是把”谁可以占用”的闸门放宽了。
| 状态 | 系统空闲内存 |
|---|---|
| 无模型(重启后基线) | 25.69 GiB |
| 加载 27B(15.33 GiB 权重)@ 4096 上下文 | 1.15 GiB |
| 加载 27B @ 16384 上下文 | 0.56 GiB |
| 加载 Qwen3.6-35B-A3B(15.46 GiB 权重)@ 32768 上下文 | 0.72 GiB |
看第一行和第三行:要塞进 15.33 GiB 的权重,系统实际付出了约 24.5 GiB 的代价 (25.69 − 1.15 = 24.54)。多出来的约 9.2 GiB 是加载模型时才会产生的固定开销。
换算成一条经验公式:
系统内存占用 ≈ 模型权重 × 1.6 + 5.8 GiB(Windows 基线)
代入验证:
| 模型权重 | 按公式所需 | 31.55 GiB 能否承受 |
|---|---|---|
| 15.33 GiB | 30.3 GiB | ✅ 实测通过(余 0.56~1.15 GiB) |
| 15.46 GiB | 30.5 GiB | ✅ 实测通过(余 0.72 GiB) |
| 16.51 GiB | 32.2 GiB | ❌ 超出,会换页 |
| 20.61 GiB | 38.8 GiB | ❌ 严重超出 |
那些”看不见”的开销到底是什么
同一时刻采样,两次得到一致的结果:
各进程工作集合计 : 14.59 GiB系统实际占用 : 30.82 GiB ───────────── 约 16 GiB 在进程之外这部分内存不属于任何进程的工作集,因此任务管理器里看不到,但它实实在在占着内存:
| 开销来源 | 量级 | 能否压缩 |
|---|---|---|
| Windows 内核 + 常驻服务 | ~5.8 GiB | 基本不能(已关掉酷狗/腾讯视频/网易云/夸克/OneDrive/MagicClaw 自启) |
| 显卡提交:模型权重 | 15.33 GiB(固定) | 只能换更小的量化 |
| 显卡提交:KV cache | 随上下文线性增长 | 已用 q8_0 量化 |
| 显卡提交:RS buffer(循环状态) | 598.5 MiB 固定(默认 4 槽位) | 已验证可用 -np 1 降到 149.6 MiB |
| 显卡提交:计算缓冲 | 随上下文增长 | 靠调小上下文 |
上下文到底吃掉多少显存(llama.cpp 分配的实测明细)
用 -lv 5 让 llama.cpp 自己打印每个缓冲区的分配量——这比看系统空闲内存可靠得多:
| 上下文 | KV buffer | RS buffer | 计算缓冲* | 非权重合计 | 相对 4k |
|---|---|---|---|---|---|
| 4096 | 136 MiB | 598.5 MiB | 150.6 MiB | 885 MiB | — |
| 16384 | 544 MiB | 598.5 MiB | 208.6 MiB | 1351 MiB | +466 MiB |
| 32768 | 1088 MiB | 598.5 MiB | 304.6 MiB | 1991 MiB | +1106 MiB |
| 65536 | 2176 MiB | 598.5 MiB | 496.6 MiB | 3271 MiB | +2386 MiB |
* 计算缓冲 = SYCL compute + Host compute
结论:27B 从 4k 开到 64k,设备侧要多花 2.33 GiB。 其中 KV 占 2.0 GiB(约 34 KiB/token),
计算缓冲占 0.35 GiB;RS buffer 是固定的 598.5 MiB,只跟服务槽位数(n_slots = 4)有关,与上下文长度无关。
这份明细也精确解释了 A/B 实验的失败点:
【32k @57%】KV 1088 MiB 分配成功 → RS 598.5 成功 → 计算图分配时炸: level_zero backend failed with error: 39 (UR_RESULT_ERROR_OUT_OF_DEVICE_MEMORY)
【64k @57%】KV 2176 MiB 分配成功 → 轮到 RS 时炸: failed to allocate SYCL0 buffer of size 627572736 ← 正好是 598.5 MiB 的 RS buffer反推:57% 下留给非权重分配的余量,落在 1351 MiB(16k 成功)与 1991 MiB(32k 失败)之间, 即约 1.3~1.9 GiB(名义额度 18.0 GiB 减去权重 15.33 GiB,再扣驱动自身占用)。
直接省下 449 MiB:把服务槽位从 4 降到 1(-np 1)
llama-server 默认开 4 个并行槽位(n_parallel = auto → 4),每个槽位都要自己的一份循环状态。
单用户用不到并发,改成 1 个槽位可以立刻省下约 449 MiB,且与上下文长度无关(每档都省):
| 16k 上下文 | 默认 -np 4 | -np 1 | 变化 |
|---|---|---|---|
| RS buffer | 598.50 MiB | 149.62 MiB | −448.88 MiB |
| KV buffer | 544.00 MiB | 544.00 MiB | 不变 |
| 计算缓冲(SYCL + Host) | 208.56 MiB | 208.56 MiB | 不变 |
| 非权重合计 | 1351 MiB | 902 MiB | −449 MiB |
换算到 64k:非权重占用从 3271 MiB 降到 2822 MiB。
已写入 D:\AI_Tools\llama-server.cmd(第 4 个参数可覆盖,默认 1)。
-np 1 加持下的上下文上限(80% 额度 + q8 KV,27B 实测)
省下的 449 MiB 直接变成了可用的上下文额度。逐档实测(“能加载 + 能出词”才算 PASS):
| 上下文 | 结果 | 备注 |
|---|---|---|
| 65536 | ✅ PASS | 非权重占用 2822 MiB |
| 131072 | ✅ PASS | 加载 56s |
| 196608 | ✅ PASS | 加载 46s |
| 212992 | ✅ PASS | 加载 67s ← 实测可用上限 |
| 229376 | ❌ FAIL | UR_RESULT_ERROR_OUT_OF_DEVICE_MEMORY |
| 262144 | ❌ FAIL | failed to allocate compute pp buffers(把 batch 降到 512 也失败) |
模型原生支持 262144(256K),但本机实测到 208K 到顶,差一档。 256k 需要约 10.5~11 GiB 的非权重分配,而 80% 额度只留出约 9 GiB。 (256k 失败时系统还空闲 25.93 GiB——再次说明卡的是显卡额度,不是内存。)
对比改注册表之前:同一个模型在 57% 下只能到 16k。现在是 208K,13 倍。
两个模型的上下文开销对比(都是 q8 KV + -np 1 + 80% 额度)
日常实际在用的 Qwen3.6-35B-A3B(qwen35moe,权重 15.46 GiB)逐档实测:
| 上下文 | KV buffer | RS buffer | 计算缓冲* | 非权重合计 |
|---|---|---|---|---|
| 4096 | 42.50 MiB | 62.81 MiB | 84.56 MiB | 190 MiB |
| 32768 | 340.00 MiB | 62.81 MiB | 196.56 MiB | 599 MiB |
| 65536 | 680.00 MiB | 62.81 MiB | 324.56 MiB | 1067 MiB |
| 131072 | 1360.00 MiB | 62.81 MiB | 580.56 MiB | 2003 MiB |
| 196608 | 2040.00 MiB | 62.81 MiB | 836.56 MiB | 2939 MiB |
| 262144 | 2720.00 MiB | 62.81 MiB | 1092.56 MiB | 3875 MiB |
* 计算缓冲 = SYCL compute + Host compute
两个关键结论:
① 35B-A3B 能用满它的原生 256K 上下文(加载仅 46 秒),全部非权重开销才 3.79 GiB; 而 27B 在 208K 就到顶了(原生 256k 差一档)。日常用 35B-A3B 就没有这个遗憾。
② 为什么差这么多——看 llama.cpp 打印的这行:
35B-A3B: KV buffer size = 680.00 MiB (65536 cells, 10 layers, 1/1 seqs)27B: KV buffer size = 2176.00 MiB (65536 cells, 16 layers, 1/1 seqs)35B-A3B 的 40 层里只有 10 层有 KV cache,其余 30 层是线性/循环状态(ssm.state_size = 128);
27B 的 65 层里有 16 层是全注意力,且每层的 KV 头数更多。结果就是 KV 每 token 便宜 3.2 倍
(35B-A3B 约 10.6 KiB/token,27B 约 34 KiB/token),RS buffer 也小 2.4 倍(62.81 vs 149.62 MiB)。
同上下文下的横向对比(128k):
| 模型 | KV | 计算缓冲 | 非权重合计 | 能否到 256k |
|---|---|---|---|---|
| Qwen3.8-27B(dense 27B) | 4352 MiB | 580 MiB | 5.2 GiB | ❌ 只能到 208k |
| Qwen3.6-35B-A3B(MoE 3B 激活) | 1360 MiB | 580 MiB | 2.0 GiB | ✅ 能用满 256k |
所以日常首选 35B-A3B 不只是因为它快 5 倍(23 vs 4.5 tok/s),它的上下文开销也只有 27B 的 40%。
⚠️ 本文早期版本的方法论错误(已修正)
早期版本写过”27B 从 4096 → 16384 只多占 0.6 GiB""35B-A3B 开 32768 只要 0.3 GiB”。 那些数字是从系统空闲内存的差值推出来的,属于错误方法:本机每次测试时内存负载都在 95~97%, Windows 会持续压缩内存、回收待机缓存来维持”空闲”数值,因此该指标根本测不出真实增量 (三次读数 1.15 / 0.56 / 0.81 GiB 的差异本身就在噪声范围内)。
改用 llama.cpp 自身的 buffer 分配明细(
-lv 5)重测后,4k→64k 的真实代价是 2.33 GiB, 是早期说法(0.3~0.6 GiB)的 4~8 倍。上表中的数字全部来自新方法。
超了会怎样:从 23 tok/s 掉到 1 tok/s 以下
一旦内存不够,超出部分会落到页面文件(C 盘)。而生成每一个 token 都要读取全部活跃权重, 只要权重有一部分在磁盘上,每一步都要等磁盘 I/O:
| 场景 | 实测速度 |
|---|---|
| 权重完全在内存里(Qwen3.6-35B-A3B) | 23~24 tok/s |
| 27B dense(同量级权重,架构不同) | 4.5 tok/s |
| 权重溢出到页面文件(推算) | < 1 tok/s,且硬盘持续读 |
本机 C 盘是 SSD,但即便是 SSD,随机读 15 GB 权重也意味着每个 token 要等几百毫秒到数秒。
一个补充:MoE 模型(如 35B-A3B,每 token 只激活约 3B 参数)对这种溢出的耐受度会略好—— 因为它每次只碰一小部分专家权重,被换出的专家可能不会立刻被用到。 但这是”能不能勉强跑”的差别,不是”能不能跑得好”的差别,不建议作为方案依赖。
结论:显存提升到底买到了什么
| 改前 57% | 改后 80% | 变化 | |
|---|---|---|---|
| 显卡提交额度(名义) | 18.0 GiB | 25.2 GiB | +40% |
| 驱动上报给 torch 的额度 | 16.46 GiB | 16.46 GiB | 无变化 |
| 实测可分配显存 | ~16 GiB | 20.50 GiB | +28% |
| 能加载的模型权重上限 | ~15.3 GiB | ~15.5 GiB | 几乎没有变化 |
| 能开的上下文长度 | 4096 | 16384+ | 提升 4 倍 |
所以这次调优换来的不是”能装更大的模型”,而是”同一个模型能开更长的上下文、更大的计算缓冲”。
原因很直白:显存额度的水龙头拧大了,但水池(物理内存 31.55 GiB)没有变大, 而池子里还要装 Windows、驱动、SYCL 运行时、KV cache 和计算缓冲。
实操建议
- 想跑更大的模型,唯一的硬解法是换内存更大的机器(>48 GiB,最好 64 GiB)。 本机内存是板载焊接的,无法加装,这是物理限制,任何软件调优都绕不过去。
- 在这台机器上的安全预算:模型权重 ≤ 15.5 GiB。已实测跑通的最佳选择是
Qwen3.6-35B-A3B-UD-Q3_K_M.gguf(15.46 GiB,SWE-bench Verified 73.4,23 tok/s)。 - 跑模型前关掉浏览器:加载后只剩 0.7 GiB 空闲,一个 Edge 窗口就能把它推入换页。
- 上下文按需给,它是要花显存的:27B 从 4k 开到 64k 要多花 2.33 GiB 设备内存 (KV 约 34 KiB/token)。日常用 8k~16k 即可,64k 只在真需要啃长文档/长代码库时打开。
- 用
-np 1启动(已设为默认):单用户可省 449 MiB 固定开销,等价于白送约 13k token 的上下文额度。 只有确实需要并发请求时才调大第 4 个参数。 - 想回滚:双击
D:\AI_Tools\igpu-shared-restore-57.reg并重启。
附录:本次用到的验证命令
:: 1. 查当前共享显存比例reg query "HKLM\SYSTEM\CurrentControlSet\Control\GraphicsDrivers\MemoryManager" /v SystemPartitionCommitLimitPercentage
:: 2. 阶梯分配测试,测出实际能分配多少显存(参数1=测试上限GiB,参数2=系统剩余内存安全阈值GiB)D:\AI_Tools\unsloth\run.cmd scripts\check_xpu_memory.py 22 1.2
:: 3. A/B 判决测试:在任意共享显存比例下自动测多个上文档位(PASS/FAIL + 出词验证)D:\AI_Tools\unsloth\run.cmd scripts\test_ctx_load.py 16384 32768 65536
:: 4. 【推荐】读 llama.cpp 自己报的 buffer 分配明细(比看系统空闲内存可靠):: 会依次用 -lv 5 启动各上下文,抓取 KV / RS / compute buffer 的真实大小D:\AI_Tools\unsloth\run.cmd scripts\probe_buffers.py 4096 16384 32768 65536
:: 5. 手动看某个上下文下的分配明细"<llama.cpp>\build\bin\llama-server.exe" -m <模型> -ngl 99 -c 65536 ^ --cache-type-k q8_0 --cache-type-v q8_0 -lv 5 --port 8098:: 关注这几行:KV buffer size / RS buffer size / compute buffer size
:: 6. 看系统真实内存占用(含进程外的部分):: 进程工作集合计 vs 系统实际占用,两者差额即为驱动/内核占用Get-Process | Measure-Object WS -Sum附录 B:两个容易踩的方法论坑
-
不要用”系统空闲内存”推断显存占用。 本机内存负载常年 95~97%,Windows 会靠压缩和回收待机缓存 把”空闲”维持在近似水平,导致增量被吃掉、看起来”几乎不涨”。要看 llama.cpp 的 buffer 明细。
-
不要把驱动上报的显存值当成上限。
llama-ls-sycl-device或启动横幅里的数字是get_global_mem_size()/1000000(SYCL 静态属性,十进制 MB),改注册表也不会变; 真正的闸门由SystemPartitionCommitLimitPercentage控制,两者不是一回事。
本文所有数字均为本机实测值,非估算或引用。
这篇文章是否对你有帮助?
发现错误或想要改进这篇文章?
在 GitHub 上编辑此页