public unsafe void Resize(ref RawBuffer buffer, int targetSize, int copyCount) { Debug.Assert(copyCount <= buffer.Length, "Can't copy more than the capacity of the buffer."); Debug.Assert(copyCount <= targetSize, "Can't copy more than the target size."); //Only do anything if the new size is actually different from the current size. targetSize = 1 << (SpanHelper.GetContainingPowerOf2(targetSize)); if (buffer.Allocated) { DecomposeId(buffer.Id, out var powerIndex, out var slotIndex); var currentSize = 1 << powerIndex; if (currentSize != targetSize) { Take(targetSize, out var newBuffer); Unsafe.CopyBlockUnaligned(newBuffer.Memory, buffer.Memory, (uint)copyCount); pools[powerIndex].Return(slotIndex); buffer = newBuffer; } else { //While the allocation size is equal to the target size, the buffer might not be. //Fortunately, if the allocation stays the same size, there's no work to be done. buffer.Length = targetSize; } } else { //Nothing to return or copy. Take(targetSize, out buffer); } }
public unsafe void ResizeToAtLeast(ref RawBuffer buffer, int targetSize, int copyCount) { Debug.Assert(copyCount <= buffer.Length, "Can't copy more than the capacity of the buffer."); Debug.Assert(copyCount <= targetSize, "Can't copy more than the target size."); //Only do anything if the new size is actually different from the current size. targetSize = 1 << (SpanHelper.GetContainingPowerOf2(targetSize)); if (buffer.Allocated) { DecomposeId(buffer.Id, out var powerIndex, out var slotIndex); var currentSize = 1 << powerIndex; if (currentSize != targetSize || pools[powerIndex].GetStartPointerForSlot(slotIndex) != buffer.Memory) { TakeAtLeast(targetSize, out var newBuffer); Buffer.MemoryCopy(buffer.Memory, newBuffer.Memory, buffer.Length, copyCount); pools[powerIndex].Return(slotIndex); buffer = newBuffer; } else { //While the allocation size is equal to the target size, the buffer might not be. //Fortunately, if the allocation stays the same size and the buffer start is at its original location, we can skip doing any work. //(With more work, you could expand *backwards*, we just didn't bother since this is exceptionally rare anyway. //The typed codepath doesn't bother doing this at all, and that's fine.) buffer.Length = targetSize; } } else { //Nothing to return or copy. TakeAtLeast(targetSize, out buffer); } }
/// <summary> /// Creates a new memory pool. /// </summary> /// <param name="memoryPoolSize">Size of the pool in elements.</param> public Allocator(long memoryPoolSize, int allocationCountEstimate = 128) { this.Capacity = memoryPoolSize; QuickDictionary <ulong, Allocation, Array <ulong>, Array <Allocation>, Array <int>, PrimitiveComparer <ulong> > .Create( new PassthroughArrayPool <ulong>(), new PassthroughArrayPool <Allocation>(), new PassthroughArrayPool <int>(), SpanHelper.GetContainingPowerOf2(allocationCountEstimate), 3, out allocations); }
public static int GetCapacityForCount <T>(int count) { if (count == 0) { count = 1; } return((1 << SpanHelper.GetContainingPowerOf2(count * Unsafe.SizeOf <T>())) / Unsafe.SizeOf <T>()); }
public void Return(ref Array <T> array) { ValidateCount(); #if DEBUG Debug.Assert(outstandingResources.Remove(array.Memory), "The buffer being returned must come from this pool, and buffers should only be returned once."); #endif pools[SpanHelper.GetContainingPowerOf2(array.Length)].Push(array.Memory); array = new Array <T>(); }
public PowerPool(int power, int minimumBlockSize, int expectedPooledCount) { Power = power; SuballocationSize = 1 << power; BlockSize = Math.Max(SuballocationSize, minimumBlockSize); Slots = new ManagedIdPool(expectedPooledCount); SuballocationsPerBlock = BlockSize / SuballocationSize; SuballocationsPerBlockShift = SpanHelper.GetContainingPowerOf2(SuballocationsPerBlock); SuballocationsPerBlockMask = (1 << SuballocationsPerBlockShift) - 1; Blocks = new Block[1]; BlockCount = 0; #if DEBUG outstandingIds = new HashSet <int>(); #if LEAKDEBUG outstandingAllocators = new Dictionary <string, HashSet <int> >(); #endif #endif }
public unsafe void Take(out RawBuffer buffer) { var slot = Slots.Take(); var blockIndex = slot >> SuballocationsPerBlockShift; if (blockIndex >= Blocks.Length) { Array.Resize(ref Blocks, 1 << SpanHelper.GetContainingPowerOf2(blockIndex + 1)); } if (blockIndex >= BlockCount) { #if DEBUG for (int i = 0; i < blockIndex; ++i) { Debug.Assert(Blocks[i].Allocated, "If a block index is found to exceed the current block count, then every block preceding the index should be allocated."); } #endif BlockCount = blockIndex + 1; Debug.Assert(!Blocks[blockIndex].Allocated); Blocks[blockIndex] = new Block(BlockSize); } var indexInBlock = slot & SuballocationsPerBlockMask; buffer = new RawBuffer(Blocks[blockIndex].Allocate(indexInBlock, SuballocationSize), SuballocationSize, (Power << IdPowerShift) | slot); Debug.Assert(buffer.Id >= 0 && Power >= 0 && Power < 32, "Slot/power should be safely encoded in a 32 bit integer."); #if DEBUG const int maximumOutstandingCapacity = 1 << 29; Debug.Assert(outstandingIds.Count * SuballocationSize <= maximumOutstandingCapacity, $"Do you actually truly really need to have {maximumOutstandingCapacity} bytes taken from this power pool, or is this a memory leak?"); Debug.Assert(outstandingIds.Add(slot), "Should not be able to request the same slot twice."); #if LEAKDEBUG var allocator = new StackTrace().ToString(); if (!outstandingAllocators.TryGetValue(allocator, out var idsForAllocator)) { idsForAllocator = new HashSet <int>(); outstandingAllocators.Add(allocator, idsForAllocator); } Debug.Assert(idsForAllocator.Count < (1 << 25), "Do you actually have that many allocations for this one allocator?"); idsForAllocator.Add(slot); #endif #endif }
public unsafe void Resize(ref RawBuffer buffer, int targetSize, int copyCount) { //Only do anything if the new size is actually different from the current size. targetSize = 1 << (SpanHelper.GetContainingPowerOf2(targetSize)); if (buffer.Length != targetSize) //Note that we don't check for allocated status- for buffers, a length of 0 is the same as being unallocated. { Take(targetSize, out var newBuffer); if (buffer.Length > 0) { //Don't bother copying from or re-pooling empty buffers. They're uninitialized. Debug.Assert(copyCount <= targetSize); Unsafe.CopyBlockUnaligned(newBuffer.Memory, buffer.Memory, (uint)copyCount); ReturnUnsafely(ref buffer); } else { Debug.Assert(copyCount == 0, "Should not be trying to copy elements from an empty span."); } buffer = newBuffer; } }
public unsafe void ReturnUnsafely(ref RawBuffer buffer) { ValidatePinnedState(true); pools[SpanHelper.GetContainingPowerOf2(buffer.Length)].Return(ref buffer); }
public void Take(int count, out RawBuffer buffer) { TakeForPower(SpanHelper.GetContainingPowerOf2(count), out buffer); }
public void Take(int count, out Array <T> span) { TakeForPower(SpanHelper.GetContainingPowerOf2(count), out span);; }