Skip to content

⚙️ GPU 算力调度

一个人用是享受,多个人抢是灾难——算力调度让资源分配有章法

问题场景

只有一个 GPU,三个人同时要跑推理:

  • 张三在微调,吃了 80% 显存
  • 李四开 vLLM,OOM 了
  • 王五跑个小模型,排队等到天荒地老

没有调度 = 抢资源 = 谁都难受。


调度策略对比

策略原理优点缺点
先到先得谁先来谁先用简单一个任务占满,后面全等
显存分区GPU 切成固定份额公平碎片化
时间片轮转每个任务轮流跑公平切换开销大
优先级队列重要任务插队灵活需要定义优先级规则
MIG(多实例)硬件级分区(A100/H100)真正隔离只有高端卡支持

方案一:vLLM 内置调度

vLLM 自带请求级调度,可以控制并发:

bash
# 限制同时处理的请求数
python -m vllm.entrypoints.openai.api_server \
  --model Qwen/Qwen2.5-7B \
  --max-num-seqs 8         # 最多同时处理 8 个请求
  --max-num-batched-tokens 4096  # 单批最大 token

这粒度是按"请求"不是按"用户",适合多人调用同一模型。


方案二:基于 API 层的优先级调度

多个模型实例 + 请求路由:

python
from fastapi import FastAPI, HTTPException
import httpx
import asyncio
from collections import deque

app = FastAPI()

# 高优先级队列
high_queue = deque()
# 普通队列
normal_queue = deque()
# 当前运行的任务
running = None

@app.post("/v1/chat/completions")
async def chat(request: dict):
    priority = request.get("priority", "normal")

    # 高优先级插队
    if priority == "high":
        high_queue.append(request)
    else:
        normal_queue.append(request)

    # 等待轮到
    result = await asyncio.wait_for(wait_for_turn(), timeout=300)
    return result

async def wait_for_turn():
    while True:
        # 高优先级队列优先
        if high_queue:
            task = high_queue.popleft()
        elif normal_queue:
            task = normal_queue.popleft()
        else:
            await asyncio.sleep(0.1)
            continue

        # 发送到 vLLM
        async with httpx.AsyncClient() as client:
            resp = await client.post(
                "http://localhost:8000/v1/chat/completions",
                json=task,
                timeout=120
            )
        return resp.json()

方案三:MIG 硬件分区(A100/H100)

在 GPU 层面把一张卡切成多个独立实例:

bash
# 开启 MIG 模式
sudo nvidia-smi -i 0 -mig 1

# A100 80GB 切分成实例
sudo nvidia-smi mig -cgi 9,9,9,9,9,9,9  # 7个 10GB 实例
# 或
sudo nvidia-smi mig -cgi 19,19,19       # 3个 20GB 实例

# 每个实例分配独立的计算实例
sudo nvidia-smi mig -cci 0 -gi 1  # 给 GPU 实例 1 分配计算实例 0

# 容器绑定特定 MIG 实例
docker run --gpus '"device=MIG-<UUID>"' ollama

MIG 的好处是硬件隔离——一个实例 OOM 不影响其他实例。缺点:只有数据中心卡(A100/H100)支持,消费卡(4090)没有。


方案四:Kubernetes + GPU Operator

如果你有多台 GPU 机器,用 K8s 做集群调度是终极方案:

yaml
# 给推理 Pod 申请 8GB 显存
apiVersion: v1
kind: Pod
spec:
  containers:
  - name: vllm
    resources:
      limits:
        nvidia.com/gpu: 1      # 1张卡
        nvidia.com/gpu-memory: 8192  # 8GB 显存

K8s 自动把 Pod 调度到有空闲 GPU 的节点。但学习曲线陡,5-6 台 GPU 以上才值。


显存管理技巧

检测显存使用

bash
# 实时看
watch -n 1 nvidia-smi

# 按进程排序
nvidia-smi --query-compute-apps=pid,used_memory --format=csv

# 谁在用 GPU
sudo fuser -v /dev/nvidia*

释放被占用的显存

bash
# 杀掉僵尸进程(推理完没释放)
sudo fuser -k /dev/nvidia0

# 或在 Python 里手动释放
import torch
torch.cuda.empty_cache()

设置显存上限

python
# 限制 PyTorch 使用的显存比例
import os
os.environ["PYTORCH_CUDA_ALLOC_CONF"] = "max_split_size_mb:128"

简单场景推荐

场景推荐方案
1个人1张卡不需要调度,直接用
1张卡多人推理vLLM 限制并发数
1张卡有人微调有人推理固定时段分配(白天推理、晚上微调)
多张卡多个人按卡分配(张三用卡0,李四用卡1)
多台机器集群K8s + GPU Operator

🎯 本章要点

  • 调度核心:别让一个人占满 GPU 让别人干等
  • 单卡多人:vLLM 限制并发或用 API 优先级队列
  • MIG 硬件分区 = 真正隔离但只有 A100/H100 支持
  • 多机集群才上 K8s,单机别折腾——简单方案能用就行
  • 僵尸进程占显存用 fuser -k /dev/nvidia* 释放
加载练习题中...

有问题或补充?欢迎留言