Python中如何并行运行多个ONNX模型推理 利用多核并解决多进程报错
ONNX模型多核并行推理实现方案
存在成熟可落地的方案,可同时满足多模型并行推理、全量利用多核计算资源的需求,且可彻底解决multiprocessing下的pickle序列化报错。
核心问题根因说明
can't pickle onnxruntime.capi.onnxruntime_pybind11_state.SessionOptions objects报错:ONNXRuntime的核心推理类(SessionOptions、InferenceSession等)都是pybind11封装的C++绑定对象,未实现Python序列化协议。如果在主进程提前初始化这类对象,再作为参数传入multiprocessing的子进程,跨进程对象传输时会触发pickle序列化逻辑,直接引发报错。- threading方案无法利用多核的原因:Python解释器的GIL会在CPU密集型的推理计算阶段阻塞线程调度,多线程并行的推理任务实际无法在多个CPU核心上同时运行,无法打满计算资源。
可落地的并行实现方案
- 核心规避逻辑:不要将主进程初始化的ORT相关对象传入子进程,改为在每个子进程的执行入口内部,单独加载模型、初始化推理会话,所有ORT对象仅在子进程的独立地址空间内创建,完全不触发跨进程序列化,从根源上解决pickle报错。
- 配套CPU资源配置调整:每个子进程是独立的操作系统进程,可独占CPU计算资源,需要给每个推理会话设置
intra_op_num_threads=1、inter_op_num_threads=1,关闭ORT默认的内部多线程逻辑。如果不做调整,每个子进程内的ORT会默认创建占满全核的计算线程,多进程并行时会产生远超核心数的线程总量,引发大量上下文切换开销,并行推理效率甚至会低于单进程串行推理。 - 参考实现代码:
import onnxruntime as ort from concurrent.futures import ProcessPoolExecutor import numpy as np # 子进程内部执行的推理逻辑,ORT会话在函数内部初始化 def run_model_inference(model_path: str, input_array: np.ndarray): # 配置单线程ORT会话,适配多进程并行的资源调度模型 so = ort.SessionOptions() so.intra_op_num_threads = 1 so.inter_op_num_threads = 1 so.log_severity_level = 3 session = ort.InferenceSession(model_path, sess_options=so, providers=["CPUExecutionProvider"]) input_name = session.get_inputs()[0].name return session.run(None, {input_name: input_array})[0] if __name__ == "__main__": # 仅传递可序列化的模型路径、输入数据,不传递任何ORT绑定对象 model_list = ["model_1.onnx", "model_2.onnx"] input_data_list = [np.random.rand(1, 3, 224, 224).astype(np.float32) for _ in range(2)] # 工作进程数与物理CPU核心数匹配,不超过核心总数 with ProcessPoolExecutor(max_workers=2) as executor: inference_results = list(executor.map(run_model_inference, model_list, input_data_list))
落地注意事项
- 工作进程总数不要超过机器的物理CPU核心数,避免进程频繁争抢CPU时间片。
- 如果涉及GPU推理,可根据显卡显存容量、计算能力,为每个子进程分配独立的CUDA设备,避免多进程争抢显存引发OOM。
- 对于长期运行的推理服务,可采用独立进程拆分的架构,为每个ONNX模型单独启动一个推理进程,主进程通过本地IPC(共享内存、本地socket)分发推理请求,相比临时创建进程的模式,资源隔离性和长期运行稳定性更强。
内容的提问来源于stack exchange,提问作者Abhishek Gangwar
相关产品推荐
相关产品推荐

