ArrayPool Benchmarks: We have a problem

11/3/2025
4 minute read

Many benchmarks that are using ArrayPool, or more specificly ArryPool<T>.Shared, are not actually measuring a "real" scenarion or the benchmark is flawed in some way. Let's examine a bit on what is going on.

What is an ArrayPool?

I wrote an article some time ago title "ObjectPool - Rent and return some instances". The basic idea is the same: You can rent some array space from a pool, use it and return it back to the pool. This way you can avoid allocating new arrays all the time and reduce pressure on the garbage collector. Because the LOH is the enemy!!!!!

Flawed to the bones

Many benchmarks that are using ArrayPool are flawed in some way. Let's start with one I posted some time ago. Yes, I do "oopsies" as well. That is why I keep saying, you should check the underlying principles and not just trust benchmarks blindly.

Here my code:

[MemoryDiagnoser]
public class Benchmark
{
    const int NumberOfItems = 10000;

    [Benchmark]
    public void ArrayViaNew()
    {
        var array = new int[NumberOfItems];
    }
    [Benchmark]
    public void SharedArrayPool()
    {
        var array = ArrayPool<int>.Shared.Rent(NumberOfItems);
        ArrayPool<int>.Shared.Return(array);
    }
}

I hope you can spot the error. Because I am not returning anything and I am not using the array in any way, I am at the mercy of the JIT. The JIT is allowed to optimize away code that is not used. And that is exactly what is happening here. Here a small repro:

var foo = new Benchmark();
foo.CreateArrayWithArrayPool();
foo.CreateArrayNewArray();

public class Benchmark
{
    private const int ArraySize = 10;
    
    public void CreateArrayWithArrayPool()
    {
        var array =  ArrayPool<int>.Shared.Rent(ArraySize);
        ArrayPool<int>.Shared.Return(array);
    }

    public void CreateArrayNewArray()
    {
        var array = new int[ArraySize];
    }
}

If we have a look what the JIT does:

Benchmark.CreateArrayNewArray()
    L0000: ret

It just returns and does absolutely nothing! So the benchmark can be completely flawed! Now, we could fix it like this:

[Benchmark]
public void ArrayViaNew() 
    => DeadCodeEliminationHelper.KeepAliveWithoutBoxing(new int[NumberOfItems]);

DeadCodeEliminationHelper is a small helper class from BenchmarkDotNet that prevents the JIT from optimizing away code. Now we have a more realistic benchmark.

| Method          | Mean       | Error      | StdDev     | Gen0   | Allocated |
|---------------- |-----------:|-----------:|-----------:|-------:|----------:|
| ArrayViaNew     | 795.766 ns | 13.1411 ns | 12.2922 ns | 4.7617 |   40024 B |
| SharedArrayPool |   6.341 ns |  0.0727 ns |  0.0680 ns |      - |         - |

Hmm why not returning the array from the ArrayPool method? Good question. Let's try that:

[Benchmark]
public int[] ArrayViaNew() 
    => new int[NumberOfItems];

[Benchmark]
public int[] SharedArrayPool()
{
    var array = ArrayPool<int>.Shared.Rent(NumberOfItems);
    ArrayPool<int>.Shared.Return(array);
    return array;
}

The results would be the same. And those two types you see mainly (well probably more often than not the first one).

The problem with that

But the issue, especially with returning the array from the ArrayPool method is, that you are now returning an array that is potentially already in use somewhere else. That is why it is a shared instance - if you return it, it might be mutated by someone else. Imagine you are renting a car, setting the seat to your liking and returning it. The next time you are entering that car, the seat might be in a completely different position. So that part is not really realistic and is "bad" by design.

The other option, the void one, is also a bit misleading to say the least. The question is do you want to return that array or not? If so, you might have to call ToArray or Array.Copy at some point in time to get your own copy of the data.

That is why, you have to check against your concrete case and benchmark for that. ArrayPool is great, but it also can make things "worse" if you are not using it correctly (plus the additional complexity that comes with it).

An error has occurred. This application may no longer respond until reloaded. Reload x