You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 23:31:05