ArrayPool for frequent array creation

Using ArrayPool for performant memory reuse when creating array frequently

Home DailyDrop

Daily Knowledge Drop

In situations where arrays are created and destroyed frequently, using the ArrayPool<T> class to rent and return memory buffers, instead of initializing an array, can lead to improved performance and less pressure on the garbage collection process.


Array initialization

The usual traditional way to instantiate an array, is to use the new keyword:

void UseInitArray(int arrayLength)
{
    ArrayItem[] array = new ArrayItem[arrayLength];

    for (int i = 0; i < arrayLength; i++)
    {
        array[i] = new ArrayItem
        {
            Id = i,
            Name = i.ToString()
        };
    }
}

Here an array of arrayLength is initialized with ArrayItem[] array = new ArrayItem[arrayLength];. An ArrayItem instance is then added to each element of the array.

When a large number of arrays and created and destroyed frequently, the garbage collector is (comparatively) under pressure, as there are large amounts of memory allocated to the various large arrays which need to be cleaned up.

This is where the ArrayPool<T> class plays its part.


ArrayPool

ArrayPool<T> shines because it allows already allocated space to be rented, before it is returned back to the pool. This eliminates the need for new memory to be allocated, and then cleaned up by the garbage collector:

void UseArrayPool(int arrayLength)
{

    ArrayItem[] array = ArrayPool<ArrayItem>.Shared.Rent(arrayLength);

    try
    {
        for (int i = 0; i < arrayLength; i++)
        {
            array[i] = new ArrayItem
            {
                Id = i,
                Name = i.ToString()
            };
        }
    }
    finally
    {
        ArrayPool<ArrayItem>.Shared.Return(array);
    }

    // DO NOT DO THIS
    // array[0] = null;
}

Instead of the new keyword being used to instantiate the array, a block of memory (of the appropriate size for the array we required) is rented from the ArrayPool, using ArrayItem[] array = ArrayPool<ArrayItem>.Shared.Rent(arrayLength);

Once the array (and the memory allocated to it) is no longer required, the memory is returned to the ArrayPool, allowing it to be reused in future.

It is important to not access the array once the memory has been returned to the pool, as this could cause instability in the application.


Notes

If an application creates and destroys a large number of array's, then using the ArrayPool is the way to go. It is not significantly more complicated that manually instantiating the array, and the application could gain an improvement in performance by leveraging the ArrayPool.


References

Stas Yakhnenko Tweet

Daily Drop 245: 30-01-2023

At the start of 2022 I set myself the goal of learning one new coding related piece of knowledge a day.
It could be anything - some.NET / C# functionality I wasn't aware of, a design practice, a cool new coding technique, or just something I find interesting. It could be something I knew at one point but had forgotten, or something completely new, which I may or may never actually use.

The Daily Drop is a record of these pieces of knowledge - writing about and summarizing them helps re-enforce the information for myself, as well as potentially helps others learn something new as well.
c# .net array arraypool performance