双卡 A100 + Ollama 吞吐调优`OLLAMA_NUM_PARALLEL`、上下文长度、KV Cache 与压测结果怎么一起看

张开发
2026/4/6 19:35:44 15 分钟阅读

分享文章

双卡 A100 + Ollama 吞吐调优`OLLAMA_NUM_PARALLEL`、上下文长度、KV Cache 与压测结果怎么一起看
一、先立一个正确的调优目标你要的是吞吐不是参数好看做本地推理服务时很多人调优的目标其实并不清晰。有的人想要的是单请求尽可能快首 token 尽量早点出来有的人想要的是单位时间内处理尽可能多的请求多用户同时进来时整体不抖这两种目标不是同一个方向。如果你要的是最低单请求延迟那很多时候更适合低并发短上下文模型常驻少排队如果你要的是最大整体吞吐那才需要开始考虑适度提高OLLAMA_NUM_PARALLEL把两张卡都吃起来合理放大队列控制上下文长度不让显存被无意义占满而你的场景前面已经定得很清楚了双卡 A100双实例部署目标是把整机吞吐压出来。那调优的核心就一句话不是让一个请求快到极致而是让每个实例在高并发时都处于“持续高效输出”的状态。Ollama 官方关于多 GPU 的说明也恰好支持这个思路如果模型能够完整放进任意一张 GPUOllama 会优先把模型加载到单张 GPU 上因为这样通常性能更好能减少推理时跨 PCIe 总线的数据传输只有当模型单卡放不下时才会分布到所有可用 GPU。也正因为这个机制在很多能单卡放下模型的场景里双实例双端口分流往往比“单实例看见两张卡”更适合做吞吐优先的服务。(Ollama 文档)二、先看最关键的核心公式并发和上下文不是叠加关系而是乘法关系这可能是整篇文章最重要的一句OLLAMA_NUM_PARALLEL和OLLAMA_CONTEXT_LENGTH不是简单相加而是一起放大内存需求。官方 FAQ 写得非常直接并行请求会让上下文大小按并发数放大例如2K context 4 个 parallel request 8K context额外的内存分配也会随之增长所需 RAM/VRAM 会按OLLAMA_NUM_PARALLEL * OLLAMA_CONTEXT_LENGTH线性放大。(Ollama 文档)这句话带来的实战含义非常大。例如你当前每个实例配置的是OLLAMA_NUM_PARALLEL4OLLAMA_CONTEXT_LENGTH8192那从内存占用角度看这个实例并不是“8192 上下文支持 4 并发”这么简单而是等价于在高并发时准备了承受4 × 8192的上下文压力。(Ollama 文档)所以你会发现一个很反直觉的现象有的人把parallel拉高吞吐反而下降有的人把上下文抬高显卡反而更忙但整体更慢有的人明明双 A100很快就开始排队根因往往不是卡不够强而是上下文和并发一起把显存预算吃爆了。这也是为什么很多调优失败的本质不是“参数不够大”而是“参数组合不对”。三、OLLAMA_NUM_PARALLEL不是越大越好它控制的是单模型的并发承载能力官方 FAQ 对OLLAMA_NUM_PARALLEL的定义很清楚它表示每个模型同时处理的最大并行请求数默认值为 1。(Ollama 文档)这意味着它解决的是“单个实例能同时扛多少请求”的问题而不是“服务是不是会更聪明”。在双实例场景下你其实有两层并发能力第一层是实例级并发。也就是你有两个端口1143411435两个实例本身就在并行工作。第二层是实例内部并发。也就是每个实例内部由OLLAMA_NUM_PARALLEL决定它能不能同时处理多个请求。(Ollama 文档)所以你调这个参数时应该遵循一个非常实用的原则1吞吐优先的起步值2 或 4如果你的模型已经常驻、上下文控制得比较克制、业务请求也偏中短文本那2或4往往是很合理的起点。这不是官方规定值而是基于官方给出的“并发 × 上下文放大内存需求”规则做出的工程化起始点。(Ollama 文档)2不建议一上来就 8因为只要你同时把上下文长度也设得不低显存压力会迅速抬高。一旦显存紧张Ollama 的表现就不会是“优雅变慢”而可能是请求排队模型装载竞争503 overloaded实例间时延抖动变大官方 FAQ 已经明确说明请求过多时会返回503 overloaded而OLLAMA_MAX_QUEUE只是决定最多排多少请求不会凭空增加吞吐。(Ollama 文档)所以调OLLAMA_NUM_PARALLEL最忌讳的不是保守而是不知道显存预算已经被上下文吃掉了多少。四、上下文长度不是“能开多大就开多大”而是要看你服务的任务类型很多人一看到自己的卡很大就忍不住想把OLLAMA_CONTEXT_LENGTH往上拉。Ollama 官方上下文长度文档确实写了默认规则 24 GiB VRAM默认 4k24–48 GiB VRAM默认 32k 48 GiB VRAM默认 256k同时官方也说明像 web search、agents、coding tools 这类需要大上下文的任务建议至少设置到64000 tokens。(Ollama 文档)很多人看到这里就会下意识得出结论我是 A100大卡默认都能到 256k那我就该开大。但这其实只说对了一半。因为官方同一页也明确提醒更大的上下文会增加运行模型所需的内存最佳性能应尽量避免把模型 offload 到 CPU可以通过ollama ps查看PROCESSOR和CONTEXT确认是不是100% GPU、当前实际上下文是多少(Ollama 文档)这意味着什么意味着“能开大”和“适合服务场景”不是一回事。什么时候适合开大上下文如果你的业务是长文总结多轮 Agent长代码分析大段 RAG 拼接结果后再推理那大上下文有价值。什么时候反而不该开大如果你的业务是普通问答短文本生成中等长度 RAG 最终回答API 服务型推理那过大的上下文很多时候只是在浪费显存预算压缩实例可承载的并发空间。所以对双 A100 双实例吞吐优先场景我更建议这样理解上下文它不是“越大越好”的能力参数而是“你愿意为单请求记忆窗口支付多少显存成本”的预算参数。如果你当前目标是吞吐8192或16384这种更克制的范围往往比一上来就上65536更现实。这个结论不是官方直接给出的固定数字而是基于官方对上下文默认值、显存消耗与 CPU offload 风险的说明做出的工程收敛。(Ollama 文档)五、OLLAMA_KEEP_ALIVE决定的不是“快不快”而是“会不会反复冷启动”Ollama 官方 FAQ 对模型驻留策略讲得非常清楚默认模型会在内存中保留5 分钟API 请求里可以通过keep_alive控制模型停留时间0表示响应后立即卸载任何负数都表示一直保留在内存中服务级也可以通过OLLAMA_KEEP_ALIVE设置默认行为(Ollama 文档)这意味着keep_alive本质上解决的是一个非常现实的问题你要不要让下一批请求继续复用已经加载好的模型。在吞吐优先场景下这几乎不用犹豫需要非常需要最好长期保持因为如果你不这么做就会出现一个非常讨厌的现象前几秒没流量模型被卸掉下一波流量一来又要重新加载压测结果里load_duration时高时低用户感觉系统“有时快有时慢”而官方 API Usage 文档明确告诉你响应里有load_duration字段它表示模型加载耗时。如果这个值在压测中反复冒高通常就说明模型没有稳定常驻或者刚刚经历了重新加载。(Ollama 文档)所以在双实例生产服务里keep_alive-1或OLLAMA_KEEP_ALIVE-1基本可以视为吞吐优先配置的标配。六、OLLAMA_FLASH_ATTENTION和OLLAMA_KV_CACHE_TYPE的真正价值在于“给并发和上下文让路”这两个参数很多人知道名字但不知道它们为什么重要。官方 FAQ 说得很清楚OLLAMA_FLASH_ATTENTION1可以显著降低上下文变大时的内存使用OLLAMA_KV_CACHE_TYPE默认是f16q8_0大约只占f16一半的内存而且通常几乎没有明显质量损失q4_0更省但精度损失会更明显(Ollama 文档)这两条放在一起看就会得到一个非常实战的结论Flash Attention 和 q8_0不是为了“看起来高级”而是为了把更多显存预算腾出来给并发和上下文。换句话说你不是因为想炫技才开它而是因为你想让每个实例在高并发时更稳尤其在双实例场景里这种价值更明显。因为你每个实例都要维持一份模型、一份上下文缓存、一套并发处理能力显存预算本来就很紧张。这时候Flash Attention 帮你压缩上下文增长带来的内存压力q8_0帮你把 KV cache 成本进一步降下来(Ollama 文档)所以我对这两个参数的建议非常明确吞吐优先推荐组合OLLAMA_FLASH_ATTENTION1OLLAMA_KV_CACHE_TYPEq8_0这几乎可以视为双卡服务型部署的默认起点。七、OLLAMA_MAX_LOADED_MODELS和OLLAMA_MAX_QUEUE一个控制显存竞争一个控制高峰缓冲这两个参数常常被忽略但在生产里都很关键。1OLLAMA_MAX_LOADED_MODELS官方 FAQ 说明它控制同时可加载的模型数量默认是3 × GPU 数量。如果有足够内存多个模型可以同时保持加载如果内存不够新的请求会排队旧模型空闲后可能被卸载以腾出空间。尤其在 GPU 推理场景下新模型必须能够完整装进 VRAM才允许并发加载。(Ollama 文档)对双 A100 的双实例固定模型服务来说这条规则给出的最佳实践其实很清楚如果你每个实例就是为了长期服务一个主模型那就把它压到 1。因为你根本不需要一个实例在繁忙时还去尝试同时装多个模型徒增显存竞争和加载抖动。2OLLAMA_MAX_QUEUE官方 FAQ 说明它控制服务繁忙时最多排队多少请求默认是512超过这个值Ollama 会返回503 overloaded。(Ollama 文档)这意味着它的作用不是“提高性能”而是给服务高峰提供一个缓冲区。调这个参数时要有一个很清醒的认识它不能创造吞吐它只能延后拒绝队列过大可能会把“快速失败”变成“长时间等待后失败”所以我的建议是中小规模服务可以先保留默认或略增双 A100 API 服务1024往往是一个还算稳妥的缓冲值再往上加之前先看业务能不能接受排队等待时间八、真正会看压测结果的人不是只看总耗时而是会拆 Ollama 返回的指标Ollama 官方 API Usage 文档已经给了你非常宝贵的一组指标total_durationload_durationprompt_eval_countprompt_eval_durationeval_counteval_duration并且所有时间值的单位都是纳秒。这些字段在非流式/api/generate或/api/chat响应里都能拿到流式响应则会在最后一个donetrue的 chunk 里给出。(Ollama 文档)这几项指标的意义非常大。1total_duration它表示整次响应的总耗时。这是最直观的总体指标但单独看它其实帮助有限因为你不知道慢在了哪里。(Ollama 文档)2load_duration它表示模型加载耗时。如果你已经做了常驻和预热这个值理应很低如果它经常偏高说明模型在重复加载或者实例状态不稳定。(Ollama 文档)3prompt_eval_count/prompt_eval_duration它们描述的是输入提示词处理阶段输入 token 有多少处理这些输入花了多久(Ollama 文档)如果这里的耗时很高通常说明你的输入太长了或者上下文过大导致提示词评估阶段变慢。4eval_count/eval_duration它们描述的是生成输出阶段生成了多少 token生成这些 token 花了多久(Ollama 文档)如果这里很高则更偏向“生成阶段慢”而不是装载或提示词处理阶段的问题。真正的调优应该是看这几项指标之间的组合而不是只盯着一个总时间。九、给你一套最实用的“压测结果判读法”情况一load_duration高后续明显下降这通常说明模型已经在变热是正常现象。你要看的不是第一次慢不慢而是后续是否稳定变低两个实例是否都一样/api/ps是否显示模型已经常驻只要后续稳定说明预热和keep_alive在发挥作用。(Ollama 文档)情况二prompt_eval_duration很高这通常说明问题不在模型加载而在输入本身。常见原因是RAG 拼接内容过长聊天历史过多OLLAMA_CONTEXT_LENGTH设置得过高导致提示词处理阶段负担变重因为上下文越大需要消耗的内存越多而并发还会把这种成本继续乘上去。(Ollama 文档)情况三eval_duration高但prompt_eval_duration不高这表示生成阶段本身更慢可能和输出 token 太多模型本身较重当前实例繁忙程度较高更相关。(Ollama 文档)情况四开始出现 503 overloaded这说明你已经把服务推到它当前承载边界了。这时应该优先做的是降低单实例并发压力检查是否所有请求都均匀分发到两个实例看上下文是不是设得过大看队列是否过长导致请求在服务里堆积而不是只会继续把OLLAMA_MAX_QUEUE往上抬。官方已经说明 503 是队列和忙碌状态触发的过载返回。(Ollama 文档)十、ollama ps不是可有可无它是你判断“有没有真吃满 GPU”的关键观察口官方上下文长度文档专门强调了一点为了获得最佳性能应尽量避免把模型 offload 到 CPU并可以通过ollama ps查看PROCESSOR字段确认是否是100% GPU同时也能看到当前分配的CONTEXT。(Ollama 文档)这意味着调优时你不该只看nvidia-smiAPI 耗时你还应该看ollamaps如果这里显示的不是100% GPU那说明你的配置可能已经把模型推到了不够理想的状态例如上下文过大模型过重显存不够参数组合导致 offload而一旦开始 offload 到 CPU吞吐表现通常就不会漂亮了。(Ollama 文档)所以真正完整的调优观察应该是三件套ollama psnvidia-smi/api/generate返回指标十一、给一套适合双 A100 双实例的参数收敛思路如果你的目标就是吞吐优先的 API 服务我建议不要一开始就四处乱试而是按下面顺序收敛。第一步先定保守稳定版OLLAMA_KEEP_ALIVE-1OLLAMA_FLASH_ATTENTION1OLLAMA_KV_CACHE_TYPEq8_0OLLAMA_MAX_LOADED_MODELS1OLLAMA_NUM_PARALLEL2OLLAMA_MAX_QUEUE512OLLAMA_CONTEXT_LENGTH8192这套配置的特点是模型常驻缓存省内存并发不激进上下文控制在吞吐友好范围第二步压测并看指标重点看load_duration是否趋于稳定prompt_eval_duration是否过高是否出现 503ollama ps是否保持100% GPU(Ollama 文档)第三步先加并发再考虑加上下文如果目前稳定且 GPU 仍有余力可以尝试OLLAMA_NUM_PARALLEL4而不是直接先把上下文抬高。因为对吞吐服务来说并发通常比大上下文更直接地转化为“每秒处理请求数”。这个判断基于官方对并发和上下文乘法放大内存需求的说明。(Ollama 文档)第四步只有业务真的需要才去提高上下文长度例如从OLLAMA_CONTEXT_LENGTH8192提到OLLAMA_CONTEXT_LENGTH16384而不是直接冲65536。因为一旦上下文翻倍它对并发的成本也会一起翻倍。(Ollama 文档)十二、最后给你一句最实战的结论如果你把这篇文章只看成“又多了几个环境变量”那意义不大。真正重要的是理解下面这组关系OLLAMA_NUM_PARALLEL决定单实例同时吃多少请求OLLAMA_CONTEXT_LENGTH决定单请求愿意占多少记忆窗口两者相乘决定实例的内存压力上限keep_alive决定模型会不会反复冷启动Flash Attention 和q8_0决定你能不能把更多显存腾给并发和上下文/api/generate返回指标决定你能不能看懂“慢在哪里”ollama ps决定你能不能确认模型是否仍在100% GPU状态(Ollama 文档)说得更直白一点吞吐调优不是调参数而是在做显存预算分配。你在分配的是给并发多少预算给上下文多少预算给模型常驻多少预算给高峰排队多少预算当你把这件事看明白双卡 A100 上的 Ollama 才真正从“能跑”进化成“能打”。

更多文章