Chapter 09

性能优化与多 GPU 配置

从 Apple Silicon 的 Metal 加速到 NVIDIA 的 CUDA 多卡配置,从 CPU 线程调优到量化等级选择,系统掌握提升本地模型推理速度的所有手段,并学会解读 tokens/s 指标。

理解 LLM 推理性能指标

在优化之前,需要理解衡量本地 LLM 性能的核心指标:

tokens/s(每秒生成 Token 数)
最核心的速度指标,直接决定用户体验。分为两个阶段:prefill speed(处理输入 prompt 的速度,越快越好)和 decode speed(生成新 token 的速度,这是用户"感知到"的速度)。一般体验参考:<5 tok/s 感觉很慢,10-20 tok/s 尚可接受,30+ tok/s 流畅,60+ tok/s 接近实时感。
内存带宽(Memory Bandwidth)
LLM 推理的真正瓶颈通常不是计算能力(FLOPS),而是内存带宽——每秒能从内存读取多少数据。Apple M2 Pro 的统一内存带宽约 200 GB/s,NVIDIA RTX 4090 约 1 TB/s。带宽越高,推理速度越快。这也是为什么 Apple Silicon 在低参数量模型上表现出色。
KV Cache(键值缓存)
Transformer 在每步生成时需要访问所有之前 token 的 key 和 value 向量。KV Cache 将这些向量缓存起来避免重复计算,但会占用大量内存。num_ctx(上下文长度)越大,KV Cache 越大,内存占用越多。减小 num_ctx 是最直接的内存优化手段。

Apple Silicon 优化

M1/M2/M3/M4 芯片的统一内存架构(CPU 和 GPU 共享同一块 LPDDR5 内存)使其成为本地 LLM 的理想平台:

# Apple Silicon 自动启用 Metal GPU 加速,无需额外配置
# 验证 Metal 正在被使用(查看 GPU 活跃情况)
sudo powermetrics --samplers gpu_power -i 1000 -n 3

# 或使用 Activity Monitor(活动监视器)→ GPU History

# 控制 GPU 层数(num_gpu)
# Ollama 默认会将所有层放入 GPU(当内存足够时)
OLLAMA_NUM_GPU=0 ollama run llama3.2    # 强制纯 CPU
OLLAMA_NUM_GPU=33 ollama run llama3.2   # 只有 33 层放 GPU(llama3.2 共 33 层)

# 内存不足时使用部分 GPU 卸载
# 例:70B 模型有 80 层,16GB Mac 只能放 20 层到 GPU
OLLAMA_NUM_GPU=20 ollama run llama3.3:70b

# 实测各芯片性能参考(llama3.2:8b Q4_K_M):
# M1 Pro 16GB:   ~30 tok/s
# M2 Pro 16GB:   ~42 tok/s
# M2 Max 64GB:   ~65 tok/s
# M3 Max 128GB:  ~85 tok/s
# M4 Max 128GB:  ~110 tok/s

NVIDIA GPU 配置与优化

# 确认 NVIDIA 驱动版本(需要 ≥ 535)
nvidia-smi

# 查看 GPU 型号、显存总量和当前占用
nvidia-smi --query-gpu=name,memory.total,memory.used,utilization.gpu \
  --format=csv,noheader,nounits

# Ollama 使用 CUDA 推理时,自动选择可用的 GPU
# 实时监控推理时的 GPU 状态
watch -n 1 nvidia-smi

# 多 GPU 环境:指定使用哪块 GPU
CUDA_VISIBLE_DEVICES=0 ollama run llama3.2   # 只使用 GPU 0
CUDA_VISIBLE_DEVICES=0,1 ollama run llama3.3:70b  # 使用 GPU 0 和 1

# 层分配示例(总显存不够时跨 GPU 分配)
# Ollama 0.5+ 支持多 GPU 自动负载均衡
# 无需手动配置,自动检测所有 NVIDIA GPU

# 显存不足的处理(部分卸载到 CPU)
OLLAMA_NUM_GPU=40 ollama run llama3.3:70b
# 40 层放 GPU,其余 30 层在 CPU 推理(会有速度下降)

# 各 GPU 型号速度参考(llama3.2:8b Q4_K_M):
# RTX 3060 12GB:  ~45 tok/s
# RTX 3090 24GB:  ~85 tok/s
# RTX 4090 24GB:  ~130 tok/s
# A100 80GB:      ~200 tok/s

CPU 推理优化

当没有兼容 GPU 时,Ollama 回退到 CPU 推理。以下参数可显著提升 CPU 性能:

