如何在C#中加速无GPU环境下的ONNX模型CPU推理
优化ONNX模型CPU环境下的并行推理速度
问题描述
我使用Python训练模型并导出为ONNX格式,在C#中运行推理正常,但希望在无GPU的CPU环境下通过Tasks或Threads加速推理。当前单张图片推理耗时约200ms,30张图片耗时约900-1000ms。相关代码如下:
private static void RunInferenceOnImages(List<Tensor<float>> inputs) { var tasks = new List<Task<int>>(); foreach (var image in inputs) { var input = new List<NamedOnnxValue>() { NamedOnnxValue.CreateFromTensor<float>("float_input", image), }; Task<int> task = Task<int>.Run(() => RunInference(modelPath, input)); tasks.Add(task); } Task.WaitAll(tasks.ToArray()); foreach (var task in tasks) { var output = task.Result; } } static int RunInference(string modelPath, List<NamedOnnxValue> inputs) { using var session = new InferenceSession(modelPath); using var results = session.Run(inputs); var scores = results.First().AsTensor<long>(); return (int)scores[0]; }
优化方案
1. 复用InferenceSession实例
原代码中每个Task都会创建新的InferenceSession,这会带来大量重复的模型加载、内存初始化开销,是性能瓶颈的核心原因。InferenceSession本身是线程安全的,全局复用一个实例即可避免此问题。
修改后的代码示例:
// 类级别静态实例,仅初始化一次 private static readonly InferenceSession _inferenceSession = new InferenceSession(modelPath); private static void RunInferenceOnImages(List<Tensor<float>> inputs) { var tasks = new List<Task<int>>(); foreach (var image in inputs) { var input = new List<NamedOnnxValue>() { NamedOnnxValue.CreateFromTensor<float>("float_input", image), }; Task<int> task = Task.Run(() => RunInference(input)); tasks.Add(task); } Task.WaitAll(tasks.ToArray()); foreach (var task in tasks) { var output = task.Result; } } static int RunInference(List<NamedOnnxValue> inputs) { using var results = _inferenceSession.Run(inputs); var scores = results.First().AsTensor<long>(); return (int)scores[0]; }
2. 控制并行度,避免CPU过载
直接创建与输入数量一致的Task会导致CPU上下文切换过于频繁,反而降低整体效率。可以通过Parallel.ForEach结合MaxDegreeOfParallelism限制并发数,推荐设置为CPU核心数(或根据实际负载调整)。
示例代码:
private static void RunInferenceOnImages(List<Tensor<float>> inputs) { var inferenceResults = new int[inputs.Count]; var parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount // 匹配CPU核心数 }; Parallel.ForEach(inputs, parallelOptions, (image, state, index) => { var input = NamedOnnxValue.CreateFromTensor<float>("float_input", image); inferenceResults[index] = RunInference(new[] { input }); }); // 后续处理inferenceResults数组 }
3. 启用ONNX Runtime CPU优化选项
创建InferenceSession时配置CPU加速参数,比如开启MKL-DNN(若环境支持)、设置合理的算子内并行线程数,进一步提升单推理的速度:
private static readonly InferenceSession _inferenceSession = new InferenceSession( modelPath, SessionOptions.MakeSessionOptionWithCpuProvider(new CpuProviderOptions { EnableMkldnn = true, // 启用MKL-DNN加速 IntraOpNumThreads = Environment.ProcessorCount // 算子内并行线程数 }) );
4. 减少不必要的内存分配
避免每次创建新的List<NamedOnnxValue>,直接传入单个NamedOnnxValue数组即可,减少内存分配和GC压力:
static int RunInference(NamedOnnxValue[] inputs) { using var results = _inferenceSession.Run(inputs); var scores = results.First().AsTensor<long>(); return (int)scores[0]; }
内容的提问来源于stack exchange,提问作者Christian
相关产品推荐
相关产品推荐

