.NET性能优化-使用RecyclableBuffer取代RecyclableMemoryStream

介绍

RecyclableBuffer 是一个高性能的缓冲区管理库,它使用 ArrayPool<byte> 实现可回收内存段。该系统提供了两个互补的缓冲区写入器实现:一个针对连续内存方案进行了优化,另一个针对多段数据处理进行了优化。

组件

SingleSegmentBufferWriter

SingleSegmentBufferWriter 提供一种在单个连续内存段中维护数据的实现。与分段方法不同,此编写器确保所有写入的数据保留在一个内存块中,非常适合需要连续数据访问或与需要单个内存区域的 API 集成的场景。

主要特征

  • 实现接口 IBufferWriter<byte>
  • 在单个连续内存段中维护数据
  • 在超出容量时提供自动缓冲区大小调整
  • 集成以实现高效的内存管理 ArrayPool<byte>
  • 支持Stream接口写入和Stream接口读取

MultipleSegmentBufferWriter

MultipleSegmentBufferWriter 提供高性能缓冲区编写器实现,用于管理数据可能超过单个缓冲区容量的方案中的多个内存段。与在连续内存上运行的 SingleSegmentBufferWriter 不同,此类根据需要自动分配和链接多个缓冲区段,将它们呈现为统一的 ReadOnlySequence<byte> 以供使用。

主要特征

  • 实现接口 IBufferWriter<byte>
  • 在多内存段中维护数据
  • 在超出容量时提供自动增加内存段
  • 集成以实现高效的内存管理 ByteArrayPool
  • 支持Stream接口写入和Stream接口读取

高效的缓冲区池

ByteArrayPool 根据请求的最小长度实现双路径分配策略,正常情况下都使用自维护的ByteArrayBucket,意外情况下转为使用ArrayPool<byte>.Shared池,从下面的第一个 Benchmark 中要可以看出池的表现稳定。

Benchmark

大部分情况下 RecyclableBuffer 的 Benchmark 成绩 略微优于 RecyclableMemoryStream

内存申请Benchmark

这里测试了三种 SizeHint 值,0B 值的绝大多数场景、8KB 的手动申请场景和 128KB+1B 的超过单个缓冲区大小场景。可以看到 RecyclableMemoryStream 在 SizeHint 超过其默认的单个缓冲区大小(128KB)时受到了性能处罚,请注意除非用户是新手,否则谁也不会指定这么大的 SizeHint 值。

Method SizeHint Mean Error StdDev Ratio RatioSD Gen0 Gen1 Gen2 Allocated Alloc Ratio
SingleSegmentBufferWriter 0 104.9 ns 2.05 ns 2.28 ns 0.80 0.03 0.0191 - - 80 B 0.45
MultipleSegmentBufferWriter 0 131.7 ns 2.62 ns 4.08 ns 1.00 0.04 0.0420 - - 176 B 1.00
RecyclableMemoryStream 0 315.5 ns 5.55 ns 4.64 ns 2.40 0.08 0.0668 - - 280 B 1.59
SingleSegmentBufferWriter 8192 106.0 ns 2.08 ns 2.23 ns 0.81 0.02 0.0191 - - 80 B 0.45
MultipleSegmentBufferWriter 8192 130.6 ns 2.51 ns 1.96 ns 1.00 0.02 0.0420 - - 176 B 1.00
RecyclableMemoryStream 8192 309.0 ns 6.14 ns 5.45 ns 2.37 0.05 0.0668 - - 280 B 1.59
SingleSegmentBufferWriter 131073 146.0 ns 2.93 ns 2.88 ns 1.05 0.03 0.0286 - - 120 B 0.68
MultipleSegmentBufferWriter 131073 139.2 ns 2.75 ns 3.76 ns 1.00 0.04 0.0420 - - 176 B 1.00
RecyclableMemoryStream 131073 40,636.0 ns 397.91 ns 332.27 ns 292.08 8.09 333.3130 333.3130 333.3130 1049020 B 5,960.34

内存写入和归还Benchmark

这里测试了三种 BufferSize 值,1KB、8KB 和 128KB+1B 的超过单个缓冲区大小场景。可以看到 SingleSegmentBufferWriter 在 BufferSize 超过其默认的单个缓冲区大小(128KB)时受到了复制的性能处罚。

Method BufferSize Mean Error StdDev Ratio RatioSD Gen0 Allocated Alloc Ratio
SingleSegmentBufferWriter 1024 132.5 ns 2.65 ns 3.15 ns 0.89 0.03 0.0191 80 B 0.45
MultipleSegmentBufferWriter 1024 149.7 ns 3.02 ns 4.70 ns 1.00 0.04 0.0420 176 B 1.00
RecyclableMemoryStream 1024 405.3 ns 16.78 ns 47.89 ns 2.71 0.33 0.0668 280 B 1.59
SingleSegmentBufferWriter 8192 224.3 ns 4.51 ns 8.90 ns 0.84 0.05 0.0191 80 B 0.45
MultipleSegmentBufferWriter 8192 267.4 ns 5.38 ns 11.81 ns 1.00 0.06 0.0420 176 B 1.00
RecyclableMemoryStream 8192 467.6 ns 9.05 ns 11.44 ns 1.75 0.09 0.0668 280 B 1.59
SingleSegmentBufferWriter 131073 9,217.2 ns 270.79 ns 768.19 ns 1.97 0.18 0.0153 126 B 0.58
MultipleSegmentBufferWriter 131073 4,675.8 ns 91.93 ns 161.00 ns 1.00 0.05 0.0458 216 B 1.00
RecyclableMemoryStream 131073 5,555.9 ns 194.58 ns 570.67 ns 1.19 0.13 0.0687 312 B 1.44

总结

如果你的 API 集成的场景需要单个内存区域,那就使用 SingleSegmentBufferWriter,但需要注意提供合适的初始缓冲区大小防止扩容而Copy;

其它场景,使用 MultipleSegmentBufferWriter 即可。