C#向RAID0磁盘写入大字节数组速率不足,如何提升写入性能?
修正FileStream配置,启用无缓冲写入
你当前缺少最关键的FileOptions.WriteThrough参数,这个参数会绕过操作系统的页缓存直接写入磁盘,避免大体积连续写入时额外的内存拷贝开销。同时可以追加FileOptions.Asynchronous启用异步IO支持,配合后续的异步写入逻辑。如果不需要其他进程读取当前写入的文件,把FileShare.Read改成FileShare.None可以减少额外的锁开销,修改后的构造参数参考:new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None, 65536, FileOptions.SequentialScan | FileOptions.WriteThrough | FileOptions.Asynchronous)另外你代码中手动调用的
fileStream.Close()是多余操作,using块会自动释放流资源,直接删除即可。消除大内存反复分配的GC开销
你当前每次处理帧都新建一个128MB的字节数组,这类大对象会直接进入大对象堆(LOH),频繁的申请释放会导致严重的内存碎片化和GC卡顿,直接拖低整体吞吐量。改用ArrayPool<byte>复用缓冲区:// 租用缓冲区,不要每次new byte[] bufferInBytes = ArrayPool<byte>.Shared.Rent(134184960); try { MIL.MbufGetColor(BufferToWrite.IDFrame, MIL.M_PLANAR, MIL.M_ALL_BANDS, bufferInBytes); using (var fs = new FileStream(Path.Combine(AcquisitionFolder, DateTime.Now.ToString("yyyy-MM-dd-HHmmss-fffffff") + ".raw"), FileMode.Create, FileAccess.Write, FileShare.None, 65536, FileOptions.SequentialScan | FileOptions.WriteThrough | FileOptions.Asynchronous)) { fs.SetLength(134184960); // 注意写入长度是实际帧大小,不是bufferInBytes.Length(租用的数组可能比请求的大) fs.Write(bufferInBytes, 0, 134184960); } } finally { // 用完归还缓冲区 ArrayPool<byte>.Shared.Return(bufferInBytes); }改用多线程并发写入
单线程写入的IO队列深度不足,无法喂饱20盘RAID0的并行写入能力。你可以启动3~5个独立的写入线程,同时从并发队列中拉取帧数据写入磁盘,注意不要开太多线程,避免上下文切换开销。如果用.NET Core 3.1+,也可以用通道(Channel)替代ConcurrentQueue,吞吐量更高,更适合生产者消费者场景。替换为更高性能的IOAPI
如果你使用.NET 6及以上版本,可以直接用RandomAccess类替代FileStream,它跳过了FileStream的层层封装,直接调用操作系统底层IO接口,开销更低:using SafeFileHandle handle = File.OpenHandle(filePath, FileMode.Create, FileAccess.Write, FileShare.None, FileOptions.Asynchronous | FileOptions.SequentialScan | FileOptions.WriteThrough); RandomAccess.Write(handle, bufferInBytes.AsSpan(0, 134184960), 0);也可以用
RandomAccess.WriteAsync做异步写入,进一步压榨IO带宽。系统层面优化
- 检查RAID卡配置,启用写回(Write Back)缓存模式(需配备UPS防止掉电丢数据),不要使用直写(Write Through)模式
- 关闭NTFS文件系统的8.3文件名生成、最后访问时间更新功能,减少文件元数据写入开销
- 确认磁盘分区的偏移量和RAID条带对齐,你当前用64K条带,只要分区起始偏移是64K的整数倍就不会有额外的读写开销
内容的提问来源于stack exchange,提问作者Danilo7

