评测效率提升用并行采样把评测时间从小时降到分钟一、串行评测是大模型迭代的最大瓶颈之一大模型开发中评测环节的耗时经常被低估。一个典型的评测流程是加载模型对 1000 条测试样本逐一推理计算指标。在单卡 A100 上7B 模型逐条推理耗时约 0.5 秒/条1000 条需要约 8 分钟。如果有 10 个评测集就是 80 分钟。每次调参后都要重跑一天下来大部分时间都在等待评测结果。更麻烦的是评测管道往往是串行的。先跑知识问答评测再跑推理能力评测最后跑安全对齐评测。因为每条评测流水线的配置不同很多团队的习惯是依次执行。总耗时就等于所有评测任务的时间之和。但这里存在一个被忽略的优化空间大部分评测任务之间没有数据依赖。知识问答的评测结果不需要等推理评测完成后再计算。可以并行执行。见证奇迹的时刻是当把评测管道从串行改为并行后原本需要 1 小时的评测可以在 5 分钟内完成。这意味着一天可以多跑 10 次实验。二、评测并行化的架构设计评测并行化的核心思路是将每条评测管道抽象为独立的采样任务通过多 GPU 或多进程的方式并行执行。graph TD A[评测调度器] -- B[任务队列] B -- C1[GPU 0: 知识问答评测] B -- C2[GPU 1: 推理能力评测] B -- C3[GPU 2: 代码生成评测] B -- C4[GPU 3: 安全对齐评测] C1 -- D1[采样结果缓存] C2 -- D2[采样结果缓存] C3 -- D3[采样结果缓存] C4 -- D4[采样结果缓存] D1 -- E[结果聚合器] D2 -- E D3 -- E D4 -- E E -- F[指标计算与汇总] F -- G[评测报告] style A fill:#e8f5e9 style E fill:#fff3e0 style G fill:#e1f5fe架构分为三层调度层负责任务分发和资源分配执行层负责在各自的 GPU 上完成模型推理和基本指标计算聚合层负责将各评测集的结果合并为统一报告。关键的设计考量模型共享如果多个评测管道使用相同的模型权重可以通过模型复制只复制权重到多张卡上来并行化。数据并行即使只有一个评测集也可以将测试数据切分为多个 shard在多卡上并行推理。异步采样推理是计算密集型的但指标计算是轻量的。合理的流水线是推理与指标计算异步执行。三、并行评测管道的实现以下代码实现了基于多进程的并行评测管道。import torch import multiprocessing as mp from typing import List, Dict, Any, Callable from dataclasses import dataclass, field import time dataclass class EvalTask: 评测任务定义 设计原因将每个评测集封装为独立任务 包含模型路径、数据、评测函数和资源需求GPU ID。 name: str model_path: str eval_data: List[Dict[str, Any]] eval_fn: Callable # 评测函数 gpu_id: int batch_size: int 1 result: Dict[str, float] field(default_factorydict) def _eval_worker(task: EvalTask, result_queue: mp.Queue): 评测工作进程 设计原因每个进程绑定一张 GPU独立加载模型和运行评测。 通过队列将结果传回主进程避免进程间共享大对象。 torch.cuda.set_device(task.gpu_id) device torch.device(fcuda:{task.gpu_id}) # 每个工作进程独立加载模型避免跨进程共享 GPU 显存 # 设计原因PyTorch 多进程间共享 CUDA tensor 有额外开销 # 不如每张卡独立加载模型来得简单可靠。 model torch.load(task.model_path, map_locationdevice) model.eval() results [] with torch.no_grad(): for i in range(0, len(task.eval_data), task.batch_size): batch task.eval_data[i:i task.batch_size] output task.eval_fn(model, batch, device) results.append(output) # 聚合本卡结果 aggregated _aggregate_local_results(results) result_queue.put((task.name, task.gpu_id, aggregated)) class ParallelEvaluator: 并行评测调度器 设计原因管理多 GPU 资源的分配和任务调度。 支持同一模型的多评测集并行和不同模型的同时评测。 def __init__(self, gpu_ids: List[int], max_tasks_per_gpu: int 1): self.gpu_ids gpu_ids self.max_tasks_per_gpu max_tasks_per_gpu def evaluate(self, tasks: List[EvalTask]) - Dict[str, Dict[str, float]]: 并行执行所有评测任务 设计原因使用 multiprocessing 的 Queue 收集结果。 GPU 分配策略优先使用空闲 GPU满载时排队等待。 result_queue mp.Queue() processes [] gpu_usage {gid: 0 for gid in self.gpu_ids} task_index 0 while task_index len(tasks) or processes: # 分配新任务到空闲 GPU while task_index len(tasks): # 找空闲 GPU free_gpu None for gid in self.gpu_ids: if gpu_usage[gid] self.max_tasks_per_gpu: free_gpu gid break if free_gpu is None: break # 无空闲 GPU等待 task tasks[task_index] task.gpu_id free_gpu p mp.Process(target_eval_worker, args(task, result_queue)) p.start() processes.append(p) gpu_usage[free_gpu] 1 task_index 1 # 收集已完成任务的结果 if not result_queue.empty(): name, gid, result result_queue.get() gpu_usage[gid] - 1 # 保存到该任务的结果中 for t in tasks: if t.name name and t.gpu_id gid: t.result result break # 清理已完成的进程 processes [p for p in processes if p.is_alive()] time.sleep(0.1) return {t.name: t.result for t in tasks}这个实现的优势在于GPU 资源的分配是动态的不会因为某个评测集特别大而阻塞其他评测。通过max_tasks_per_gpu控制单卡并发任务数避免显存溢出。四、并行评测的适用条件与约束并行评测不是无代价的需要考虑以下约束条件。适用条件多 GPU 环境。最少需要 2 张卡才能看到明显加速。模型可以独立加载到每张卡上。模型权重较大时40GB多副本会占用大量显存。评测任务间没有数据依赖。所有评测集都使用相同的模型权重快照。不适用场景单卡环境。并行评测不适用于单卡但可以通过增大 batch_size 来提升推理吞吐。模型权重变化频繁。每次评测都需要重新加载权重到所有卡上加载时间可能抵消并行收益。评测数据极度不平衡。如果某个任务耗时是其他任务的 10 倍并行加速比有限受最大任务制约。加速比分析GPU 数量理论加速比实际加速比经验值22x1.5~1.8x44x2.5~3.5x88x4~6x加速比不能线性增长的原因模型加载时间固定、任务不均匀、以及 Python GIL 在多进程间的通信开销。见证奇迹的时刻不在于加速了多少倍而在于识别出评测中的串行点和并行点。推理是天然可并行的但模型加载、权重同步、结果聚合是串行瓶颈。优化策略应该针对瓶颈而不是在已经很快的部分上浪费时间。五、总结大模型评测效率的优化核心在于识别评测管道中的可并行化部分。通过多 GPU 并行推理和异步任务调度可以将评测总耗时从串行累加降低到近似最慢任务的耗时。实现时需要处理的关键点包括每张 GPU 独立加载模型避免跨进程共享开销、动态 GPU 资源分配以应对任务不平衡、以及合理的并发控制防止显存溢出。实际加速比受模型加载时间和任务分布不均限制通常在 GPU 数量的 60%~80% 范围内。对于单卡环境通过增大推理 batch_size 是提升吞吐的替代方案。