# 控制 CPU 线程数(默认使用所有逻辑核心)
OLLAMA_NUM_THREAD=8 ollama run llama3.2   # 手动设置线程数

# 最佳实践:设置为物理核心数(非超线程数)
# 查看物理核心数
sysctl -n hw.physicalcpu  # macOS
nproc --all               # Linux

# CPU 推理时,关闭不必要的后台程序可提升 10-20%

# AMD CPU 的 AVX2/AVX-512 加速(llama.cpp 自动检测)
# 验证 CPU 是否支持 AVX-512
grep -m1 avx512 /proc/cpuinfo  # Linux
sysctl -n machdep.cpu.features  # macOS

# 纯 CPU 速度参考(llama3.2:8b Q4_K_M):
# 4 核笔记本 CPU:   ~3-5 tok/s(勉强可用)
# 8 核台式 CPU:     ~6-10 tok/s
# 16 核服务器 CPU:  ~15-25 tok/s

环境变量完整参考

Ollama 通过环境变量控制所有运行时配置:

# 持久化配置(写入 ~/.bashrc 或 ~/.zshrc)

# 模型存储路径(指向大容量磁盘)
export OLLAMA_MODELS=/data/ollama-models

# 监听地址(默认只允许本机,0.0.0.0 允许局域网访问)
export OLLAMA_HOST="0.0.0.0:11434"

# GPU 层数(-1 为自动,0 为纯 CPU)
export OLLAMA_NUM_GPU=-1

# CPU 线程数
export OLLAMA_NUM_THREAD=8

# 模型在内存中保持的时间(秒),0=立即卸载,-1=永久保持
export OLLAMA_KEEP_ALIVE="5m"  # 5分钟不使用后卸载

# 最大并发加载的模型数
export OLLAMA_MAX_LOADED_MODELS=2

# 最大并发请求队列长度
export OLLAMA_MAX_QUEUE=512

# 调试日志(详细输出,用于排查问题)
export OLLAMA_DEBUG=1

# CUDA 设备选择
export CUDA_VISIBLE_DEVICES="0,1"  # 指定 GPU 0 和 1

# Flash Attention 加速(支持的 GPU 上自动启用)
export OLLAMA_FLASH_ATTENTION=1

模型并发与多用户场景

# 允许同时加载多个模型(需要足够内存)
export OLLAMA_MAX_LOADED_MODELS=3

# 每个模型允许的并发请求数
export OLLAMA_NUM_PARALLEL=4  # 同一模型同时处理 4 个请求

# 查看当前内存中的模型
curl http://localhost:11434/api/ps
# 返回:正在运行的模型列表 + 过期时间

# 性能测试脚本
import ollama
import time

def benchmark(model: str, prompt: str = "写一首100字的诗", runs: int = 3):
    """简单的性能基准测试。"""
    results = []
    for i in range(runs):
        start = time.perf_counter()
        resp = ollama.generate(model=model, prompt=prompt)
        elapsed = time.perf_counter() - start

        tokens = resp.get("eval_count", 0)
        tok_per_s = tokens / elapsed if elapsed > 0 else 0
        results.append(tok_per_s)
        print(f"Run {i+1}: {tok_per_s:.1f} tok/s ({tokens} tokens, {elapsed:.2f}s)")

    avg = sum(results) / len(results)
    print(f"\n平均: {avg:.1f} tok/s")
    return avg

# 对比不同量化版本的速度
for model in ["qwen2.5:7b", "llama3.2"]:
    print(f"\n=== {model} ===")
    benchmark(model)
优化优先级排序 1. 选合适的量化版本(Q4_K_M > Q8 > Q2);2. 确保 GPU 加速已启用(nvidia-smi / Metal 活跃);3. 减小 num_ctx(4096 代替 131072);4. 增大 num_parallel(如 GPU 内存足够);5. 关闭不用的模型(OLLAMA_KEEP_ALIVE=0 立即释放)。单一优化往往比多个小优化叠加效果更显著。
本章小结 性能优化核心:GPU 加速是最大提升(10-20x vs CPU);量化等级 Q4_K_M 是速度/质量最佳平衡点;减小 num_ctx 节省内存同时提速;Apple Silicon 通过 Metal 自动加速,无需配置;NVIDIA 通过 CUDA_VISIBLE_DEVICES 控制多卡使用;OLLAMA_KEEP_ALIVE 控制模型在内存中的保持时间。下一章学习生产环境的安全部署。