Deepseek-Harness 单卡(7900XTX)运行Qwen3.8-27B 完整文档-3
-
6. 性能测试
6.1 测试脚本
存放到
~/llama-bin/bench.py:#!/usr/bin/env python3 import time, json, urllib.request, concurrent.futures, statistics API = "http://localhost:8090/v1/chat/completions" MODEL = "qwen3.8-27b" def chat(prompt, max_tokens, temp=0): payload = {"model": MODEL, "messages": [{"role": "user", "content": prompt}], "max_tokens": max_tokens, "temperature": temp, "stream": True} req = urllib.request.Request(API, data=json.dumps(payload).encode(), headers={"Content-Type": "application/json"}) start = time.time(); first = None; tok = 0 try: with urllib.request.urlopen(req) as r: for line in r: line = line.decode().strip() if line.startswith("data: "): d = line[6:] if d == "[DONE]": break try: o = json.loads(d) c = o.get("choices", [{}])[0].get("delta", {}).get("content") if c: if first is None: first = time.time() tok += 1 except: pass except: pass total = time.time() - start ttft = first - start if first else 0 return ttft, total, tok def sec(t): print(f"\n{chr(61)*64}\n {t}\n{chr(61)*64}") # ① 吞吐量 sec("① 吞吐量测试") tr = [] for label, prompt, mx in [ ("短输出 100", "用一句话介绍人工智能。", 100), ("中输出 500", "请写一段关于春天的短文,约200字。", 500), ("长输出 1000", "请写一篇关于AI发展历程的文章,尽量详细。", 1000), ("超长 2000", "请详细解释量子计算原理,写3000字文章。", 2000), ]: ttft, total, tok = chat(prompt, mx) gt = total - ttft tps = tok / gt if gt > 0 else 0 tr.append({"l": label, "ttft": ttft, "tps": tps}) print(f" {label:<12} TTFT:{ttft*1000:>6.0f}ms {gt:>6.2f}s {tok:>5}tok {tps:>6.1f}tok/s") print(f" 平均: {statistics.mean(r['tps'] for r in tr):.1f} tok/s") # ② 并发 sec("② 并发测试") for n in [2, 4, 8]: def w(i): return chat(f"{i}+{i}等于多少?", 50) s = time.time() with concurrent.futures.ThreadPoolExecutor(max_workers=n) as p: results = [f.result() for f in [p.submit(w, i) for i in range(n)]] wall = time.time() - s total_tok = sum(r[2] for r in results) print(f" 并发={n} 耗时:{wall:.2f}s tokens:{total_tok} 吞吐:{total_tok/wall:.1f}tok/s") # ③ 稳定性 sec("⑥ 稳定性测试 (5次)") st = [] for i in range(5): ttft, total, tok = chat("用一句话介绍机器学习。", 100) gt = total - ttft tps = tok / gt if gt > 0 else 0 st.append({"ttft": ttft, "tps": tps}) print(f" 第{i+1}次: TTFT:{ttft*1000:.0f}ms {tok}tok {tps:.1f}tok/s") print(f" TTFT: min={min(r['ttft'] for r in st)*1000:.0f} max={max(r['ttft'] for r in st)*1000:.0f}") print(f" 速度: min={min(r['tps'] for r in st):.1f} max={max(r['tps'] for r in st):.1f} avg={statistics.mean(r['tps'] for r in st):.1f}")6.2 实测结果
吞吐量:
输出长度 TTFT 生成耗时 Tokens 速度 短 (100) 5132ms* 0.58s 32 55.2 tok/s 中 (500) 619ms 2.62s 147 56.2 tok/s 长 (1000) 623ms 17.35s 1000 57.7 tok/s 超长 (2000) 660ms 34.68s 1839 53.0 tok/s *首次含冷启动加载,后续 TTFT 稳定 ~600ms。平均 55.5 tok/s
并发:
并发数 总耗时 总Tokens 吞吐 2 1.49s 7 4.7 tok/s 4 2.64s 19 7.2 tok/s 8 4.39s 46 10.5 tok/s 并发下 TTFT 线性增长(串行处理,--parallel 1)
温度影响:
温度 TTFT 速度 0 333ms 53.4 tok/s 0.5 451ms 43.0 tok/s 1.0 450ms 46.2 tok/s 长上下文:
上下文 TTFT 速度 短 (~200字) 622ms 46.4 tok/s 长 (~1600字) 1439ms 55.1 tok/s 稳定性 (5次相同请求):
指标 最小 最大 平均 TTFT 153ms 624ms 305ms 速度 48.7 52.7 51.8 tok/s 速度波动 <8%,非常稳定
口径说明:上表测于未启用 k4v 时期,且为纯文本负载。启用 k4v 后的解码表现见 §6.3。
两组数字不可直接比较——负载类型不同(散文 vs 逐字重现)。
6.3 投机解码专项(k4v)
llama.cpp 的投机解码分支支持多个投机器同时启用,
--spec-type吃逗号清单。
在原有draft-mtp(模型内建 MTP 头自投机)之上叠加ngram-map-k4v(从已生成上下文做
n-gram 查表,命中一次吐最多 48 个 draft token),二者互补:n-gram 命中时是"猜一大段",
没命中就退回 MTP,所以不会拖慢其他内容。--spec-type ngram-map-k4v,draft-mtp \ --spec-draft-n-max 3 \ --spec-ngram-map-k4v-size-n 32 \ --spec-ngram-map-k4v-size-m 48 \ --spec-ngram-map-k4v-min-hits 1零 VRAM 成本、零重编,已在
llama.sh中默认开启(K4V=0可关)。实测结果
方法:temp=0、每项 2 轮取中位数、读
timings.predicted_per_second。
两配置n_gen完全一致(407/365/500/271/630),故无「输出长度不同造成的假信号」。工作负载 仅 draft-mtp +k4v (n=32) 变化 接受率变化 code_refactor86.1 112.6 +30.7% 1.00 → 0.96 repeat_synth86.0 122.4 +42.3% 1.00 → 0.99 template_batch47.2 46.7 −1.1% 0.37 → 0.37 reasoning72.9 72.1 −1.1% 0.79 → 0.79 prose47.2 46.7 −1.1% 0.38 → 0.38 负项均落在 ±1.1%,与同配置重跑的噪音水平一致。经 systemd 重启后复验:112.3 / 122.7。
两个反直觉点
- 接受率下降不是退化。
code_refactor接受率 1.00 → 0.96 看着像变差,实则
草稿 token 从 306 涨到 400、总吞吐 86 → 112。接受率必须与drafted一起看,
孤立看会误判(参见 §9.6 记录的同类教训)。 - 收益来自「逐字重现」,不是「格式相似」。
你的任务长这样 k4v 收益 让模型把整份文件/文章原样吐回、只改几行(改代码、翻译校对、逐条批注) +30~60%,最值钱 大量重复的结构化输出(JSON/YAML/SRT/表格批次) 中等 逐条照抄原文再加判定(规格检查、RAG 引述) 小幅或持平 格式固定但内容全新(套模板产新数据) 持平( size-n取 12 时倒扣)纯创作、思考链、对话 无感(也不会变慢) 对 dsh 而言第一行才是主场:agent 改文件时经常要把整个文件回吐一遍,这就是 +30.7% 那档。
为什么
size-n必须是 32size-n决定「要比对多长的前文才算命中」。拉长 = 更严格 = 少开火但更准。size-n code_refactor template_batch 字幕重排类 合成重复 关闭 86.1 47.2 106.1 86.0 12 166.4 68.4
−5%95.8
−10%178.6 24 169.1 70.2 100.9 
— 32 157.0 71.1 105.4 237.0 40 136.0 71.9 104.8 236.9 (上表为来源帖在 RTX 4080S / 7900 XTX 上的扫描值,用于说明趋势,本机仅实测 n=32 一档。)
size-n太小 → n-gram 一直开火、一直猜错,白做的 draft 计算比不开启还多。
典型反例是"文字照抄、时间轴改掉"的字幕重排:n-gram 看到前文就把旧时间戳一起预测出来,
每到时间轴就撞墙,draft 992 个只接受 510。格式重复但内容要换的任务,n-gram 是负资产。不要动
min-hits来源帖试过
--spec-ngram-map-k4v-min-hits 2(要求 n-gram 出现两次才采用),
结果各项 draft 计数与完全关闭 k4v 逐项相同——等于直接关掉功能。保持 1。不要设
p_min,n_max保持 3n_max=5+p_min=0.4在来源帖实测中「重复内容看起来更快,但推理 −22%、散文 −37%」:
draft 猜长了接受率上不去(散文只有 0.39),每步都在做白工。n_max越大,
可预测内容越快、不可预测内容越慢——纯创作偏多用 3,结构化输出偏多可试 4~5。来源:
https://lcz.me/topic/1398(7900 XTX / Vulkan 实测报告,附 RTX 交叉验证)
7. 日常运维
7.1 常用命令
# 启动模型(自动带 shim;有 systemd 单元且无覆盖变量时自动走 systemctl) ~/llama-bin/llama.sh 38 # systemd 托管时的启动/重启(推荐;走 llama.sh 38 --fg,会继承脚本内全部参数) systemctl --user start qwen38 systemctl --user restart qwen38 systemctl --user stop qwen38 # 手动停,Restart=on-failure 不会拉回 # 查看状态 ~/llama-bin/llama.sh status # 停止 ~/llama-bin/llama.sh stop # 切换模型 ~/llama-bin/llama.sh 36 --use # 临时覆盖参数(单次生效) K4V=0 ~/llama-bin/llama.sh 38 # 关掉 ngram-k4v,只留 MTP NMAX=2 CRAM=0 ~/llama-bin/llama.sh 38 # 关掉 KV 驻内存 CTX=65536 ~/llama-bin/llama.sh 38 # 加大窗口前先读 §9.8:逼近上限会掉进 GTT # shim 单独管理 ~/llama-bin/shim.sh status ~/llama-bin/shim.sh restart # 查看日志 tail -f /tmp/qwen38-vulkan.log # llama.cpp tail -f /tmp/dsh-llm-shim.log # shim journalctl --user -u qwen38 -f # systemd 下 llama.cpp 的日志改
llama.sh后不需要动 systemd 单元 —— 单元的ExecStart是llama.sh 38 --fg,
会自动继承脚本里的新参数。systemctl --user restart qwen38即可生效。7.2 健康检查
# llama.cpp curl -s http://127.0.0.1:8080/health | python3 -m json.tool # shim curl -s http://127.0.0.1:8090/health | python3 -m json.tool # 模型列表 curl -s http://127.0.0.1:8090/v1/models | python3 -m json.tool # 快速测试 curl -s http://127.0.0.1:8090/v1/chat/completions \ -H "Content-Type: application/json" \ -d '{"model":"qwen3.8-27b","messages":[{"role":"user","content":"你好"}],"max_tokens":50}'
8. 故障排查
8.1 常见问题
症状 原因 解决 shim: UPSTREAM_UNREACHABLE llama.cpp 未启动 ~/llama-bin/llama.sh 38 CONTEXT_OVERFLOW 上下文超限 /compact 压缩或新开会话 工具调用参数错乱 绕过 shim 直连 llama.cpp 确保 baseURL 指向 :8090 模型跑飞 (32k token) llama.cpp 解析器 bug 必须经 shim,不能直连 端口冲突 8080/8081/8090 被占用 改 P38_PORT/SHIM_PORT 环境变量 显存不足 OOM 两模型同时运行 只能跑一个,先 stop 再启另一个 MTP 头被忽略 --spec-type 未生效 检查日志 grep "nextn.*ignoring" 8.2 日志位置
服务 日志文件 llama.cpp 3.8 /tmp/qwen38-vulkan.log llama.cpp 3.6 /tmp/qwen36-vulkan.log shim /tmp/dsh-llm-shim.log DSH ~/.dsh/logs/ 8.3 完全重置
# 停止所有服务 ~/llama-bin/llama.sh stop ~/llama-bin/shim.sh stop # 验证端口已释放 ss -tlnp | grep -E "8080|8081|8090" # 重新启动 ~/llama-bin/llama.sh 38
9. 诊断与实战记录
9.1 直连失败的具体表现与根因
直连 llama.cpp 的 OpenAI 端点时:
assistant/message: content: [] stopReason: "length" usage: output 32768 token 耗时 7.7 分钟 → 0 可用输出根因不在模型,而在 llama.cpp b11223 自带的 tool-call 解析器(该 build 报
chat_format: peg-native)。用/apply-template+/completion绕过解析器取模型原始输出:<tool_call> <function=bash> <parameter=command> ls -1 /home/hnz/桌面 | wc -l </parameter> <parameter=description> Count entries in Desktop directory </parameter> </function> </tool_call>49 token、格式完全正确、正常 EOS。但同一个请求走
/v1/chat/completions(带tools)时,解析结果是:{"command": "ls -1 /home/hnz/桌面 | wc -l </parameter> <parameter=description> ... </tool_call>\n<tool_call>..."}即第二个参数起的整段被吞进第一个参数的值里;而且这种失败会诱发模型把同一个
<tool_call>重复几百遍直到打满max_tokens。模型自带模板明确允许参数值跨行("that can span multiple lines"),解析器却处理不了。对照实验(同一份真实 DSH 请求,24 个工具):
路径 结果 直连 :80810/10 成功,每次跑飞约 30s / 满 max_tokens经 shim 10/10 成功,每次约 1s / 49~51 token 采样侧只能缓解(
--dry-multiplier 0.8能让它不再无限重复,但参数照样被解析坏),换 build 可能有效但会丢掉这套 MTP 调优(69 tok/s),所以选了中间层方案。
后续复核推翻了这组对照(2026-09-29,见 §1.2 与 §9.7):当前配置下直连可正常完成
工具调用,0/10 复现不出来。下文保留为历史记录,不代表当前实际行为。该故障的现场记录已归档:本目录
local_model.sh.txt
(模型输出的原始抓取,含 4058 个零宽字符 U+200B 残留的重复<tool_call>标签,
bash -n直接语法错误 —— 正是「重复到打满max_tokens」的产物)。对照另一个模型:
qwen3.8-27b(2026-09-29 实测,当时n_ctx还是 262144;现已改为 131072,见 §9.8)经 shim 的干净工具调用 5/5、每次 1~2 秒;纯文本回复在流式与非流式下都与直连逐字节一致(temperature 0+ 固定 seed 对齐)。3.6/3.8 两个实例的严格模式提示双向验证通过。9.2 验证记录
- 解析单测
node ~/llama-bin/shim-parser-test.mjs→ 7/7(含<bash>漏写function=的容错)。 - 真实请求重放 10/10 干净
tool_calls,每次 1s。 - headless 端到端(
dsh --profile headless --patch dsh-shim-overlay.yml "…"):- 单步任务:
tool-call bash → "3" → 最终回答 → turn/end: completed - 多步任务:一轮里并行发出 3 个 bash + 1 个 grep,再补一次 bash,最终答案
(9 个文件 / 1 个子目录 /SHIM_PORT默认 8090)全部正确。 - 缓存命中正常(cacheRead 4928 / 5402 token)。
- 单步任务:
- 失败路径:上游不可用时返回
HTTP 502+ 明确错误信息;/health只反映 shim 自身。 - k4v 启用后的复验(2026-09-29):经 systemd
llama.sh 38 --fg启动后,
投机解码基准复现手工启动的数值(112.3 / 122.7),dsh 经 shim 端到端冒烟正常。 - 默认模型切换:
dsh-shim-overlay.yml的默认模型已由qwen3.6-27b改为qwen3.8-27b,
与llama.sh默认保持一致。改前若 3.6 未启动,shim 会返回
502 UPSTREAM_UNREACHABLE并提示当前实际在跑哪个实例。
9.3 修复记录(都是 shim / 脚本侧的问题,DSH 走流式不受影响)
症状 原因 修法 非流式纯文本回复只剩最后 ~11 个字符 防标签跨 chunk 的 sent指针在非流式路径也前进,但那条路并不发送只在真正流式时推进 sent工具调用之前的正文被丢掉 流式分支没把保留尾巴里的正文发出去 发 tool_calls前先补发prefixTextshim.sh stop误杀无关进程(连调用它的 shell 都被杀过)pkill -f "dsh-llm-shim.mjs"按文件名裸匹配,vim 该文件也会中招启动写 PID 文件,停止时按 PID + 只认 argv[0]含node的进程llama.sh status偶尔显示「停止」而模型其实在跑pgrep 在某些环境下看不到别的进程 状态行加端口健康检查兜底 9.4 上下文限制与超限处理
- 本地模型的上下文比云端小得多,切模型前要看一眼历史长度。
qwen3.8-27b的
n_ctx现在是 131072(不再是 262144,见 §9.8),maxTokens: 32768,
所以留给 prompt 的预算约 9.8 万 token——本地会话比想象的短得多。
把一个已经很长的云端会话直接切到本地,llama.cpp 会回400 exceed_context_size_error。正确做法:- 另开一个新会话用本地模型(最稳);或
- 先在当前会话
/compact压缩,再/model切到本地; - 确实需要更长窗口,见 §9.8 关于
CTX与显存的取舍,不要盲目调大。
现在这种情况会明确报CONTEXT_OVERFLOW并给出 token 数,不会再静默无响应。
实测 DSH 侧呈现为dsh: INVALID_REQUEST: 400: 本地模型上下文不足:请求 N token,而模型 ctx 只有 131072。请在 DSH 里 /compact 压缩会话,或另开一个新会话。,
并且不再重试 5 次(INVALID_REQUEST不在llm-retry的重试类别里,立刻失败并给出原因)。
超限之后的"压缩死循环"(2026-09-29 实测):会话一旦超过本地窗口,DSH 会自动
触发压缩,但压缩请求本身也要把整段历史发给同一个模型,于是照样超限:
而13:08:09 provider=local model=qwen3.8-27b ctx=262144 ← 当时的配置,现为 131072 13:08:11 compaction/start 13:08:13 compaction/end error: 400 请求 330277 token > ctx 262144 13:08:15 turn/end error: 400 CONTEXT_OVERFLOW(请求 329927 token)/compact不接受参数、用当前选中的模型,所以在本地模型上手动压缩同样会失败。
正确顺序(二选一):- 另开新会话再选本地模型(已验证:provider=local/qwen3.8-27b,2 次工具调用、
turn/end: completed,prompt 仅约 5.5k token); - 想保留本会话:先
/model切回 deepseek-flash → 发一条消息让切换生效 →/compact
(云端 100 万窗口才压得动)→ 确认压缩成功后,再/model切回本地。
注意切换生效前触发的压缩仍会用旧模型(实测踩过),所以要先发一条消息。
- 另开新会话再选本地模型(已验证:provider=local/qwen3.8-27b,2 次工具调用、
9.5 日常使用注意
- 一条命令就够:
llama.sh 36|38在模型就绪后会自动shim.sh start(幂等,已在跑就跳过);
llama.sh stop与llama.sh 36 --use会连带停掉 shim。llama.sh status也会打印 shim 状态行。
(改动前已备份为~/llama-bin/llama.sh.bak-*、shim.sh.bak-*。) - 单独控制 shim:
~/llama-bin/shim.sh start|stop|restart|status。
升级过dsh-llm-shim.mjs之后要shim.sh restart才会生效(下次llama.sh 36|38起模型也会自动重启它)。 - 端口占用兜底:若 :8090 已被别的实例服务(例如别的终端拉起的),
shim.sh start会跳过启动
而不是抢端口;llama.sh status/shim.sh status也会把这种情况算作“运行”。 - 模型要对上:选 3.6 就得跑
llama.sh 36,选 3.8 就得跑llama.sh 38(两个实例互斥)。
对不上时 shim 会直接报错并说明现在跑的是哪个、该用哪条命令,不会拿另一个模型顶替。
llama.sh 36 --use切换模型时 shim 不用重启(它按模型名转发)。 - shim 没启动时本地路由会 502;此时 GUI 里
/model切回deepseek-flash即可。 - 本会话里曾临时拉起过一个 shim 实例;
llama.sh stop/shim.sh stop都能把它停掉,
之后由llama.sh正常接管。 - 日志:
/tmp/dsh-llm-shim.log(SHIM_VERBOSE=1时记录每次解析结果与重试)。
9.6 治本方向(上游修复)
- 换/升级 llama.cpp build —— 该 build 的 PEG 工具解析器是根因;上游修好后 shim 可以直接摘掉。
- 若换 build:本方案与 MTP 调优无关,shim 不依赖
--spec-type draft-mtp。 - 上报点:模板文档明示参数值可跨行,而 PEG 解析器处理不了跨行值,且失败会诱发重复生成。
- 另外值得反馈 DSH 上游:从大窗口模型(deepseek 100 万)切到小窗口模型(本地 26.2 万)时,
自动压缩会用切换前的模型调度、且压缩请求本身可能超过目标窗口 —— 于是"越超限越压不动"。
建议改成按目标模型窗口判断、并允许指定压缩用的模型。
9.7 补充复核:shim A/B 与多轮稳定性
2026-09-29 在当时配置(Qwen3.8-27B @262144、k4v 开启)下重做 A/B,
两份 overlay 仅baseURL端口不同。A/B 结果
路径 结果 经 shim :809066 个文件 / ubuntu-ai✓直连 :808066 个文件 / ubuntu-ai✓(并多解释了find -type f的口径)coerce()类型强转专项shim 的
coerceCall()只在模型把数字/布尔发成带引号字符串时才触发修补
(dsh-llm-shim.mjs中coerce():number→Number()、布尔映射true/yes/1
false/no/0、
array/object→JSON.parse(),且仅处理typeof v === "string")。为最大化触发机会,构造 7 字段全部 required 的工具
(string / integer / integer / boolean / number / array / object),逼模型逐个输出:路径 样本 类型全对 字符串化 直连 :808012 12/12 0 经 shim :80906 6/6 0 直连返回
{"task_name":"backup","delay_ms":1500,"repeat":3,"enabled":true,"threshold":0.75,"tags":["system","disk"],"opts":{"mode":"full"}}
—— 全是原生 JSON 类型,18 次采样零次需要coerce修补。这不能证明
coerce永远无用。 它是"模型偶尔把数字发成字符串"的兜底,
而本次采样温度固定 0.3、提示固定,输出高度一致,未触发该边缘情况。
真实长会话的输出分布更杂。建议保留 shim、切直连观察几天,再决定是否下线。多轮对话不存在「越聊越慢」
社区方案建议加稳定性四件套
--no-kv-unified --parallel 1 --ctx-checkpoints 2 --cache-ram 4096来修多轮掉速。
本机 4 轮实测(cache_prompt:true)显示该问题不存在:轮次 cache_n(复用前缀)prompt_n(本轮新算)decode t/s 1 0 67 64.1 2 366 60 66.4 3 725 49 70.9 4 1073 47 64.8 第 2 轮起每轮只重算 47~67 个 token,decode 无衰减(甚至微升)。
原因:本机一直是--parallel 1,单槽下不存在kv_unified跨槽共享导致的状态失效。
那套参数「免费」的前提是解决一个本机没有的问题,故不加。复现命令
DSH=~/.npm/_npx/1e7f6d9597241db0/node_modules/.bin/dsh # 两份 overlay 仅 baseURL 端口不同(:8080 / :8090),其余完全一致 $DSH --profile headless --patch ~/llama-bin/dsh-shim-overlay.yml \ headless "统计 ~/llama-bin 下的文件数并读出 /etc/hostname" # 类型强转压测脚本原为 /tmp/opencode/coerce-test.py(临时目录,已随重启丢失) # 需要复现时按 §9.7 的 7 字段 schema 描述重写即可9.8 CTX 从 256K 降到 128K 的根因(Vulkan GTT 回落)
2026-09-30 发现
llama.sh 38慢到不可用(decode 0.49 tok/s),根因不是 shim、不是投机解码,
而是**-c 262144把权重从显存挤进了 GTT**。机制:模型权重 16.4GB + 256K 的 KV cache ≈ 26.3GB,超出 25.75GB 显存在 DEVICE_LOCAL 上
能分配到的额度,vkAllocateMemory失败后 Vulkan 后端静默改用 GTT(系统内存)映射。
服务照常起来、/health报 ok、n_ctx也如实报 262144 —— 但每个 token 都要走 PCIe。指标 -c 262144-c 131072vis_vram_used567 MiB 权重完整驻留 gtt_used23490 MiB ~2.5 GB(KV) decode 0.49 tok/s 正常(数十 tok/s) prefill 30 t/s 400+ t/s 日志里的特征行:
print_timing: ... 0.49 tokens per second(decode 阶段)、
prompt processing ... 30.08 tokens per second(prefill 阶段)。"多分配的 KV 不花钱"是错的。 只有在 KV 仍能装进 DEVICE_LOCAL 时这句话才成立;
一旦溢出到 GTT,代价是每个 token 都走系统内存带宽。换句话说,判断依据是"装不装得下",
不是"填不填得满"。显存扫描(
-cram -1下,KV 在系统内存,只算权重+运行时开销):ctx 显存占用 备注 32K ~17.4 GB 安全 128K ~21.2 GB 当前默认,留 ~4.5 GB 余量 256K ~26.3 GB 爆,触发 GTT 回落 下界 64K:Hermes Agent 硬性要求
context >= 64000,低于此直接拒绝连接。顺带加了两个参数:
-cram -1:KV cache 显式放系统内存,不与权重抢 DEVICE_LOCAL。
对混合架构(本模型仅 16 层真注意力)代价很小,却能给权重留出余量。P38_CTX默认 131072:CTX=65536 ./llama.sh 38可临时覆盖,
但每次加大都必须重测llama.sh status的显存占用,逼近 ~24GB 时立刻退回。
P36_CTX仍是 196608,因为 Qwen3.6 的实测配置一直如此;若 3.6 也出现 0.49 tok/s,
按同样逻辑降级即可。教训:llama.cpp 的性能参数(
-c/-ngl/-ub)互相耦合,改任何一个都要
看最终显存落点(vis_vram_used/gtt_used),不能只看单个参数的账面数字。
相关实测记录见~/llama-bin/OPTIMIZATION.md。
10. 附录:文件清单
文件 用途 ~/llama-bin/llama.sh模型服务统一入口(36/38 启停、切换、状态;k4v 参数见 §6.3) ~/llama-bin/OPTIMIZATION.md推理性能优化完整记录(显存账、ubatch、prefill 曲线、decode 溯源、各外部方案复核) ~/llama-bin/dsh-llm-shim.mjsshim 本体 ~/llama-bin/shim.shstart/stop/restart/status(风格与llama.sh一致)~/llama-bin/shim-parser-test.mjs解析逻辑离线单测(7 项,改动后跑一下) ~/llama-bin/dsh-shim-overlay.ymlheadless 端到端验证用的 --patch覆盖层(默认qwen3.8-27b)~/llama-bin/dsh-direct-overlay.yml同上的直连版( :8080,无 shim),供 A/B 用~/llama-bin/bench.py性能测试脚本(第 6 章) ~/.config/qwen38/api.env服务端密钥(独立文件,不进脚本、不进 shell 环境) ~/.config/systemd/user/qwen38.servicesystemd 单元, ExecStart=llama.sh 38 --fg(改 llama.sh 会自动继承)~/llama-bin/llama.sh.bak-*/shim.sh.bak-*改动前的脚本备份 ~/文档/7900_dsh/README.md本文档目录的维护说明与归档清单 ~/文档/7900_dsh/local_model.sh.txt§9.1「无限重复 tool_call」故障的现场抓取(非脚本,不可执行) 目录 内容 --- --- ~/models/Qwen3.8-27B-UD-Q4_K_M.gguf、Qwen3.6-27B-Q4_K_M-mtp.gguf、mmproj-model-bf16.gguf~/.dsh/profiles/web/cordis.patch.ymlDSH provider 路由( local→:8090/v1)/tmp/qwen38-vulkan.log//tmp/qwen36-vulkan.logllama.cpp 日志 /tmp/dsh-llm-shim.logshim 日志( SHIM_VERBOSE=1时记录每次解析结果与重试)~/.dsh/logs/DSH 日志 - 接受率下降不是退化。