Use Pinned Object Heap for MemoryPool (#21614)

This commit is contained in:
Ben Adams 2020-05-09 23:26:35 +01:00 committed by GitHub
parent b062d1bbc3
commit a410ed4601
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 13 additions and 49 deletions

View File

@ -25,7 +25,7 @@ namespace System.Buffers
Pool = pool; Pool = pool;
Slab = slab; Slab = slab;
Memory = MemoryMarshal.CreateFromPinnedArray(slab.Array, _offset, _length); Memory = MemoryMarshal.CreateFromPinnedArray(slab.PinnedArray, _offset, _length);
} }
/// <summary> /// <summary>

View File

@ -1,8 +1,6 @@
// Copyright (c) Microsoft. All rights reserved. // Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information. // Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Runtime.InteropServices;
namespace System.Buffers namespace System.Buffers
{ {
/// <summary> /// <summary>
@ -11,18 +9,9 @@ namespace System.Buffers
/// </summary> /// </summary>
internal class MemoryPoolSlab : IDisposable internal class MemoryPoolSlab : IDisposable
{ {
/// <summary> private MemoryPoolSlab(byte[] pinnedData)
/// This handle pins the managed array in memory until the slab is disposed. This prevents it from being
/// relocated and enables any subsections of the array to be used as native memory pointers to P/Invoked API calls.
/// </summary>
private GCHandle _gcHandle;
private bool _isDisposed;
public MemoryPoolSlab(byte[] data)
{ {
Array = data; PinnedArray = pinnedData;
_gcHandle = GCHandle.Alloc(data, GCHandleType.Pinned);
NativePointer = _gcHandle.AddrOfPinnedObject();
} }
/// <summary> /// <summary>
@ -30,51 +19,24 @@ namespace System.Buffers
/// memory pool size an entire slab must be removed. That is done by (1) setting IsActive to false and removing the /// memory pool size an entire slab must be removed. That is done by (1) setting IsActive to false and removing the
/// slab from the pool's _slabs collection, (2) as each block currently in use is Return()ed to the pool it will /// slab from the pool's _slabs collection, (2) as each block currently in use is Return()ed to the pool it will
/// be allowed to be garbage collected rather than re-pooled, and (3) when all block tracking objects are garbage /// be allowed to be garbage collected rather than re-pooled, and (3) when all block tracking objects are garbage
/// collected and the slab is no longer references the slab will be garbage collected and the memory unpinned will /// collected and the slab is no longer references the slab will be garbage collected
/// be unpinned by the slab's Dispose.
/// </summary> /// </summary>
public bool IsActive => !_isDisposed; public bool IsActive => PinnedArray != null;
public IntPtr NativePointer { get; private set; } public byte[] PinnedArray { get; private set; }
public byte[] Array { get; private set; }
public static MemoryPoolSlab Create(int length) public static MemoryPoolSlab Create(int length)
{ {
// allocate and pin requested memory length // allocate requested memory length from the pinned memory heap
var array = new byte[length]; var pinnedArray = GC.AllocateUninitializedArray<byte>(length, pinned: true);
// allocate and return slab tracking object // allocate and return slab tracking object
return new MemoryPoolSlab(array); return new MemoryPoolSlab(pinnedArray);
}
protected void Dispose(bool disposing)
{
if (_isDisposed)
{
return;
}
_isDisposed = true;
Array = null;
NativePointer = IntPtr.Zero;
if (_gcHandle.IsAllocated)
{
_gcHandle.Free();
}
}
~MemoryPoolSlab()
{
Dispose(false);
} }
public void Dispose() public void Dispose()
{ {
Dispose(true); PinnedArray = null;
GC.SuppressFinalize(this);
} }
} }
} }

View File

@ -3,6 +3,7 @@
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Diagnostics; using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
namespace System.Buffers namespace System.Buffers
@ -110,7 +111,8 @@ namespace System.Buffers
var slab = MemoryPoolSlab.Create(_slabLength); var slab = MemoryPoolSlab.Create(_slabLength);
_slabs.Push(slab); _slabs.Push(slab);
var basePtr = slab.NativePointer; // Get the address for alignment
IntPtr basePtr = Marshal.UnsafeAddrOfPinnedArrayElement(slab.PinnedArray, 0);
// Page align the blocks // Page align the blocks
var offset = (int)((((ulong)basePtr + (uint)_blockSize - 1) & ~((uint)_blockSize - 1)) - (ulong)basePtr); var offset = (int)((((ulong)basePtr + (uint)_blockSize - 1) & ~((uint)_blockSize - 1)) - (ulong)basePtr);
// Ensure page aligned // Ensure page aligned