1. 工业相机高速存储的挑战与解决方案
在锂电池极片检测、半导体晶圆扫描等工业视觉场景中,我们经常需要处理4K@90fps甚至更高帧率的图像数据。以常见的RAW12格式为例,单帧4K图像大小约为12MB,这意味着系统需要持续处理高达1.08GB/s的数据流。传统基于FileStream的写入方式往往难以满足这样的性能需求。
1.1 传统文件写入的瓶颈分析
使用FileStream.WriteAsync进行图像存储时,数据需要经历多个缓冲区的拷贝:
- 应用内存 → .NET托管缓冲区
- .NET缓冲区 → 系统内核缓冲区
- 内核缓冲区 → 物理磁盘
这个过程中存在三个主要性能瓶颈:
- 内存拷贝开销:数据在多个缓冲区之间复制消耗CPU资源
- GC压力:频繁分配和释放缓冲区导致垃圾回收频繁触发
- 系统调用开销:每次写入都需要进行用户态到内核态的上下文切换
实测表明,即使在配备高性能NVMe SSD(理论速度5GB/s)的系统上,传统方式通常只能达到600-900MB/s的持续写入速度,难以满足高帧率工业相机的需求。
1.2 内存映射文件的优势
内存映射文件(Memory-Mapped Files, MMF)提供了一种更高效的I/O路径:
- 应用程序直接操作映射到文件的虚拟内存区域
- 操作系统负责在后台将脏页异步写入磁盘
- 完全避免了用户态和内核态之间的数据拷贝
这种机制带来了三个关键优势:
- 零拷贝:数据直接从相机缓冲区写入映射内存
- 延迟写入:应用程序不会被磁盘I/O阻塞
- 大块连续:可以预分配超大文件空间,避免碎片化
在我们的测试中,MMF方案实现了2.1GB/s的持续写入速度,CPU占用低于15%,完全满足4K@90fps的存储需求。
2. MMF高速写入器的实现细节
2.1 核心类设计
我们设计了MmfImageWriter类来封装MMF的核心功能:
csharp复制public class MmfImageWriter : IDisposable
{
private readonly string _filePath;
private readonly long _totalFileSize;
private MemoryMappedFile? _mmf;
private MemoryMappedViewAccessor? _accessor;
private long _currentOffset = 0;
private readonly object _lock = new();
// 构造函数预分配文件空间
public MmfImageWriter(string filePath, long totalFileSize)
{
_filePath = filePath;
_totalFileSize = totalFileSize;
using var f = File.Create(filePath);
f.SetLength(totalFileSize); // 关键:预分配空间
}
}
2.2 高性能写入实现
写入方法采用unsafe代码直接操作内存指针:
csharp复制public bool TryWrite(ReadOnlySpan<byte> data)
{
lock (_lock)
{
if (_currentOffset + data.Length > _totalFileSize)
