消费级显卡跑本地LLM:RTX 5060并发压测实战与显存优化

📅 发布时间:2026/8/27 22:05:27
消费级显卡跑本地LLM:RTX 5060并发压测实战与显存优化 在本地显卡上跑大模型的朋友应该都遇到过这类场景模型明明能跑通单次提问也很快但一旦同时来几个请求要么显存直接爆掉要么响应变得特别慢甚至前端直接弹出“连接已断开”。当我们准备把本地 LLM 部署成一个可供团队使用的小服务时并发能力就成了一道绕不开的坎。消费级显卡和 A100/H100 这类数据中心卡不同显存带宽、内存容量、功耗墙都有限盲目套用服务器端的并发配置很容易翻车。本文就用一块消费级显卡 RTX 5060 作为示例从硬件特性、显存计算、并发原理讲到压测脚本和排错思路带你搞明白“本地 LLM 到底能扛多少并发”。文章适合以下读者打算把本地大模型接入内部工具链的开发者想摸清自己显卡并发上限的折腾党以及正准备采购消费级显卡做 LLM 推理的同学。1. 背景与核心概念1.1 什么是 LLM 并发LLM 并发简单理解就是同一时间有多少个请求正在等待模型生成回答。它和你用刷网页不太一样普通 Web 服务并发高是因为每个请求都很快返回但 LLM 生成是流式的一个请求可能要持续几秒甚至几十秒期间显存、显存带宽、计算单元一直被占用。在单张消费级显卡上并发数通常和被占用显存成正比而不是像 CPU 服务那样主要受制于线程池大小。换句话说你堆再多的线程如果显存装不下多个请求的中间状态照样会被系统拒绝或排队。这里要先区分几个容易混淆的概念并发请求数客户端同时发起的请求数量。模型推理 batch size显卡一次真正同时推理的样本数量。排队等待请求到达后如果显存不足或推理引擎正在忙请求会进入队列。吞吐量单位时间内完成的请求数或生成的 token 数。首 token 延迟从发起请求到收到第一个 token 的耗时。很多人在本地测试时只关注“一次回复是不是快”却忽略了“同时多个请求时会不会互相拖垮”。这正是本文要测的核心。1.2 为什么消费级显卡也要测并发消费级显卡的用户很容易陷入两种极端第一种是“我单卡也能跑所以不用管并发”但一旦把模型接进 Web 端、钉钉机器人、VS Code 插件或在团队内部开一个共享入口多个请求一拥而上立刻露馅。第二种是“别人服务器端显存 80GB 可以开 64 并发我也照抄”结果 RTX 5060 上直接 OOM连系统桌面都跟着卡顿。所以在消费级硬件上做一次有依据的并发压测能帮我们确定几个关键参数显存充足的理想并发数响应延迟随并发上升的曲线适合本地场景的模型量化精度是否需要加网关做队列和限流。1.3 RTX 5060 定位与测试前提RTX 5060 属于 Blackwell 架构的消费级显卡是面向甜品级市场的型号。不同版本显存可能存在差异常见版本有 8GB 或 12GB 显存。本文以一块 8GB/12GB 显存的 RTX 5060 作为示例环境重点演示并发测试的思路。需要说明的是不同驱动、不同 CUDA 版本、不同显存版本都会影响最终结果。下面的数据重点是相对变化趋势而不是绝对性能标杆。你自己的显卡只要显存容量相近测试流程可以完全照搬。2. 环境准备与版本说明2.1 测试环境清单为了减少变量建议使用干净的环境项目说明操作系统Windows 11 或 Ubuntu 22.04 LTS显卡RTX 50608GB 或 12GB 显存版本驱动NVIDIA 最新 Game Ready 或 Studio 驱动CUDA以推理引擎要求为准建议 CUDA 12.xPython3.10 或 3.11推理引擎Ollama / vLLM / llama.cpp 任选压测脚本Python requests ThreadPoolExecutor这里不写死具体版本号因为 Ollama、vLLM 更新非常频繁建议以官方最新稳定版为准。重点是思路和脚本不是某一版配置。2.2 安装推理引擎先用 Ollama 举例它是消费级显卡上最省事的 LLM 推理工具之一# Linux / macOS curl -fsSL https://ollama.com/install.sh | sh # Windows 直接下载安装包或者用 winget winget install Ollama.Ollama安装完成后确认版本ollama --version再拉取一个适合消费级显卡的模型。这里以 Qwen2.5 7B 的量化版本为例ollama pull qwen2.5:7b如果你的显存只有 8GB建议选择 4bit 量化版本例如qwen2.5:7b-instruct-q4_K_M或者直接使用 Ollama 自动选择的默认量化。显存充足到 12GB 时也可以尝试 7B 的更高精度版本比如 Q6/Q8。2.3 检查显卡信息在开始压测前先确认驱动和显卡状态nvidia-smi输出中需要关注几个指标Driver Version驱动版本Memory-Usage当前显存占用GPU-Util显卡利用率下方进程列表当前哪些进程占用显存。这一步可以帮助我们建立“空闲态”的基线比如空闲时显存占用 300MB那么在计算模型可用显存时就要用总显存减去这个值。3. 核心原理解析显存、精度与并发3.1 模型推理时显存去哪儿了很多人以为模型加载后显存占用就固定了其实不然。LLM 推理时的显存可以分为两块第一部分是静态显存也就是模型权重本身。以 7B 模型为例不同的精度占用大致如下精度每个参数占用7B 权重所需显存约FP324 字节28 GBFP16 / BF162 字节14 GBINT81 字节7 GBINT4约 0.5 字节约 3.5 GB这解释了为什么消费级显卡必须用量化模型。8GB 显存跑 FP16 的 7B 模型基本不现实但跑 Q4 量化版本就非常流畅。第二部分是动态显存主要由 KV Cache 构成。LLM 在生成每个 token 时都需要读取和更新之前所有 token 的 Key 和 Value 缓存。并发请求越多累积的上下文越长KV Cache 占用就越大。KV Cache 的估算公式大致是KV Cache 字节数 ≈ 2K 和 V × 层数 × 注意力头数 × 头维度 × 序列长度 × batch size × 每个元素的字节数不同模型结构参数不同实际数值差异较大。我们只需要记住结论并发数翻倍KV Cache 占用差不多也翻倍上下文长度变长KV Cache 也会线性增长。这就是为什么“8GB 显存单请求可用并发 4 就直接爆显存”。3.2 FP16、BF16、INT8、INT4 对并发的影响FP16 和 BF16 都是 2 字节精度主要区别在于指数位和尾数位分配不同。FP16 的动态范围较小训练时容易出现精度问题BF16 动态范围更大更适合大模型训练但某些消费级显卡对 BF16 的支持和加速效果需要实测不能默认“一定比 FP16 快”。INT8 和 INT4 是量化格式权重更小所以可以留出更多显存给 KV Cache从而支持更高的并发。代价是模型生成质量可能略微下降。在实际并发测试中推荐一个顺序先用 INT4 量化模型测出并发上限如果显存充裕、质量不满意再逐步提高精度记录不同精度下并发数与显存占用的对应关系。3.3 vLLM 与 Ollama 的调度策略差异Ollama 的并发放置与配置方式比较直观它通过环境变量控制同时处理的请求数量。默认情况下Ollama 并不是每个请求都复制一份模型到显存里而是让多个请求共享同一个已加载模型并通过调度器排队。vLLM 则引入了 PagedAttention 和 continuous batching连续批处理机制。它把 KV Cache 分成固定大小的块按需分配让多个请求在 GPU 上真正并行推理而不是简单排队。效果是吞吐量更高但显存管理也更复杂。在消费级显卡上如果只是简单试用Ollama 上手成本最低如果希望模拟生产环境的高并发vLLM 更接近真实部署形态。两种引擎的测试我们都会涉及。4. 完整实战用 Python 脚本压测本地 LLM 并发4.1 测试目标先明确要测得的数据单请求基准响应时间不同并发数下的平均响应时间并发请求期间显存峰值占用是否出现 OOM、连接被断开等异常。为了简化变量我们统一使用流式生成streamtrue并固定请求的 prompt 长度和最大生成 token 数。提示词越长、生成长度越大测试结果越能反映真实使用场景。4.2 创建压测脚本创建一个 Python 文件例如llm_concurrency_test.py。# 文件路径llm_concurrency_test.py import json import time import threading import requests from concurrent.futures import ThreadPoolExecutor, as_completed OLLAMA_URL http://localhost:11434/api/generate def build_payload(prompt: str, max_tokens: int 256): return { model: qwen2.5:7b, prompt: prompt, stream: False, options: { num_predict: max_tokens, temperature: 0.7, }, } def single_request(request_id: int, payload: dict): start time.time() try: resp requests.post(OLLAMA_URL, jsonpayload, timeout180) resp.raise_for_status() data resp.json() elapsed time.time() - start return { request_id: request_id, status: success, elapsed: elapsed, eval_count: data.get(eval_count, 0), total_duration: data.get(total_duration, 0) / 1e9, } except Exception as e: elapsed time.time() - start return { request_id: request_id, status: failed, elapsed: elapsed, error: str(e), } def run_concurrency_test(concurrency: int, total_requests: int, prompt: str, max_tokens: int 256): payload build_payload(prompt, max_tokens) results [] print(f\n 并发数: {concurrency} | 总请求数: {total_requests} ) with ThreadPoolExecutor(max_workersconcurrency) as executor: future_map { executor.submit(single_request, i, payload): i for i in range(total_requests) } for future in as_completed(future_map): result future.result() results.append(result) print(f请求 {result[request_id]} 完成 | 耗时 {result[elapsed]:.2f}s | f状态 {result[status]}) success_list [r for r in results if r[status] success] failed_list [r for r in results if r[status] failed] if success_list: avg_elapsed sum(r[elapsed] for r in success_list) / len(success_list) total_tokens sum(r[eval_count] for r in success_list) print(f成功: {len(success_list)} / {total_requests}) print(f失败: {len(failed_list)}) print(f平均响应时间: {avg_elapsed:.2f}s) print(f总生成 tokens: {total_tokens}) print(f吞吐量: {total_tokens / max(avg_elapsed, 0.001):.2f} tokens/s) return results if __name__ __main__: test_prompt 请用 200 字以内介绍并发编程在 Web 服务中的作用。 # 先测单请求 run_concurrency_test(concurrency1, total_requests1, prompttest_prompt) # 再依次增加并发 for c in [2, 4, 8, 12]: run_concurrency_test(concurrencyc, total_requestsc, prompttest_prompt)这段脚本的逻辑不复杂build_payload构造请求体streamFalse表示关闭流式方便统计整体耗时single_request发送单个 HTTP 请求记录成功或失败run_concurrency_test通过ThreadPoolExecutor同时发起多个请求主程序先测 1 并发再测试 2、4、8、12 并发。运行脚本python llm_concurrency_test.py4.3 同时监控显存占用压测过程中建议另开一个终端实时监控显存。watch -n 1 nvidia-smi如果是在 Windows 上可以用如下命令周期查看while (1) { nvidia-smi; Start-Sleep -Seconds 1 }把显存峰值和压测结果对应起来才能判断瓶颈是显存容量、显存带宽还是引擎调度。4.4 观察结果趋势以一块 12GB 显存显卡 Q4 量化 7B 模型为例典型趋势可能是并发数平均响应时间显存占用状态14.5s5.2GB正常25.8s6.8GB正常48.9s9.5GB正常延迟明显上升815.2s11.8GB接近显存上限12直接报错或排队过长OOM/超时部分失败8GB 显存的情况会更快到极限可能在并发 4-6 时就出现 OOM。这不是 RTX 5060 特有现象而是消费级显卡的普遍规律。注意上面数据只是趋势示意不是固定基准。你的实际结果会和驱动、模型量化、上下文长度强相关。4.5 用 vLLM 做一次对比压测如果你希望更接近生产环境可以试用 vLLM。先安装pip install vllm然后启动一个 OpenAI 兼容的服务python -m vllm.entrypoints.openai.api_server \ --model Qwen/Qwen2.5-7B-Instruct-GPTQ-Int4 \ --dtype float16 \ --max-model-len 8192 \ --gpu-memory-utilization 0.90 \ --port 8000启动成功后把压测脚本的 URL 改成http://localhost:8000/v1/completions请求体改成 OpenAI 格式即可。# 文件路径vllm_concurrency_test.py import time import requests from concurrent.futures import ThreadPoolExecutor, as_completed VLLM_URL http://localhost:8000/v1/completions def single_request(request_id: int, prompt: str): payload { model: Qwen/Qwen2.5-7B-Instruct-GPTQ-Int4, prompt: prompt, max_tokens: 256, temperature: 0.7, } start time.time() try: resp requests.post(VLLM_URL, jsonpayload, timeout300) resp.raise_for_status() data resp.json() elapsed time.time() - start return {request_id: request_id, status: success, elapsed: elapsed, eval_count: len(data[choices][0][text].split())} except Exception as e: return {request_id: request_id, status: failed, elapsed: time.time() - start, error: str(e)} with ThreadPoolExecutor(max_workers8) as executor: futures [executor.submit(single_request, i, 请用 200 字以内介绍并发编程。) for i in range(8)] for future in as_completed(futures): print(future.result())vLLM 的 continuous batching 通常会在相同并发下获得比 Ollama 排队模式更高的吞吐但在 8GB 显存上要特别注意--gpu-memory-utilization参数设置过高可能导致加载模型时直接 OOM。5. 常见问题与排查思路5.1 显存不足 OOM问题现象常见原因解决思路并发请求时进程退出显存峰值超过显卡容量减小并发数、换更低比特量化、限制最大生成 token 数、关闭其它显存占用程序模型可以加载但 nvidia-smi 显示已满静态权重 KV Cache 超出检查是否有多进程同时加载模型使用--gpu-memory-utilization限制 vLLM 显存占用桌面或系统卡顿显存被占满共享内存兜底暂停压测及时释放显存Windows 下避免边压测边开大型程序5.2 连接断开或超时在压测中经常遇到请求发出去后没有响应或者提示“connection closed”。这在本地服务里常见原因有并发数超过了引擎能处理的队列上限单个请求生成长度过大耗时间太长被 HTTP 客户端超时中断Windows 防火墙或杀毒软件拦截了本地端口某些依赖第三方 LLM API 网关的服务会在账号并发额度超限时返回类似stream disconnected before completion: concurrency limit exceeded for account的错误。这种情况说明上游服务限制了你账号的并发数需要减小本地并发或申请更高配额。排查顺序建议先测单请求是否正常再逐步提高并发同时查看服务端日志和nvidia-smi确认是不是显存被占满导致拒绝新请求。5.3 单请求快但并发后所有请求都变慢这是典型的显存带宽瓶颈。消费级显卡的显存带宽远低于数据中心显卡多个请求同时读取权重和 KV Cache 时带宽被争抢每个请求的 token 生成速度都会下降。这种情况不是配置错误而是硬件物理限制。解决思路是降低并发数保证每个请求的体验使用更小的模型或更低的精度减少权重读取量在应用层做排队而不是把大量请求直接打到推理引擎。5.4 Ollama 模型一直切换导致响应极慢如果同时加载了多个不同模型Ollama 默认最多同时加载 1 个模型可通过环境变量调整。当请求来自模型 A 又切换到模型 B再切回 A每次切换都要重新加载权重非常耗时。# 设置最多同时加载 2 个模型 set OLLAMA_MAX_LOADED_MODELS2 # 设置每模型最多并行请求数 set OLLAMA_NUM_PARALLEL4在 Windows 上可以临时设置环境变量后重启 Ollama 服务$env:OLLAMA_MAX_LOADED_MODELS2 $env:OLLAMA_NUM_PARALLEL4 ollama serve注意提高OLLAMA_NUM_PARALLEL会增加显存占用要结合模型大小和显存容量一起评估。6. 最佳实践与工程建议6.1 根据显存选择模型和精度消费级显卡跑 LLM 的第一原则是预留显存给 KV Cache而不是让权重占满所有显存。8GB 显存推荐 7B 的 Q4 量化模型最大上下文控制在 4K-8K并发建议 2-412GB 显存可以考虑 7B 的 Q6/Q8或 14B 的 Q4并发建议 4-816GB 及以上可以尝试更大模型但依然要关注 KV Cache。不要只看“能加载”就认为“能用”要留出至少 20% 显存余量给突发并发。6.2 在应用层加队列和限流本地推理引擎的并发控制能力有限更推荐在应用层加一层队列用 Redis 列表或消息队列保存请求使用令牌桶算法限制每秒请求数把并发数固定在一个经过测试的安全值例如 4超过队列长度时快速失败而不是让请求无限等待。这样即使前端突然涌入 100 个请求推理引擎也只看到稳定的 4 个并发。6.3 关闭流式时的超时设置如果关闭流式模型需要生成完所有 token 才返回一个 256 token 的请求可能耗时几十秒甚至几分钟。HTTP 客户端超时一定要设置得足够大否则会出现“请求还在跑客户端已经超时断开”的假失败。6.4 压测时要记录环境变量压测结果受以下因素影响写报告或博客时要一并记录模型名称和量化精度提示词长度和最大生成 token 数推理引擎版本驱动和 CUDA 版本显存总容量和空闲显存是否开启 KV Cache 量化或 PageAttention。没有这些上下文单看“并发 8 延迟 15s”没有任何复现价值。6.5 注意冷却和功耗消费级显卡长时间满负载运行温度和功耗会明显上升。RTX 5060 这类显卡的散热设计和供电接口与数据中心卡不同长时间压测要注意监控温度。如果温度超过 85°C建议降低并发或暂停测试等温度回落后再继续。7. 总结与下一步学习路线本文从 RLX 5060 这类消费级显卡的实际限制出发梳理了 LLM 并发测试的核心要素显存分配、KV Cache、量化精度、推理引擎调度策略并提供了一个可直接运行的 Python 压测脚本。你可以通过这套方法测量自己显卡的并发上限、显存峰值和延迟曲线。下一步可以继续学习这几个方向深入了解 PagedAttention 的内存管理原理尝试量化工具对不同模型做逐层量化并对比质量学习 OpenAI 兼容 API 的接入方式把本地模型接到自己的应用研究流式输出对并发测试的影响记录首 token 延迟和 token 生成速率。在实际项目中优先关注三件事显存余量、HTTP 超时设置、应用层限流。只要这三件事做好了消费级显卡也能稳定扛住小团队的 LLM 服务需求。如果你手头正好有 RTX 5060 或其他消费级显卡不妨把脚本跑一遍记录一份属于自己硬件的并发基准数据后面做容量规划时能省不少力气。