.Net performance tuning-MemoryPool

created at 09-14-2021 views: 2

Simple usage

//Get an instance of MemoryPool, actually returns an ArrayMemoryPool<T>
MemoryPool<char> Pool = MemoryPool<char>.Shared;

//Plus using
using IMemoryOwner<char> owner = Pool.Rent(1024 * 900);

Memory<char> memory = owner.Memory;
for (int i = 0; i <1024 * 900; i++)
{
memory.Span[i] ='x';
}

//Take 10 characters from the subscript of 100
var sliceArr = memory.Span.Slice(100, 10).ToArray();

ArrayMemoryPool\

Through MemoryPool<int>.Shared we can get an example of MemoryPool<T>, the type of this instance is ArrayMemoryPool<T>

ArrayMemoryPool<T> actually has only one function available, which is Rent(), and there is also a Dispose() function but there is no code in it

Rent() limits the maximum lease length: 2147483647u=(1024^3*2-1)=(2G-1bytes), and returns an IMemoryOwner<T> object

ArrayMemoryPoolBuffer\

The Rent() of ArrayMemoryPool<T> actually returns an ArrayMemoryPoolBuffer<T>, which inherits IMemoryOwner<T>

It provides a Memroy property to get a Memory<T> object, we can use Memroy<T> to manipulate the array we rent

The inside of ArrayMemoryPool<T> is actually called ArrayPool<T>.Shared to rent the array

The implementation code is as follows:

public Memory<T> Memory
{
    get
    {
        T[] array = _array;
        if (array == null)
        {
            System.ThrowHelper.ThrowObjectDisposedException_ArrayMemoryPoolBuffer();
        }   
        return new Memory<T>(array);
    }
}

public ArrayMemoryPoolBuffer(int size)
{
    _array = ArrayPool<T>.Shared.Rent(size);
}

public void Dispose()
{
    T[] array = _array;
    if (array != null)
    {
        _array = null;
        ArrayPool<T>.Shared.Return(array);
    }
}

Memory\

The constructor of Memory<T> receives an array<T>, which is stored in the private variable _object. The operation of the array in Memory ultimately depends on Span<T>

public readonly struct Memory<T> : IEquatable<Memory<T>>
{
    private readonly object _object;

    private readonly int _index;

    private readonly int _length;

    public static Memory<T> Empty => default(Memory<T>);

    public int Length => _length;

    public bool IsEmpty => _length == 0;

    public unsafe Span<T> Span=>{ _object...}

    public T[] ToArray()
    {
        return Span.ToArray();
    }
}

Slice(start,length)

public Memory<T> Slice(int start)
{
    return new Memory<T>(_object, _index + start, _length - start);
}

The Slice function of Memory can intercept the array. This function still returns a Memory<T> object. The new object records the original _object and the index and length to be cut, so this function will not cause additional memory consumption.

Pin()

The function of this function is to obtain the management right of the array memory, prevent the garbage collector from reclaiming it, and manage the memory by itself, but there is no research on how he manages it by himself. . . Interested friends can research on their own.

But because our array is rented from ArrayPool<T> and recycled by the Dispose function of ArrayMemoryPool<T>, this function will not be used in the use of Memory in this article.

So in summary, the simple understanding is that Memory\ is mainly used to manage Span\

So what is Span? Why does Memrory<T> not directly provide a way to manipulate arrays, but return a Span<T>?

Ahaha, I'm just asking about it

The reason is simply to use Span<T> to operate the array cutting and assignment, etc., which is faster, more memory-saving, and data flow without copying. The Span itself is too low-level and has many limitations in its use. So finally use Memory<T> to control the life cycle of Span<T>, and only provide users with functions related to Span's operation array.

As for how it is at the bottom and what are its limitations, if you want to study in depth, there are many excellent articles on the Internet that are worth reading. For Span, I can only study it now. I will come back to study it later when I learn about the related knowledge of memory. Sooner or later, I will pay back the debts owed...

scenes to be used

For MemoryPool, I really didn't think of usage scenarios. Because the previous article has introduced ArrayPool. And the array of MemoryPool is created by ArrayPool, but compared to ArrayPool, it consumes more to create IMemoryOwner. 

In fact, what we need more is Memory<T> and Span<T>. MemoryExtensions under the System.Memory namespace provides our Array[] with the extension method AsMemory\<T\>(this T[]? array) AsSpan \<T\>(this T[]? array) and so on dozens of extensions, enough to meet your needs

So if you don’t have to use MemoryPool, it is recommended to use ArrayPool directly.

NET
Please log in to leave a comment.