AI 推理 GPU 资源池化:共享与非共享模式的成本对比

📅 2026/7/13 13:21:34
AI 推理 GPU 资源池化:共享与非共享模式的成本对比
AI 推理 GPU 资源池化共享与非共享模式的成本对比一、GPU 利用率仅 15%但推理请求还经常排队超时AI 推理服务的 GPU 使用现状是极端的两极分化大部分时间 GPU 在空转等待请求高峰时段却排队排到超时。根源是 GPU 和推理服务是 1:1 绑定的——每个推理 Pod 独占一张 GPU。Pod 的并发处理能力上限由模型大小和显存决定如果流量不饱和GPU 就是昂贵的暖炉。资源池化是打破 1:1 绑定的手段。但池化有很多种方式——MIG 分区、MPS 进程共享、多模型时间片调度——每种方式的性能开销和资源隔离程度不同。选错了轻则性能退化重则显存溢出 OOM Kill 整个节点。二、GPU 共享模式的架构对比graph TD A[GPU 资源使用模式] -- B[独占模式br/1 Pod : 1 GPU] A -- C[MIG 分区br/物理隔离] A -- D[MPS 共享br/进程级复用] A -- E[时间片调度br/时分复用] B -- B1[利用率 20%] B -- B2[隔离性最好br/成本最高] C -- C1[GPU 切分为 7 个独立实例br/(A100 为例)] C -- C2[完全隔离br/显存/算力独立] C -- C3[总吞吐降低 5-10%br/(切分开销)] D -- D1[多进程共享 CUDA Context] D -- D2[无硬隔离br/显存叠加溢出风险] D -- D3[总吞吐提升 30-50%br/(利用率提高)] E -- E1[按时间片轮转 GPU 执行] E -- E2[隔离性最弱br/延迟不可预测] E -- E3[适合离线批处理br/不适合在线推理] style C fill:#50B86C,color:#fff style D fill:#F5A623,color:#000 style E fill:#FF6B6B,color:#fff三种池化模式的权衡MIGMulti-Instance GPUGPU 在硬件级别切分为独立的计算实例。每个 MIG 实例有自己的显存、缓存和计算单元安全性等同于物理隔离。适合需要强隔离的多租户场景。代价切分后的总吞吐比一张完整 GPU 低 5-10%。MPSMulti-Process Service多个进程共享一个 CUDA ContextGPU 在进程间交错执行 Kernel。隔离性弱——一个进程的显存泄漏可能影响其他进程但资源利用率最高。时间片调度按固定时间片将 GPU 分配给不同的推理请求。适合离线推理和批处理任务不适合在线推理——延迟不可预测。三、生产级 GPU 池化方案MIG 分区配置NVIDIA A100#!/bin/bash # configure-a100-mig.sh # NVIDIA A100 MIG 分区配置按推理服务需求切分 # A100 40GB 可以配置的 MIG 实例组合 # - 7x 5GB (1g.5gb) -- 7 个推理服务共享 # - 3x 10GB (2g.10gb) 1x 5GB -- 4 个服务 # - 1x 20GB (3g.20gb) 2x 10GB -- 3 个服务 # - 1x 40GB (4g.20gb 或 7g.40gb) -- 独占 # 我们的场景3 个轻量推理模型各需 8GB 显存 # 选择 3x 10GB 1x 5GB 的组合 # Step 1: 启用 MIG 模式 echo 启用 GPU MIG 模式... nvidia-smi -i 0 -mig 1 # Step 2: 查看可用的 MIG profile nvidia-smi mig -lgip # Step 3: 创建 GPU 实例 # 创建 GPU 实例切片 echo 创建 GPU 实例... nvidia-smi mig -cgi 9,9,9,14 -C # 9: 2g.10gb (2 个计算切片10GB 显存) # 14: 1g.5gb (1 个计算切片5GB 显存用于小模型) # Step 4: 为每个 GPU 实例创建对应的计算实例 echo 创建计算实例... nvidia-smi mig -cci -i 0 # Step 5: 验证 MIG 配置 echo 当前 MIG 配置 nvidia-smi mig -lgi nvidia-smiK8s GPU 资源池化管理器# gpu-pool-daemonset.yaml # GPU 资源池化 DaemonSet在每个 GPU 节点上管理 MIG/MPS apiVersion: apps/v1 kind: DaemonSet metadata: name: gpu-pool-manager namespace: kube-system spec: selector: matchLabels: app: gpu-pool-manager template: metadata: labels: app: gpu-pool-manager spec: hostPID: true hostNetwork: true tolerations: # 允许调度到 GPU 节点 - key: nvidia.com/gpu operator: Exists effect: NoSchedule containers: - name: gpu-pool-manager image: nvidia/cuda:12.2-runtime-ubuntu22.04 command: - /bin/bash - -c - | # 初始化 MIG 配置并注册为 K8s 设备插件 # 检测 GPU 型号并选择 MIG 策略 GPU_MODEL$(nvidia-smi --query-gpuname --formatcsv,noheader | head -1) if [[ $GPU_MODEL *A100* ]]; then echo 检测到 A100启用 MIG 模式 nvidia-smi -mig 1 # A100 80GB: 采用 3x 20GB 1x 10GB 策略 # 如果 GPU 是 40GB: 采用 3x 10GB 1x 5GB MEMORY$(nvidia-smi --query-gpumemory.total --formatcsv,noheader | head -1 | grep -oP \d) if [ $MEMORY -ge 80000 ]; then nvidia-smi mig -cgi 14,14,14,9 -C # 3x 3g.20gb 1x 2g.10gb else nvidia-smi mig -cgi 9,9,9,14 -C # 3x 2g.10gb 1x 1g.5gb fi nvidia-smi mig -cci elif [[ $GPU_MODEL *T4* ]]; then # T4 不支持 MIG使用 MPS echo 检测到 T4启用 MPS 模式 nvidia-cuda-mps-control -d else echo GPU 型号 $GPU_MODEL 不支持 MIG 或 MPS使用独占模式 fi # 保持容器运行 sleep infinity resources: limits: nvidia.com/gpu: 1 securityContext: privileged: true volumeMounts: - name: nvidia-mps mountPath: /tmp/nvidia-mps volumes: - name: nvidia-mps hostPath: path: /tmp/nvidia-mps type: DirectoryOrCreate推理服务的 GPU 资源调度器 GPU 资源池化调度器 根据推理请求的模型类型、显存需求和优先级 动态分配到 MIG 实例或 MPS 共享池 from dataclasses import dataclass, field from enum import Enum from typing import Dict, List, Optional import asyncio import time import threading class GPURequirement(Enum): SMALL small # 4GB MEDIUM medium # 4-10GB LARGE large # 10-20GB XLARGE xlarge # 20GB dataclass class GPUInstance: GPU 实例MIG 切片或整卡 instance_id: str # MIG UUID 或 GPU UUID total_memory_mb: int available_memory_mb: int compute_percent: int # 可用算力百分比 mig_profile: Optional[str] None # MIG 配置文件名 model_bindings: List[str] field(default_factorylist) # 绑定的模型列表 is_shared: bool False # 是否 MPS 共享模式 class GPUPoolManager: GPU 资源池管理器 核心职责 1. 维护 GPU 实例列表 2. 根据请求分配最优的 GPU 实例 3. 监控实例健康状态 4. 自动回收闲置实例 def __init__(self): self.instances: Dict[str, GPUInstance] {} self._lock threading.Lock() # 自动发现 GPU 实例MIG 切片或整卡 self._discover_instances() def _discover_instances(self): 自动发现节点上的 GPU 实例 import subprocess import json # 查询 MIG 实例 try: result subprocess.run( [nvidia-smi, mig, -lgi, --query-gpu..., --formatcsv,noheader], capture_outputTrue, textTrue, timeout10 ) # 解析 MIG 实例列表... except Exception: pass # 查询完整 GPU try: result subprocess.run( [nvidia-smi, --query-gpuuuid,memory.total,memory.free,compute_mode, --formatcsv,noheader,nounits], capture_outputTrue, textTrue, timeout10 ) for line in result.stdout.strip().split(\n): parts [p.strip() for p in line.split(,)] if len(parts) 4: instance GPUInstance( instance_idparts[0], total_memory_mbint(parts[1]), available_memory_mbint(parts[2]), compute_percent100, ) self.instances[instance.instance_id] instance except Exception: pass def allocate( self, model_name: str, required_memory_mb: int, gpu_requirement: GPURequirement, prefer_shared: bool False, timeout_sec: float 30.0, ) - Optional[str]: 为推理请求分配 GPU 实例 分配优先级 1. 优先选独占实例性能隔离好 2. 显存足够的情况优先选已绑定的实例缓存预热 3. 无合适独占实例时降级到共享池 deadline time.time() timeout_sec while time.time() deadline: with self._lock: candidates [] for instance in self.instances.values(): # 筛选条件 1: 显存足够 if instance.available_memory_mb required_memory_mb: continue # 筛选条件 2: 不能混合共享和独占 if instance.is_shared and not prefer_shared: continue # 计算候选分数越高越优先 score 0 # 加分: 已绑定缓存预热 if model_name in instance.model_bindings: score 100 # 加分: 非共享模式性能隔离更好 if not instance.is_shared: score 50 # 加分: 匹配 GPU 需求级别 # 为什么匹配级别而非选最大 # 匹配级别能避免大材小用释放高性能 GPU 给大模型 if self._req_match(gpu_requirement, instance): score 30 # 扣分: 已绑定太多模型显存碎片化 score - len(instance.model_bindings) * 5 candidates.append((score, instance)) if candidates: # 选最高分 candidates.sort(keylambda x: x[0], reverseTrue) _, best candidates[0] # 更新实例状态 best.available_memory_mb - required_memory_mb if model_name not in best.model_bindings: best.model_bindings.append(model_name) return best.instance_id # 无可用实例等待后重试 time.sleep(0.5) return None # 超时无可用资源 def release(self, instance_id: str, model_name: str, memory_mb: int): 释放 GPU 实例资源 with self._lock: instance self.instances.get(instance_id) if instance: instance.available_memory_mb memory_mb instance.available_memory_mb min( instance.available_memory_mb, instance.total_memory_mb ) def _req_match( self, requirement: GPURequirement, instance: GPUInstance ) - bool: 检查 GPU 实例是否匹配需求级别 mem_mb instance.total_memory_mb thresholds { GPURequirement.SMALL: (0, 5000), GPURequirement.MEDIUM: (5000, 12000), GPURequirement.LARGE: (12000, 25000), GPURequirement.XLARGE: (25000, 999999), } low, high thresholds[requirement] return low mem_mb high # 使用示例 pool GPUPoolManager() async def handle_inference_request(model: str, input_data: dict): 处理推理请求自动分配 GPU # 确定 GPU 需求 model_configs { bert-base: {memory_mb: 2000, requirement: GPURequirement.SMALL}, llama-7b: {memory_mb: 14000, requirement: GPURequirement.LARGE}, whisper: {memory_mb: 3000, requirement: GPURequirement.SMALL}, stable-diffusion: {memory_mb: 8000, requirement: GPURequirement.MEDIUM}, } config model_configs.get(model) if not config: raise ValueError(f未知模型: {model}) # 分配 GPU gpu_id pool.allocate( model_namemodel, required_memory_mbconfig[memory_mb], gpu_requirementconfig[requirement], timeout_sec10.0, ) if gpu_id is None: raise RuntimeError(fGPU 资源不足无法分配 {model}) try: # 在分配到的 GPU 上执行推理 result await run_inference_on_gpu(model, input_data, gpu_id) return result finally: # 释放 GPU pool.release(gpu_id, model, config[memory_mb])四、GPU 池化的边界与成本分析不同模式的成本对比以 A100 80GB 为例模式GPU 利用率月成本/单模型隔离性最佳场景独占15%$1500完全隔离需要严格 QoSMIG(3x)45%$500硬隔离多租户 SaaSMPS65%$350弱隔离内部推理服务时间片85%$250无隔离离线批处理缺点MIG 的碎片化损失GPU 切分后单实例的最大显存减少。如果你有一个 25GB 的大模型A100 40GB 切分后就放不下了——整张卡才行。MPS 的故障传播一个推理进程在 MPS 共享中显存溢出会导致该 GPU 上所有推理进程一起 OOM。冷启动惩罚MIG 模式下如果 MIG 实例还没有加载模型首次推理的冷启动延迟和独占 GPU 相当。禁用场景使用小于 4GB 的嵌入模型独占一张 GPU 浪费极小MIG 切分余地也不大。对延迟极度敏感的在线服务P99 10ms任何共享模式都会引入不可控的调度延迟。五、总结GPU 资源池化不是单一方案而是 MIG/MPS/时间片三种模式按业务场景选择的组合。MIG 适合多租户需要强隔离的场景MPS 适合内部推理服务追求高利用率时间片适合离线批处理。成本维度上MPS 可将 GPU 利用率从 15% 提升到 65%月成本可降低 75% 以上。选型核心是隔离性要求和延迟敏感度决定了你能接受哪种共享模式。