共计 2306 个字符,预计需要花费 6 分钟才能阅读完成。
背景痛点:C# 部署 YOLO 的常见瓶颈
在工业质检和安防监控等实时场景中,我们发现 C# 部署 YOLO 模型时存在三个典型问题:

- 跨语言交互开销:通过 Python 桥接调用模型时,序列化 / 反序列化消耗 20%+ 的推理时间
- GC 频繁触发:每帧图像处理产生约 1.2MB 临时对象,导致 GC 每 10 秒触发一次
- GPU 利用率低:默认模式下 GPU 显存利用率仅 30%-40%,存在大量空闲时段
技术选型:ONNX Runtime vs TensorRT
通过实测 YOLOv5s 模型(输入尺寸 640×640)得出对比数据:
| 指标 | ONNX Runtime | TensorRT |
|---|---|---|
| 单帧耗时(ms) | 45 | 28 |
| 显存占用(MB) | 1200 | 800 |
| 最大吞吐量(FPS) | 22 | 35 |
| 模型体积(MB) | 27 | 18 |
决策建议:对延迟敏感场景选 TensorRT,需要快速迭代时用 ONNX Runtime
核心实现步骤
1. ML.NET 加载 ONNX 模型
// 必须设置 ExecutionProvider 优先使用 GPU
var sessionOptions = new SessionOptions()
{
GraphOptimizationLevel = GraphOptimizationLevel.ORT_ENABLE_ALL,
EnableMemoryPattern = true,
ExecutionMode = ExecutionMode.ORT_PARALLEL
};
sessionOptions.AppendExecutionProvider_CUDA();
var modelPath = "yolov5s.onnx";
using var session = new InferenceSession(modelPath, sessionOptions);
2. GPU 显存预分配技巧
// 创建可复用的输入 / 输出 Tensor
var inputOrtValue = OrtValue.CreateTensorValueFromMemory(inputData, // 预分配的 float[]数组
new long[] { batchSize, 3, 640, 640});
// 输出缓存池
var outputBuffers = new DisposableList<OrtValue>(maxBatchSize);
for (int i = 0; i < maxBatchSize; i++)
{
outputBuffers.Add(OrtValue.CreateAllocatedTensorValue(session.OutputMetadata[0].Value,
outputShape));
}
3. 多 Batch 并行处理
// 线程安全的推理队列
class InferenceQueue : IDisposable
{private readonly BlockingCollection<ImageTask> _queue = new(10);
public void AddTask(Mat image)
{_queue.Add(new ImageTask(image));
}
// 每个 worker 线程执行
void Process()
{var batch = new List<ImageTask>(batchSize);
while (_queue.TryTake(out var item, 100))
{batch.Add(item);
if (batch.Count == batchSize)
{RunBatch(batch);
batch.Clear();}
}
}
}
性能验证
使用 BenchmarkDotNet 测试结果(BatchSize=4):
| 模式 | 均值(ms) | 误差(ms) | 内存分配(MB) |
|---|---|---|---|
| CPU 单线程 | 182 | ±3.2 | 45.6 |
| GPU 单线程 | 68 | ±1.8 | 12.4 |
| GPU 四线程 | 21 | ±0.9 | 16.8 |
避坑指南
OpenCVSharp 的 DLL 冲突
当同时引用 OpenCVSharp4 和 OpenCVSharp4.runtime.win 时,需在.csproj 中明确指定:
<ItemGroup>
<PackageReference Include="OpenCVSharp4" Version="4.5.5.20211231" />
<PackageReference Include="OpenCVSharp4.runtime.win"
Version="4.5.5.20211231"
ExcludeAssets="runtime" />
</ItemGroup>
内存泄漏防护
实现 IDisposable 模式时注意多层释放:
class SafeInferenceSession : IDisposable
{
private readonly InferenceSession _session;
private readonly OrtAllocator _allocator;
public void Dispose()
{_allocator?.Dispose();
_session?.Dispose();
GC.SuppressFinalize(this);
}
}
延伸优化方向
- INT8 量化:使用 TensorRT 的 calibration 工具可再提升 1.5 倍速度
- 动态尺寸输入 :通过 ONNX 的
--dynamic参数导出模型,适应可变分辨率 - 异步流水线:将图像预处理也移至 GPU,参考 CUDA 的 NPP 库
实践心得
经过三个版本的迭代,我们最终在工业零件检测项目中实现了平均 29ms 的单帧处理速度。关键收获是:
- 批量处理比单帧处理效率提升 40%
- 固定尺寸模型比动态尺寸快 15%
- 显存预分配减少 20% 的 GC 暂停
建议先用 ONNX Runtime 快速验证流程,再针对核心模型做 TensorRT 优化。遇到性能瓶颈时,多关注 NVIDIA Nsight 工具显示的 CUDA 内核利用率。
正文完
