Skip to content

CUDA MPS 实战:多进程共享 GPU 的小任务并发优化 ​

摘要: 单卡 GPU 上跑几十个推理小任务,默认的进程级并发会互相"踩踏"——每个进程各自分配显存上下文、串行占用 SM。CUDA MPS(Multi-Process Service)让多个进程共享 CUDA 上下文并发执行,是 MIG 之外的另一种算力切分思路。本文讲清楚"什么时候用 MPS、怎么配、收益多大、坑在哪"。

0. 实验目标 ​

  • 理解 MPS 与 MIG 的定位差异(软件共享 vs 硬件隔离)
  • 掌握 MPS 两种部署方式:控制台模式(交互实验)与守护进程模式(生产)
  • 用并发实验量化 MPS 的吞吐收益(小任务场景)
  • 知道 MPS 的边界与坑:故障隔离、显存上限、超时配置

1. 为什么需要 MPS:小任务并发的"踩踏"问题 ​

原理讲解:

  • 没有 MPS 时,GPU 上每个 CUDA 进程各自初始化一份完整的 CUDA context(几 GB 显存开销),且 SM 调度是进程间"轮流"的——小任务密集场景(几十路推理/预处理)吞吐上不去、显存浪费严重。
  • MPS 的核心:多个进程通过 MPS server 共享同一个 CUDA context,GPU 调度粒度从"进程"变成"线程/任务",小任务能真正并行占满 SM,同时显存上下文只存一份。

MPS vs MIG(关键对照):

维度CUDA MPSMIG(Multi-Instance GPU)
隔离级别软件级共享(context 共享)硬件级隔离(SM/显存/带宽切分)
适合场景几十路小任务、吞吐优先多租户强隔离、故障隔离优先
显存共享一份 context,省显存按实例硬切分
故障影响一个进程崩溃可能影响共享组实例间完全隔离
适用卡全部 NVIDIA 卡A100/H100 等

结论:MIG 是"切蛋糕",MPS 是"拼桌子"。混合算力池里两者配合:租户间用 MIG 硬隔离,租户内小任务用 MPS 提吞吐。

2. 控制台模式:一分钟上手 ​

bash
# 查询 GPU 是否支持 MPS(默认返回 Enabled)
nvidia-smi -q | grep -A 2 "MPS"

# 控制台模式:把当前 shell 的所有 CUDA 进程纳入 MPS(实验/验证用)
export CUDA_MPS_PIPE_DIRECTORY=/tmp/nvidia-mps
export CUDA_MPS_LOG_DIRECTORY=/tmp/nvidia-mps-log
nvidia-cuda-mps-control -d            # 后台启动 MPS 控制守护进程

# 验证:MPS 进程已运行
ps aux | grep "mps" | grep -v grep

# 实验结束退出
echo quit | nvidia-cuda-mps-control

逐段拆解:

  • CUDA_MPS_PIPE_DIRECTORY:MPS 的控制管道目录,所有要共享的进程必须指向同一个目录——这是"加入同一个 MPS server"的关键。
  • nvidia-cuda-mps-control -d:守护进程模式启动 server,之后任意设置了 CUDA_MPS_PIPE_DIRECTORY 的 CUDA 进程自动接入。
  • 注意:MPS 只对"设置了环境变量"的进程生效;没设置的在 MPS server 之外独立运行,两边互不干扰。

3. 守护进程模式:生产部署 ​

bash
# systemd 管理 MPS 守护进程(生产标准做法)
cat > /etc/systemd/system/nvidia-mps.service <<'EOF'
[Unit]
Description=NVIDIA MPS control daemon
After=network.target

[Service]
Type=forking
ExecStart=/usr/bin/nvidia-cuda-mps-control -d
ExecStop=/usr/bin/nvidia-cuda-mps-control --kill
Restart=on-failure
Environment=CUDA_MPS_PIPE_DIRECTORY=/var/run/nvidia-mps
Environment=CUDA_MPS_LOG_DIRECTORY=/var/log/nvidia-mps

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable --now nvidia-mps

工程启示:

  • 生产环境用 systemd 托管 MPS 守护进程,开机自启、崩溃自动拉起。
  • 接入方(推理服务容器/进程)统一注入 CUDA_MPS_PIPE_DIRECTORY=/var/run/nvidia-mps,K8s 里用环境变量或 envFrom 统一下发。
  • MPS 的显存上限:默认情况下 MPS 客户端共享全部显存;可设 CUDA_MPS_ACTIVE_THREAD_PERCENTAGE 限制单个客户端可占用的 SM 比例(0-100),用于粗粒度的配额控制。

4. 实验:小任务并发的 MPS 收益 ​

bash
# 实验设计:16 路并发小推理(batch=1),对比 MPS 开关
# 开启前:nvidia-smi 看 GPU 利用率波动大、任务排队
# 开启后:SM 利用率稳定打满,总吞吐显著提升
nvidia-smi --query-gpu=utilization.gpu,memory.used --format=csv -l 1

实验结果(示例量级 · 8 路推理进程 · 单卡):

场景GPU 利用率总吞吐显存上下文
无 MPS(默认)~35% 波动基准 1.0×每进程一份(N 份)
开启 MPS~92% 稳定~2.3×共享一份
MPS + 限制 SM 50%~50% 封顶~1.4×共享一份

结论:小任务密集场景 MPS 收益显著(吞吐翻倍+);大任务(单请求占满整卡)不需要 MPS,反而建议 MIG 隔离或整卡独占。MPS 的 SM 比例限制适合"多租户共享一张卡 + 配额管理"。

5. 坑与边界(生产必看) ​

  1. 故障隔离弱:MPS 客户端崩溃可能影响同组其他进程(共享 context)。关键服务别和实验任务混在一个 MPS 组,或用 MIG 做硬隔离兜底。
  2. 不支持所有 API:部分 CUDA API(如某些多进程互操作、图形 API)在 MPS 下有兼容限制,部署前跑一遍应用自测。
  3. 多用户权限:不同用户要加入同一 MPS server 需要目录权限正确(/var/run/nvidia-mps 权限 777 或组授权),否则"找不到 MPS 管道"。
  4. 与 MIG 互斥:MIG 开启时 MPS 行为受实例约束(MPS 在 MIG 实例内可用但粒度不同);不要同时混用两套切分策略。
  5. 容器场景:K8s 里要共享 GPU 的 Pod 需挂载 MPS 管道目录(/var/run/nvidia-mps + socket),并用 hostPID/共享卷打通;NVIDIA 官方 device plugin 有 MPS 支持。

6. 工程启示汇总 ​

  1. 先分场景:小任务密集 → MPS;多租户强隔离 → MIG;两者可嵌套(MIG 租户内再开 MPS)。
  2. 生产用 systemd 托管 MPS 守护进程,开机自启 + 崩溃拉起。
  3. 接入方统一注入 CUDA_MPS_PIPE_DIRECTORY,K8s 用 envFrom 统一下发。
  4. CUDA_MPS_ACTIVE_THREAD_PERCENTAGE 做粗粒度配额,别裸奔全量共享。
  5. 关键服务与实验任务分 MPS 组,故障隔离弱是 MPS 的天生短板。
  6. 验证先行:MPS 兼容性按应用自测,别等上线才发现 API 不支持。
  7. 监控 GPU 利用率:MPS 生效的标志是利用率从"波动"变"稳定打满"。

7. 延伸阅读 ​

AI-HPC 智算超算专家 · Contact: openaihpc@gmail.com