使用ArrayPool池化大型数组(翻译) (2)

完成使用后,只需使用Return方法将其返回到相同的池中即可。Return方法有一个重载,它允许你清理缓冲区,以便后续的消费者调用Rent方法不会看到以前的消费者的内容。默认情况下,内容保持不变。

源码中有一段关于ArrayPool的一个非常重要的备注

Once a buffer has been returned to the pool, the caller gives up all ownership of the buffer and must not use it. The reference returned from a given call to Rent must only be returned via Return once.

这意味着,开发人员需要正确使用此功能。如果在将缓冲区返回到池后继续使用对缓冲区的引用,则存在不可预料的风险。据我所知,截止至今天来说还没有一个静态代码分析工具可以校验正确的用法。 ArrayPool是corefx库的一部分,它不是C#语言的一部分。

压测

让我们使用BenchmarkDotNet来比较使用new操作符分配数组和使用ArrayPool

class Program { static void Main(string[] args) => BenchmarkRunner.Run<Pooling>(); } [MemoryDiagnoser] [Config(typeof(DontForceGcCollectionsConfig))] // we don't want to interfere with GC, we want to include it's impact public class Pooling { [Params((int)1E+2, // 100 bytes (int)1E+3, // 1 000 bytes = 1 KB (int)1E+4, // 10 000 bytes = 10 KB (int)1E+5, // 100 000 bytes = 100 KB (int)1E+6, // 1 000 000 bytes = 1 MB (int)1E+7)] // 10 000 000 bytes = 10 MB public int SizeInBytes { get; set; } private ArrayPool<byte> sizeAwarePool; [GlobalSetup] public void GlobalSetup() => sizeAwarePool = ArrayPool<byte>.Create(SizeInBytes + 1, 10); // let's create the pool that knows the real max size [Benchmark] public void Allocate() => DeadCodeEliminationHelper.KeepAliveWithoutBoxing(new byte[SizeInBytes]); [Benchmark] public void RentAndReturn_Shared() { var pool = ArrayPool<byte>.Shared; byte[] array = pool.Rent(SizeInBytes); pool.Return(array); } [Benchmark] public void RentAndReturn_Aware() { var pool = sizeAwarePool; byte[] array = pool.Rent(SizeInBytes); pool.Return(array); } } public class DontForceGcCollectionsConfig : ManualConfig { public DontForceGcCollectionsConfig() { Add(Job.Default .With(new GcMode() { Force = false // tell BenchmarkDotNet not to force GC collections after every iteration })); } } 结果

如果你对于BenchmarkDotNet在内存诊断程序开启的情况下所输出的内容不清楚的话,你可以读我的这一篇来了解如何阅读这些结果。

BenchmarkDotNet=v0.10.7, OS=Windows 10 Redstone 1 (10.0.14393) Processor=Intel Core i7-6600U CPU 2.60GHz (Skylake), ProcessorCount=4 Frequency=2742189 Hz, Resolution=364.6722 ns, Timer=TSC dotnet cli version=2.0.0-preview1-005977 [Host] : .NET Core 4.6.25302.01, 64bit RyuJIT Job-EBWZVT : .NET Core 4.6.25302.01, 64bit RyuJIT Method SizeInBytes Mean Gen 0 Gen 1 Gen 2 Allocated
Allocate   100   8.078 ns   0.0610   -   -   128 B  
RentAndReturn_Shared   100   44.219 ns   -   -   -   0 B  

对于非常小的内存块,默认分配器可以更快

Method SizeInBytes Mean Gen 0 Gen 1 Gen 2 Allocated
Allocate   1000   41.330 ns   0.4880   0.0000   -   1024 B  
RentAndReturn_Shared   1000   43.739 ns   -   -   -   0 B  

对于1000个字节他们的速度也差不多

Method SizeInBytes Mean Gen 0 Gen 1 Gen 2 Allocated
Allocate   10000   374.564 ns   4.7847   0.0000   -   10024 B  
RentAndReturn_Shared   10000   44.223 ns   -   -   -   0 B  

随着分配的字节增加,被分配的内存增多导致程序越来越慢。

Method SizeInBytes Mean Gen 0 Gen 1 Gen 2 Allocated
Allocate   100000   3,637.110 ns   31.2497   31.2497   31.2497   10024 B  
RentAndReturn_Shared   100000   46.649 ns   -   -   -   0 B  

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zyzwgg.html