1

我知道使用的内部缓冲区IMemoryOwner<byte>.Memory可能比要求的要大。但是,是IMemoryOwner<byte>.Memory.Length用我问的还是用内部缓冲区的大小定义的?该文件似乎不够准确。

4

1 回答 1

2

让我们来看看。

MemoryPool<T>.Rent是抽象的,所以我们将去寻找一个实现。ArrayMemoryPool<T>.Rent看起来不错,有代表性的候选人。该实现如下所示:

public sealed override IMemoryOwner<T> Rent(int minimumBufferSize = -1)
{
    if (minimumBufferSize == -1)
        minimumBufferSize = 1 + (4095 / Unsafe.SizeOf<T>());
    else if (((uint)minimumBufferSize) > MaximumBufferSize)
        ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.minimumBufferSize);

    return new ArrayMemoryPoolBuffer(minimumBufferSize);
}

让我们追到ArrayMemoryPoolBuffer

private sealed class ArrayMemoryPoolBuffer : IMemoryOwner<T>
{
    private T[]? _array;

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

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

            return new Memory<T>(array);
        }
    }

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

new Memory<T>(array)意味着Memory<T>.Length底层数组的大小:我们没有任何逻辑来让一个较小Memory<T>的数组包裹一个更大的数组。所以让我们看看是否ArrayPool<T>.Shared.Rent给了我们一个正确大小的数组......

这又是抽象的,但我们可以在ConfigurableArrayPool<T>. 该方法的要点是:

int index = Utilities.SelectBucketIndex(minimumLength);
if (index < _buckets.Length)
{
    // Search for an array starting at the 'index' bucket. If the bucket is empty, bump up to the
    // next higher bucket and try that one, but only try at most a few buckets.
    const int MaxBucketsToTry = 2;
    int i = index;
    do
    {
        // Attempt to rent from the bucket.  If we get a buffer from it, return it.
        buffer = _buckets[i].Rent();
        if (buffer != null)
        {
            if (log.IsEnabled())
            {
                log.BufferRented(buffer.GetHashCode(), buffer.Length, Id, _buckets[i].Id);
            }
            return buffer;
        }
    }
    while (++i < _buckets.Length && i != index + MaxBucketsToTry);

    // The pool was exhausted for this buffer size.  Allocate a new buffer with a size corresponding
    // to the appropriate bucket.
    buffer = new T[_buckets[index]._bufferLength];
}
else
{
    // The request was for a size too large for the pool.  Allocate an array of exactly the requested length.
    // When it's returned to the pool, we'll simply throw it away.
    buffer = new T[minimumLength];
}

我们可以看到池中有多个桶,每个桶包含指定大小的数组。此方法是查找刚好大于请求大小的数组桶;如果那个桶是空的,它会去寻找更大的桶。如果它没有找到任何东西,它会创建一个带有桶大小的新数组;只有当请求的大小大于池可以管理的大小时,它才会创建一个精确请求大小的数组。

因此,底层数组不太可能Memory<T>具有请求的大小,并且构造的构造Memory<T>不会假装Memory<T>小于其底层数组。

结论是IMemoryOwner<byte>.Memory.Length确实可以比要求的大。

于 2021-12-01T11:09:43.657 回答