OpenCode 夜间代码改进:96 消息/54 工具的深度优化实践
本文最后更新于 2026年7月18日 凌晨
凌晨两点,我盯着 OpenCode 的终端,96 条消息排队等待处理,54 个工具调用在后台疯狂争夺显存。
模型开始胡言乱语,生成的代码越来越离谱——这不是 bug,而是系统在超负荷下崩溃的前兆。如果你也经历过类似的“深夜翻车”,这篇文章就是为你准备的。
前置条件
这次优化在 ModelBase 服务器上完成,硬件配置比较特殊,提前列出来,避免你照着教程踩坑。
- 服务器:ModelBase(内网服务器)
- 显卡:2× RTX 2080 Ti 魔改版,单卡 22 GB 显存,双卡总计 44 GB(官方版是 11 GB,魔改版翻倍,别搞错)
- 推理引擎:llama.cpp,models-preset 多模型模式
- 代理层:new-api(内网代理)
- 当前模型:Qwen3.5-35B-A3B-Ornith,Q4_K_M 量化,实测速度 104 t/s(长输出场景)
整体架构
OpenCode 的核心是一个消息循环加工具调度器。消息来自 IDE 插件、终端交互和自动化脚本,工具负责代码补全、重构、单元测试等具体任务。默认配置下,每次消息批次大小固定为 32,工具并发上限为 16——这在显存充足时过于保守,浪费了双卡 44 GB 的潜力。
我们的目标是把这两个阈值分别提升到 96 和 54,让系统在夜间批量处理任务时把吞吐量压到极限,同时保持稳定。
核心流程拆解
消息批量读取:从 32 到 96
原始代码在 message_loop.cpp 中使用固定大小的队列读取:
1 | |
我们把保留容量改为 96,并加入自适应退出条件——当队列连续两次读取不到新消息时,提前结束本轮批次,避免空闲时无谓等待。
1 | |
工具并发调度:从 16 到 54
工具调度原来用硬编码的信号量控制并发数:
1 | |
我们改为可配置的 54,并在启动时从环境变量读取,方便不同机器灵活调节:
1 | |
任务分发时,每拿到一个工具就 acquire,完成后 release,最多允许 54 个工具同时在两张显卡上跑批量推理。
显存监控:防止溢出
批量增大后,显存压力随之上升。我们在每轮推理前后检查显存占用:
1 | |
得益于 22 GB 单卡显存,跑 Qwen3.5-35B-A3B-Ornith 时单卡占用约 18 GB,仍有足够空间给批次扩展。
源码关键片段
实际生效的配置放在 opencode.yaml 中:
1 | |
重启服务后,日志输出确认配置生效:
1 | |
设计思想
批量与并发的平衡:单纯增大批量会导致显存压力,单纯提升并发会增加上下文切换开销。通过显存监控动态调节批量,保证在硬件极限附近运行。
自适应退出机制:消息队列在夜间往往呈 bursty 特征,长时间空闲会导致无谓等待。连续两轮读取不到新消息就提前结束批次,既保证了高峰期的吞吐,又避免了低谷期的资源浪费。
可配置而非硬编码:所有阈值均可通过环境变量或配置文件调节,便于在不同机型上复用——比如以后升级到 40 GB 显存的卡,只需改一行配置。
引申思考
- 更细粒度的调度:目前所有工具被看作同类资源,但实际工具之间的消耗差异很大(单元测试生成比代码补全更显存敏感)。后续可以引入工具级别的显存配额,实现更精细的调度。
- 异步流水线:把消息读取、模型推理和结果回写分离到不同的线程或协程,有可能进一步降低延迟。
- 异构利用:当前 Turing 架构不支持 FP8/BF16 原生,但通过 Q4_K_M 量化已经能获得接近半精度的吞吐。如果换成支持原生 FP8 的卡,同样的批量设置有望再翻倍。
参考文献