完成使用后,只需使用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 AllocatedAllocate 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 AllocatedAllocate 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 AllocatedAllocate 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 AllocatedAllocate 100000 3,637.110 ns 31.2497 31.2497 31.2497 10024 B
RentAndReturn_Shared 100000 46.649 ns - - - 0 B