Merge from branch giga

This commit is contained in:
Nitload
2025-08-31 10:42:51 +08:00
parent 1fc687f7d0
commit 1a0ccdd8cb
1619 changed files with 2812 additions and 733136 deletions
@@ -87,8 +87,10 @@ namespace StgSharp.HighPerformance.Memory
public void Push(T item)
{
Header* header = (Header*)_buffer;
if (header->Top >= header->Capacity) {
if (header->Top >= header->Capacity)
{
Expand();
header = (Header*)_buffer;
}
T* dataStart = (T*)(_buffer + sizeof(Header));
@@ -125,8 +127,8 @@ namespace StgSharp.HighPerformance.Memory
T* dataStart = (T*)(_buffer + sizeof(Header));
fixed (T* sourcePtr = span) {
Buffer.MemoryCopy(sourcePtr, dataStart,
header->Capacity * (nuint)sizeof(T),
Buffer.MemoryCopy(sourcePtr, dataStart,
header->Capacity * (nuint)sizeof(T),
(nuint)span.Length * (nuint)sizeof(T));
}
@@ -1,251 +0,0 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="ConcurrentStackBuffer.cs"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the “Software”), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software,
// and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and
// this permission notice shall be included in all copies
// or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”,
// WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH
// THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using StgSharp.Threading;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace StgSharp.HighPerformance.Memory
{
public static class ConcurrentStackBufferBuilder
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static ConcurrentStackBuffer<T> BuildStack<T>(ReadOnlySpan<T> span)
where T: unmanaged
{
return new ConcurrentStackBuffer<T>(span);
}
}
[CollectionBuilder(
builderType: typeof(ConcurrentStackBufferBuilder),
methodName: nameof(ConcurrentStackBufferBuilder.BuildStack))]
public unsafe class ConcurrentStackBuffer<T> : IDisposable where T: unmanaged
{
private byte* _buffer;
private readonly BufferExpansionLock _lock = new();
private ulong _count;
internal ConcurrentStackBuffer(ReadOnlySpan<T> span)
{
AllocateBuffer(span.Length);
Span<T> targe = new(_buffer + sizeof(Header), span.Length);
span.CopyTo(targe);
}
public ConcurrentStackBuffer(int capacity)
{
if (capacity <= 0) {
throw new ArgumentOutOfRangeException(nameof(capacity));
}
AllocateBuffer(capacity);
}
public ulong Count
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => Volatile.Read(ref _count);
}
public void Clear()
{
Header* header = (Header*)_buffer;
_lock.EnterExpansionProcess();
try
{
header->Top = 0;
}
finally
{
_lock.ExitExpansionProcess();
}
}
public void Dispose()
{
_lock.Dispose();
Marshal.FreeHGlobal((nint)_buffer);
}
public T Pop()
{
Header* header = (Header*)_buffer;
while (true)
{
nuint oldTop = Volatile.Read(ref header->Top);
if (oldTop <= 0) {
throw new InvalidOperationException("Stack underflow");
}
if (Interlocked.CompareExchange(ref header->Top, oldTop - 1, oldTop) == oldTop)
{
_lock.EnterBufferRead();
try
{
T* dataStart = (T*)(_buffer + sizeof(Header));
_ = Interlocked.Decrement(ref _count);
return dataStart[oldTop - 1];
}
finally
{
_lock.ExitBufferRead();
}
}
Thread.Sleep(0); // Yield to avoid busy waiting
}
}
public void Push(T item)
{
while (true)
{
_lock.EnterMetaDataRead();
try
{
Header* header = (Header*)_buffer;
nuint oldTop = Volatile.Read(ref header->Top);
nuint cap = Volatile.Read(ref header->Capacity);
if (oldTop >= cap)
{
_lock.ExitMetaDataRead();
_lock.EnterExpansionProcess();
try
{
header = (Header*)_buffer;
if (header->Top >= header->Capacity)
{
nuint newCap = header->Capacity * 2;
nuint newSize = (nuint)sizeof(Header) + (newCap * (nuint)sizeof(T));
_lock.EnterBufferCopy();
try
{
_buffer = (byte*)NativeMemory.Realloc(_buffer, newSize);
((Header*)_buffer)->Capacity = newCap;
}
finally
{
_lock.ExitBufferCopy();
}
}
}
finally
{
_lock.ExitExpansionProcess();
}
continue;
}
if (Interlocked.CompareExchange(ref header->Top, oldTop + 1, oldTop) == oldTop)
{
_lock.EnterBufferRead();
try
{
T* dataStart = (T*)(_buffer + sizeof(Header));
dataStart[oldTop] = item;
return;
}
finally
{
_lock.ExitBufferRead();
}
}
}
finally
{
if (_lock.IsThreadReadingMetaData) {
_lock.ExitMetaDataRead();
}
}
Thread.Sleep(0); // Yield to avoid busy waiting
}
}
public bool TryPop(out T value)
{
Header* header = (Header*)_buffer;
while (true)
{
nuint oldTop = Volatile.Read(ref header->Top);
if (oldTop <= 0)
{
value = default;
return false;
}
if (Interlocked.CompareExchange(ref header->Top, oldTop - 1, oldTop) == oldTop)
{
_lock.EnterBufferRead();
try
{
T* dataStart = (T*)(_buffer + sizeof(Header));
value = dataStart[oldTop - 1];
}
finally
{
_lock.ExitBufferRead();
}
return true;
}
Thread.Sleep(0); // Yield to avoid busy waiting
}
}
private void AllocateBuffer(int capacity)
{
nuint size = (nuint)sizeof(Header) + ((nuint)capacity * (nuint)sizeof(T));
_buffer = (byte*)NativeMemory.Alloc(size);
Header* header = (Header*)_buffer;
header->Top = 0;
header->Capacity = (nuint)capacity;
}
private struct Header
{
public nuint Capacity; // max capacity of the stack (number of elements)
public nuint Top; // next available index in the stack
}
}
}
@@ -28,17 +28,12 @@
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using StgSharp.Entities;
using System;
using System.Buffers;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Drawing;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using Allocator = StgSharp.HighPerformance.Memory.HybridLayerSegregatedFitAllocator;
@@ -48,7 +43,6 @@ namespace StgSharp.HighPerformance.Memory
{
internal readonly byte* BufferPointer;
internal readonly Allocator.Entry* EntryHandle;
public readonly uint AllocSize;
@@ -48,8 +48,6 @@ namespace StgSharp.HighPerformance.Memory
public unsafe partial class HybridLayerSegregatedFitAllocator
{
private BufferExpansionLock _spareLock = new();
public hlsfHandle Alloc(uint size)
{
ArgumentOutOfRangeException.ThrowIfGreaterThan<nuint>(size, (nuint)levelSizeArray[^1] * 2);
@@ -57,15 +55,7 @@ namespace StgSharp.HighPerformance.Memory
int segmentIndex = DetermineSegmentIndex(size, level);
int i = level, s = segmentIndex;
if (TryPopLevelForAllocation(i, s, out Entry* handle))
{
SetEntryState(handle, EntryState.Allocated);
return new hlsfHandle(handle, size);
}
fallback:
i = level;
s = segmentIndex;
Entry* handle;
while (!TryPopLevelForAllocation(i, s, out handle))
{
if (++s >= 3)
@@ -75,30 +65,19 @@ namespace StgSharp.HighPerformance.Memory
}
if (i >= levelSizeArray.Length)
{
if (!TryAllocateNew16MBBlock(out handle)) {
throw new OverflowException("Out of memory: Unable to allocate new 16MB block or find suitable bucket for allocation.");
if (TryAllocateNew16MBBlock(out handle))
{
break;
}
Thread.Sleep(0);
goto fallback;
throw new OverflowException("Out of memory: Unable to allocate new 16MB block or find suitable bucket for allocation.");
}
}
try
{
if (handle != null)
{
AcquireEntryNextLock(handle);
SliceMemory(handle, (segmentIndex + 1) * levelSizeArray[level]);
SetEntryState(handle, EntryState.Allocated);
}
return new hlsfHandle(handle, size);
}
finally
{
ReleaseEntryNextLock(handle);
if (handle != null) {
SliceMemory(handle, (segmentIndex + 1) * levelSizeArray[level]);
}
handle->State = EntryState.Allocated;
return new hlsfHandle(handle, size);
}
private void SliceMemory(Entry* e, int offset)
@@ -109,116 +88,110 @@ namespace StgSharp.HighPerformance.Memory
ulong originOffset = e->Position - (ulong)m_Buffer;
ulong finalOffset = originOffset + (ulong)offset;
ulong end = e->Position + e->Size;
int remainSize = (int)(e->Size - offset);
AcquireEntryNextLock(e);
AcquireEntryPrevLock(next);
int minLevel = int.Max((BitOperations.TrailingZeroCount(finalOffset) - 6) / 2, 0);
int i = minLevel >= levelSizeArray.Length ? levelSizeArray.Length - 1 : minLevel;
try
for (; i < levelSizeArray.Length; i++)
{
if ((originOffset & 63) != 0) {
throw new InvalidOperationException("Entry position offset is not aligned to 64 bytes.");
}
int minLevel = Math.Max((BitOperations.TrailingZeroCount(originOffset) - 6) / 2, 0);
int i = minLevel >= levelSizeArray.Length ? levelSizeArray.Length - 1 : minLevel;
for (; i < levelSizeArray.Length; i++)
int levelSize = levelSizeArray[i];
ulong board = (finalOffset + (ulong)levelSize - 1) & ~((ulong)levelSize - 1);
int blockCount = (int)((ulong.Min(board, end) - board) / (ulong)levelSize);
if (blockCount == 0)
{
int levelSize = levelSizeArray[i];
int blockCount = remainSize / levelSize % 4;
if (blockCount == 0)
{
break;
}
int s = blockCount * levelSize;
// create and init new entry
Entry* newEntry = (Entry*)_entries.Allocate();
newEntry->Position = (nuint)((ulong)m_Buffer + finalOffset);
newEntry->Size = (uint)s;
newEntry->LockBuffer = 0;
BucketNode* bucket = (BucketNode*)newEntry->Position;
bucket->EntryRef = newEntry;
bucket->LockBuffer = 0;
newEntry->State = EntryState.ThreadOccupied;
// link new entry
newEntry->PreviousNear = (ulong)current;
newEntry->NextNear = n;
current->NextNear = (ulong)newEntry;
next->PreviousNear = (ulong)newEntry;
newEntry->Level = i;
PushLevel(i, blockCount - 1, bucket);
// set new entry
Entry* old = current;
current = newEntry;
// update lock and data
finalOffset += (ulong)s;
remainSize -= s;
ReleaseEntryNextLock(old);
AcquireEntryNextLock(current);
break;
}
if (remainSize <= 0)
{
ReleaseEntryNextLock(current);
ReleaseEntryPrevLock(next);
return;
}
for (; i >= 0; i--)
{
int levelSize = levelSizeArray[i];
int blockCount = remainSize / levelSize % 4;
if (blockCount == 0)
{
continue;
}
int s = blockCount * levelSize;
int s = blockCount * levelSize;
// create and init new entry
Entry* newEntry = (Entry*)_entries.Allocate();
newEntry->LockBuffer = 0;
newEntry->Position = (nuint)((ulong)m_Buffer + finalOffset);
newEntry->Size = (uint)s;
BucketNode* bucket = (BucketNode*)newEntry->Position;
bucket->EntryRef = newEntry;
bucket->LockBuffer = 0;
newEntry->State = EntryState.ThreadOccupied;
// create and init new entry
Entry* newEntry = (Entry*)_entries.Allocate();
newEntry->Position = (nuint)((ulong)m_Buffer + finalOffset);
newEntry->Size = (uint)s;
BucketNode* bucket = (BucketNode*)newEntry->Position;
bucket->EntryRef = newEntry;
// link new entry
newEntry->PreviousNear = (ulong)current;
current->NextNear = (ulong)newEntry;
newEntry->NextNear = n;
next->PreviousNear = (ulong)newEntry;
// link new entry
newEntry->PreviousNear = (ulong)current;
newEntry->NextNear = n;
current->NextNear = (ulong)newEntry;
next->PreviousNear = (ulong)newEntry;
newEntry->Level = i;
PushLevel(i, blockCount - 1, bucket);
newEntry->Level = i;
PushLevel(i, blockCount - 1, bucket);
// set new entry
Entry* old = current;
current = newEntry;
// set new entry
Entry* old = current;
current = newEntry;
// update lock and data
finalOffset += (ulong)s;
remainSize -= s;
ReleaseEntryNextLock(old);
AcquireEntryNextLock(current);
if (remainSize <= 0)
{
break;
}
}
// update lock and data
finalOffset += (ulong)s;
remainSize -= s;
newEntry->Level = i;
PushLevel(i, blockCount - 1, bucket);
// set new entry
current = newEntry;
// update data
finalOffset += (ulong)s;
remainSize -= s;
}
catch (Exception)
{
throw;
if (remainSize <= 0) {
return;
}
finally
for (i -= 1; i >= 0; i--)
{
// Release locks in reverse order
ReleaseEntryNextLock(current);
ReleaseEntryPrevLock(next);
int levelSize = levelSizeArray[i];
ulong board = (finalOffset + (ulong)levelSize - 1) & ~((ulong)levelSize - 1);
int blockCount = (int)((ulong.Min(board, end) - finalOffset) / (ulong)levelSize);
if (blockCount == 0)
{
continue;
}
int s = blockCount * levelSize;
// create and init new entry
Entry* newEntry = (Entry*)_entries.Allocate();
newEntry->Position = (nuint)((ulong)m_Buffer + finalOffset);
newEntry->Size = (uint)s;
BucketNode* bucket = (BucketNode*)newEntry->Position;
bucket->EntryRef = newEntry;
// link new entry
newEntry->PreviousNear = (ulong)current;
newEntry->NextNear = n;
current->NextNear = (ulong)newEntry;
if (next != null) {
next->PreviousNear = (ulong)newEntry;
}
newEntry->Level = i;
PushLevel(i, blockCount - 1, bucket);
// set new entry
Entry* old = current;
current = newEntry;
// update lock and data
finalOffset += (ulong)s;
remainSize -= s;
newEntry->Level = i;
PushLevel(i, blockCount - 1, bucket);
// set new entry
current = newEntry;
// update data
finalOffset += (ulong)s;
remainSize -= s;
}
}
@@ -233,22 +206,14 @@ namespace StgSharp.HighPerformance.Memory
/// </returns>
private bool TryAllocateNew16MBBlock(out Entry* handle)
{
nuint pos = Volatile.Read(ref _spareMemory->Position);
nuint pos = _spareMemory->Position;
nuint blockSize = (nuint)levelSizeArray[^1] * 2; // Use last level size (16MB)
nuint newPos = pos + blockSize;
ulong p;
Entry* previous;
do
{
p = Volatile.Read(ref _spareMemory->PreviousNear);
previous = (Entry*)p;
if (previous != null) {
AcquireEntryNextLock(previous);
}
AcquireEntryPrevLock(_spareMemory);
break;
} while (true);
p = _spareMemory->PreviousNear;
previous = (Entry*)p;
if (_spareMemory->Size <= 0)
{
handle = null;
@@ -263,42 +228,26 @@ namespace StgSharp.HighPerformance.Memory
}
// Try to allocate new Entry from slab allocator
try
{
handle = (Entry*)_entries.Allocate();
handle->Position = pos;
handle->Size = (uint)blockSize;
SetEntryState(handle, EntryState.ThreadOccupied);
handle->Level = GetLevelFromSize((uint)blockSize);
handle = (Entry*)_entries.Allocate();
handle->Position = pos;
handle->Size = (uint)blockSize;
handle->Level = GetLevelFromSize((uint)blockSize);
handle->PrevLock = 0;
handle->NextLock = 0;
// Update spare memory position
_spareMemory->Position = newPos; // Replace Volatile.Write with direct access
BucketNode* b = (BucketNode*)handle->Position;
b->EntryRef = handle;
PushLevel(handle->Level, DetermineSegmentIndex((uint)blockSize, handle->Level), b);
// Update spare memory position
_spareMemory->Position = newPos;
BucketNode* b = (BucketNode*)handle->Position;
b->EntryRef = handle;
PushLevel(handle->Level, DetermineSegmentIndex((uint)blockSize, handle->Level), b);
// update position linked list
handle->NextNear = (ulong)_spareMemory;
handle->PreviousNear = p;
_spareMemory->PreviousNear = (ulong)handle; // Replace Volatile.Write with direct access
if (previous != null) {
previous->NextNear = (ulong)handle;
}
// update position linked list
handle->NextNear = (ulong)_spareMemory;
handle->PreviousNear = p;
_spareMemory->PreviousNear = (ulong)handle;
if (previous != null) {
previous->NextNear = (ulong)handle;
}
return true;
}
catch
{
handle = null;
return false;
}
finally
{
ReleaseEntryNextLock(previous);
ReleaseEntryPrevLock(_spareMemory);
}
return true;
}
/// <summary>
@@ -39,12 +39,6 @@ namespace StgSharp.HighPerformance.Memory
public unsafe partial class HybridLayerSegregatedFitAllocator
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void AcquireBucketNodeSpinLock(BucketNode* node)
{
AcquireBucketNodeBothLocks(node);
}
/// <summary>
/// Initialize bucket system (called in constructor)
/// </summary>
@@ -53,6 +47,16 @@ namespace StgSharp.HighPerformance.Memory
_bucketHeads = new ulong[levelSizeArray.Length * 3];
}
#region BucketNode Operations
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool IsNullOrEmpty(BucketNode* node)
{
return node == null || node->Magic != BucketNode.MAGIC_NUMBER;
}
#endregion
private void PushLevel(int levelIndex, int sizeIndex, BucketNode* node)
{
if (node == null || node->EntryRef == null) {
@@ -60,286 +64,110 @@ namespace StgSharp.HighPerformance.Memory
}
int index = (3 * levelIndex) + sizeIndex;
if (Interlocked.CompareExchange(ref _bucketHeads[index], (nuint)node, EmptyHandle) == EmptyHandle)
if (_bucketHeads[index] == EmptyHandle)
{
_bucketHeads[index] = (nuint)node;
node->IsInBucket = true;
node->Magic = BucketNode.MAGIC_NUMBER;
return;
} else
{
while (true)
{
AcquireBucketNodeNextLock(node);
ulong head = Volatile.Read(ref _bucketHeads[index]);
node->NextLevel = head;
BucketNode* headNode = (BucketNode*)head;
try
{
if (Interlocked.CompareExchange(ref _bucketHeads[index], (nuint)node, head) == head)
{
if (headNode != null && !TryAcquireBucketNodePrevLock(headNode))
{
// reset state and retry
_ = Interlocked.CompareExchange(ref _bucketHeads[index], head, (nuint)node);
Thread.Sleep(0);
continue;
}
try
{
node->IsInBucket = true;
node->Magic = BucketNode.MAGIC_NUMBER;
if (headNode != null) {
headNode->PreviousLevel = (ulong)node;
}
return;
}
finally
{
if (headNode != null) {
ReleaseBucketNodePrevLock(headNode);
}
}
} else
{
Thread.Sleep(0);
continue;
}
}
finally
{
ReleaseBucketNodeNextLock(node);
}
ulong head = _bucketHeads[index];
node->NextLevel = head;
BucketNode* headNode = (BucketNode*)head;
node->IsInBucket = true;
node->Magic = BucketNode.MAGIC_NUMBER;
if (headNode != null) {
headNode->PreviousLevel = (ulong)node;
}
}
}
/// <summary>
/// 根据锁类型释放BucketNode锁
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void ReleaseBucketNodeLockByType(BucketNode* node, byte lockType)
{
switch (lockType)
{
case 0:
ReleaseBucketNodePrevLock(node);
return; // PrevLock
case 1:
ReleaseBucketNodeBothLocks(node);
return; // BothLocks
case 2:
ReleaseBucketNodeNextLock(node);
return; // NextLock
default:
return;
}
}
/// <summary>
/// 按地址顺序释放BucketNode锁
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void ReleaseOrderedBucketLocks(BucketNode* previous, BucketNode* node, BucketNode* next)
{
if (previous != null) {
ReleaseBucketNodeNextLock(previous);
}
if (node != null) {
ReleaseBucketNodeBothLocks(node);
}
if (next != null) {
ReleaseBucketNodePrevLock(next);
return;
}
}
private void RemoveFromBucket(int levelIndex, int sizeIndex, BucketNode* node)
{
int index = (3 * levelIndex) + sizeIndex;
while (true)
if ((BucketNode*)_bucketHeads[index] == node)
{
if ((BucketNode*)Volatile.Read(ref _bucketHeads[index]) == node)
{
if (TryPopLevel(levelIndex, sizeIndex, out _)) {
return;
}
Thread.Sleep(0);
continue;
}
ulong p = Volatile.Read(ref node->PreviousLevel);
ulong n = Volatile.Read(ref node->NextLevel);
BucketNode* previous = (BucketNode*)p;
BucketNode* next = (BucketNode*)n;
if (!TryAcquireOrderedBucketLocks(previous, node, next))
{
Thread.Sleep(0);
continue;
}
try
{
if (Volatile.Read(ref node->PreviousLevel) != p || Volatile.Read(ref node->NextLevel) != n)
{
Thread.Sleep(0);
continue;
}
if (previous != null) {
previous->NextLevel = n;
}
if (next != null) {
next->PreviousLevel = p;
}
node->NextLevel = EmptyHandle;
node->PreviousLevel = EmptyHandle;
node->IsInBucket = false;
if (TryPopLevel(levelIndex, sizeIndex, out _)) {
return;
}
finally
{
ReleaseOrderedBucketLocks(previous, node, next);
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool TryAcquireBucketNodeSpinLock(BucketNode* node)
{
return TryAcquireBucketNodeBothLocks(node);
}
ulong p = node->PreviousLevel;
ulong n = node->NextLevel;
BucketNode* previous = (BucketNode*)p;
BucketNode* next = (BucketNode*)n;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool TryAcquireOrderedBucketLocks(BucketNode* previous, BucketNode* node, BucketNode* next)
{
if (previous != null && node != null && next != null)
{
// previous -> node -> next
return TryAcquireBucketNodeNextLock(previous) &&
TryAcquireBucketNodeBothLocks(node) &&
TryAcquireBucketNodePrevLock(next);
} else if (previous != null && node != null) {
return TryAcquireBucketNodeNextLock(previous) && TryAcquireBucketNodeBothLocks(node);
if (previous != null) {
previous->NextLevel = n;
}
return true;
if (next != null) {
next->PreviousLevel = p;
}
node->NextLevel = EmptyHandle;
node->PreviousLevel = EmptyHandle;
node->IsInBucket = false;
return;
}
private bool TryPopLevel(int levelIndex, int sizeIndex, out BucketNode* node)
{
int index = (3 * levelIndex) + sizeIndex;
while (true)
ulong h = _bucketHeads[index];
BucketNode* head = (BucketNode*)h;
if (head == null)
{
ulong h = Volatile.Read(ref _bucketHeads[index]);
BucketNode* head = (BucketNode*)h;
if (head == null)
{
node = null;
return false;
}
if (!TryAcquireBucketNodeNextLock(head))
{
Thread.Sleep(0);
continue;
}
ulong n = Volatile.Read(ref head->NextLevel);
BucketNode* next = (BucketNode*)n;
if (next == null)
{
if (Interlocked.CompareExchange(ref _bucketHeads[index], EmptyHandle, h) == h)
{
node = head;
return true;
}
node = null;
return false;
}
try
{
if (!TryAcquireBucketNodePrevLock(next))
{
Thread.Sleep(0);
continue;
}
try
{
if (Interlocked.CompareExchange(ref _bucketHeads[index], head->NextLevel, h) == h)
{
node = head;
node->IsInBucket = false;
node->NextLevel = EmptyHandle;
if (next != null) {
next->PreviousLevel = EmptyHandle;
}
return true;
} else
{
Thread.Sleep(0);
continue;
}
}
finally
{
if (next != null) {
ReleaseBucketNodePrevLock(next);
}
}
}
finally
{
ReleaseBucketNodeNextLock(head);
}
node = null;
return false;
}
ulong n = head->NextLevel;
BucketNode* next = (BucketNode*)n;
if (next == null)
{
_bucketHeads[index] = EmptyHandle;
node = head;
return true;
}
_bucketHeads[index] = head->NextLevel;
node = head;
node->IsInBucket = false;
node->NextLevel = EmptyHandle;
if (next != null) {
next->PreviousLevel = EmptyHandle;
}
return true;
}
private bool TryRemoveFromBucket(int levelIndex, int sizeIndex, BucketNode* node)
{
int index = (3 * levelIndex) + sizeIndex;
if ((BucketNode*)Volatile.Read(ref _bucketHeads[index]) == node)
{
if (TryPopLevel(levelIndex, sizeIndex, out _)) {
return true;
}
return false;
if ((BucketNode*) _bucketHeads[index] == node) {
return TryPopLevel(levelIndex, sizeIndex, out _);
}
ulong p = Volatile.Read(ref node->PreviousLevel);
ulong n = Volatile.Read(ref node->NextLevel);
ulong p = node->PreviousLevel;
ulong n = node->NextLevel;
BucketNode* previous = (BucketNode*)p;
BucketNode* next = (BucketNode*)n;
if (!TryAcquireOrderedBucketLocks(previous, node, next)) {
return false;
if (previous != null) {
previous->NextLevel = n;
}
if (next != null) {
next->PreviousLevel = p;
}
try
{
if (Volatile.Read(ref node->PreviousLevel) != p || Volatile.Read(ref node->NextLevel) != n) {
return false;
}
if (previous != null) {
previous->NextLevel = n;
}
if (next != null) {
next->PreviousLevel = p;
}
node->NextLevel = EmptyHandle;
node->PreviousLevel = EmptyHandle;
node->IsInBucket = false;
return true;
}
finally
{
ReleaseOrderedBucketLocks(previous, node, next);
}
node->NextLevel = EmptyHandle;
node->PreviousLevel = EmptyHandle;
node->IsInBucket = false;
return true;
}
/// <summary>
@@ -364,139 +192,5 @@ namespace StgSharp.HighPerformance.Memory
}
#region BucketNode Operations
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool IsNullOrEmpty(BucketNode* node)
{
return node == null || node->Magic != BucketNode.MAGIC_NUMBER;
}
// BucketNode 前驱锁操作(控制PreviousLevel字段)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool TryAcquireBucketNodePrevLock(BucketNode* node)
{
if (node == null) {
return true;
}
int thread = Environment.CurrentManagedThreadId;
return Interlocked.CompareExchange(ref node->PrevLock, thread, 0) == 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void AcquireBucketNodePrevLock(BucketNode* node)
{
if (node == null) {
return;
}
int thread = Environment.CurrentManagedThreadId;
if (Volatile.Read(ref node->PrevLock) != thread)
{
while (Interlocked.CompareExchange(ref node->PrevLock, thread, 0) != 0) {
Thread.Sleep(0);
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void ReleaseBucketNodePrevLock(BucketNode* node)
{
if (node == null) {
return;
}
int thread = Environment.CurrentManagedThreadId;
if (Volatile.Read(ref node->PrevLock) == thread) {
Volatile.Write(ref node->PrevLock, 0);
}
}
// BucketNode 后继锁操作(控制NextLevel字段)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool TryAcquireBucketNodeNextLock(BucketNode* node)
{
if (node == null) {
return true;
}
int thread = Environment.CurrentManagedThreadId;
return Interlocked.CompareExchange(ref node->NextLock, thread, 0) == 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void AcquireBucketNodeNextLock(BucketNode* node)
{
if (node == null) {
return;
}
int thread = Environment.CurrentManagedThreadId;
if (Volatile.Read(ref node->NextLock) != thread)
{
while (Interlocked.CompareExchange(ref node->NextLock, thread, 0) != 0) {
Thread.Sleep(0);
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void ReleaseBucketNodeNextLock(BucketNode* node)
{
if (node == null) {
return;
}
int thread = Environment.CurrentManagedThreadId;
if (Volatile.Read(ref node->NextLock) == thread) {
Volatile.Write(ref node->NextLock, 0);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool TryAcquireBucketNodeBothLocks(BucketNode* node)
{
if (node == null) {
return true;
}
if (TryAcquireBucketNodePrevLock(node))
{
if (TryAcquireBucketNodeNextLock(node))
{
return true;
} else
{
ReleaseBucketNodePrevLock(node); // 回滚
return false;
}
}
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void ReleaseBucketNodeBothLocks(BucketNode* node)
{
if (node == null) {
return;
}
ReleaseBucketNodePrevLock(node);
ReleaseBucketNodeNextLock(node);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void AcquireBucketNodeBothLocks(BucketNode* node)
{
if (node == null) {
return;
}
AcquireBucketNodePrevLock(node);
AcquireBucketNodeNextLock(node);
}
#endregion
}
}
@@ -54,36 +54,26 @@ namespace StgSharp.HighPerformance.Memory
if (e == null) {
return;
}
AcquireEntrySpinLock(e);
try
{
if (GetEntryState(e) != EntryState.Allocated) {
return;
}
SetEntryState(e, EntryState.ThreadOccupied);
// Step 4: Try merge with adjacent free blocks and remove them from buckets
MergeAndRemoveFromBuckets(e);
// Step 5: Calculate final level and segment for the merged block
uint finalSize = e->Size;
int finalLevel = GetLevelFromSize(finalSize);
int finalSegment = DetermineSegmentIndex(finalSize, finalLevel);
// Step 6: Set the merged entry state to Empty (ready to be reused)
SetEntryState(e, EntryState.Empty);
// Step 7: Push the merged entry back to appropriate bucket
PushLevel(finalLevel, finalSegment, (BucketNode*)e->Position);
// Note: If mergedEntry is different from original e, the original e
// and any merged entries have already been freed to _entries by MergeAndRemoveFromBuckets
}
finally
{
// Step 8: Always release the spin lock
ReleaseEntrySpinLock(e);
if (e->State != EntryState.Allocated) {
return;
}
// Set to ThreadOccupied to prevent other operations
e->State = EntryState.Empty;
// Try merge with adjacent free blocks and remove them from buckets
MergeAndRemoveFromBuckets(e);
// Calculate final level and segment for the merged block
uint finalSize = e->Size;
int finalLevel = GetLevelFromSize(finalSize);
int finalSegment = DetermineSegmentIndex(finalSize, finalLevel);
// Set the merged entry state to Empty (ready to be reused)
e->State = EntryState.Empty;
// Push the merged entry back to appropriate bucket
PushLevel(finalLevel, finalSegment, (BucketNode*)e->Position);
}
}
@@ -41,24 +41,18 @@ namespace StgSharp.HighPerformance.Memory
public unsafe partial class HybridLayerSegregatedFitAllocator
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void AcquireEntrySpinLock(Entry* entry)
private bool AssertBackwardBoundary(ulong currentOffset, Entry* target, int level)
{
AcquireEntryBothLocks(entry);
int nextLevelSize = levelSizeArray[level] * 4;
ulong targetHead = target->Position - (ulong)m_Buffer;
return (currentOffset / (ulong)nextLevelSize) == (targetHead / (ulong)nextLevelSize);
}
private bool AssertBackwardBoundary(ulong current, Entry* target, int level)
private bool AssertForwardBoundary(ulong currentOffset, Entry* target, int level)
{
int size = (level * 2) + 6;
ulong align = current >> size;
return align == target->Position >> size && align == (current + target->Size) >> size;
}
private bool AssertForwardBoundary(ulong current, Entry* target, int level)
{
int size = (level * 2) + 6;
ulong align = current >> size;
return align == target->Position >> size && align == (current - target->Size) >> size;
int nextLevelSize = levelSizeArray[level] * 4;
ulong targetTail = target->Position - (ulong)m_Buffer + target->Size - 1;
return (currentOffset / (ulong)nextLevelSize) == (targetTail / (ulong)nextLevelSize);
}
/// <summary>
@@ -94,113 +88,78 @@ namespace StgSharp.HighPerformance.Memory
Entry* result = entry;
uint size = entry->Size;
int originLevel = GetLevelFromSize(size);
int originLevel = entry->Level;
int segment = DetermineSegmentIndex(size, originLevel);
if (!TryAcquireEntrySpinLock(entry)) {
return;
}
if (!TryRemoveFromBucket(originLevel, segment, (BucketNode*)entry->Position)) { }
try
RemoveFromBucket(originLevel, segment, (BucketNode*)entry->Position);
do
{
_ = TrySetEntryState(entry, EntryState.Empty, EntryState.Merging);
// current Level merge
while (originLevel <= entry->Level)
originLevel = entry->Level;
while (true)
{
ulong offset = entry->Position - (ulong)m_Buffer;
// backward merging - simplified to single attempt
ulong p = entry->PreviousNear;
Entry* prev = (Entry*)p;
// backward merging
while (true)
{
ulong p = entry->PreviousNear;
Entry* prev = (Entry*)p;
if (prev == null || // null ref
prev == _spareMemory || // point at end of list
GetEntryState(prev) != EntryState.Empty || // not empty
!IsAdjacentTo(entry, prev) || // not nearby
entry->Level != prev->Level || // not same level
!AssertBackwardBoundary(offset, prev, entry->Level) || // not aligned if merge
!TryAcquireEntrySpinLock(prev)) // occupied by threads
{
break;
}
ulong pp = prev->NextNear;
Entry* prevPrev = (Entry*)pp;
if (!TryAcquireEntryNextLock(prevPrev))
{
break;
}
try
{
if (!TryRemoveFromBucket(originLevel, DetermineSegmentIndex(prev->Size, originLevel), (BucketNode*)prev->Position))
{
break;
}
entry->Position = prev->Position; // Update position to the previous entry
entry->Size += prev->Size; // Merge sizes
prevPrev->NextNear = (ulong)entry;
RecycleEntry(prev);
}
finally
{
ReleaseEntryNextLock(prevPrev);
ReleaseEntrySpinLock(prev);
}
}
// forward merging
while (true)
{
ulong n = entry->NextNear;
Entry* next = (Entry*)n;
if (next == null || // null ref
next == _spareMemory || // point at end of list
GetEntryState(next) != EntryState.Empty || // not empty
!IsAdjacentTo(entry, next) || // not nearby
entry->Level != next->Level || // not same level
!AssertBackwardBoundary(offset, next, entry->Level) || // not aligned if merge
!TryAcquireEntrySpinLock(next)) // occupied by threads
{
break;
}
ulong nn = next->NextNear;
Entry* nextNext = (Entry*)nn;
if (!TryAcquireEntryPrevLock(nextNext))
{
break;
}
try
{
if (!TryRemoveFromBucket(originLevel, DetermineSegmentIndex(next->Size, originLevel), (BucketNode*)next->Position))
{
break;
}
entry->Size += next->Size;
entry->NextNear = next->NextNear;
RecycleEntry(next);
}
finally
{
ReleaseEntrySpinLock(next);
}
}
// 计算当前块相对于分配空间基址的偏移量
ulong currentOffset = entry->Position - (ulong)m_Buffer;
entry->Level = GetLevelFromSize(entry->Size);
if (originLevel == entry->Level)
if (prev == null ||
prev == _spareMemory ||
!AssertBackwardBoundary(currentOffset, prev, originLevel) ||
!IsAdjacentTo(entry, prev) ||
entry->Level != prev->Level ||
!TryRemoveFromBucket(originLevel, DetermineSegmentIndex(prev->Size, originLevel), (BucketNode*)prev->Position))
{
break;
}
originLevel = entry->Level;
// Update linked list
Entry* prevPrev = (Entry*)prev->PreviousNear;
if (prevPrev != null) {
prevPrev->NextNear = (ulong)entry;
}
entry->PreviousNear = (ulong)prevPrev;
// Merge
entry->Position = prev->Position;
entry->Size += prev->Size;
RecycleEntry(prev);
}
_ = TrySetEntryState(entry, EntryState.Merging, EntryState.Empty);
return;
}
finally
{
ReleaseEntrySpinLock(entry);
}
while (true)
{
// forward merging - simplified to single attempt
ulong n = entry->NextNear;
Entry* next = (Entry*)n;
ulong currentOffset = entry->Position - (ulong)m_Buffer;
if (next == null ||
next == _spareMemory ||
!AssertForwardBoundary(currentOffset, next, entry->Level) ||
!IsAdjacentTo(entry, next) ||
entry->Level != next->Level ||
!TryRemoveFromBucket(originLevel, DetermineSegmentIndex(next->Size, originLevel), (BucketNode*)next->Position))
{
break;
}
// Update linked list
Entry* nextNext = (Entry*)next->NextNear;
if (nextNext != null) {
nextNext->PreviousNear = (ulong)entry;
}
entry->NextNear = (ulong)nextNext;
// Merge
entry->Size += next->Size;
RecycleEntry(next);
}
entry->Level = GetLevelFromSize(entry->Size);
} while (originLevel < entry->Level);
}
private void RecycleEntry(Entry* entry)
@@ -208,14 +167,10 @@ namespace StgSharp.HighPerformance.Memory
if (entry is null) {
return;
}
AcquireEntrySpinLock(entry);
_entries.Free((nuint)entry);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void ReleaseEntrySpinLock(Entry* entry)
{
ReleaseEntryBothLocks(entry);
entry->PreviousNear = EmptyHandle;
entry->NextNear = EmptyHandle;
_entries.Free((nuint)entry);
}
/// <summary>
@@ -223,64 +178,45 @@ namespace StgSharp.HighPerformance.Memory
/// </summary>
private void RemoveFromPositionChain(Entry* entry)
{
AcquireEntrySpinLock(entry);
try
{
Entry* prev = (Entry*)entry->PreviousNear;
Entry* next = (Entry*)entry->NextNear;
Entry* prev = (Entry*)entry->PreviousNear;
Entry* next = (Entry*)entry->NextNear;
if (prev != null) {
prev->NextNear = entry->NextNear;
}
if (next != null) {
next->PreviousNear = entry->PreviousNear;
}
// Clear node pointers
entry->NextNear = EmptyHandle;
entry->PreviousNear = EmptyHandle;
_entries.Free((nuint)entry);
if (prev != null) {
prev->NextNear = entry->NextNear;
}
finally
{
ReleaseEntrySpinLock(entry);
if (next != null) {
next->PreviousNear = entry->PreviousNear;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool TryAcquireEntrySpinLock(Entry* entry)
{
return TryAcquireEntryBothLocks(entry);
// Clear node pointers
entry->NextNear = EmptyHandle;
entry->PreviousNear = EmptyHandle;
_entries.Free((nuint)entry);
}
/// <summary>
/// Entry structure - streamlined version (only position and state information) Stored in
/// external SLAB
/// </summary>
[StructLayout(LayoutKind.Explicit, Size = 44)]
[StructLayout(LayoutKind.Explicit, Size = 36)]
internal struct Entry
{
[FieldOffset(28)] internal int Level; // Level of the block in the allocator
[FieldOffset(32)] internal int NextLock;
[FieldOffset(36)] internal int PrevLock;
[FieldOffset(40)] internal int State; // Node state (using int for atomic operations)
[FieldOffset(24)] internal uint Size; // Block size information
[FieldOffset(32)] internal ulong LockBuffer;
[FieldOffset(8)] internal ulong NextNear; // Position linked list: next pointer
[FieldOffset(16)] internal ulong PreviousNear; // Position linked list: previous pointer
[FieldOffset(32)] public EntryState State;
[FieldOffset(0)] public nuint Position; // Memory block position
}
// 更新后的EntryState常量类 - 使用int类型
private static class EntryState
internal enum EntryState : uint
{
public const int Allocated = 1; // Already allocated to user
public const int Empty = 0; // Free, can be allocated
public const int Merging = 3; // Currently being merged
public const int ThreadOccupied = 2; // Occupied by thread (being operated on)
Empty,
Allocated
}
@@ -298,176 +234,6 @@ namespace StgSharp.HighPerformance.Memory
return entry == null;
}
// Entry 状态操作
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool TrySetEntryState(Entry* entry, int expectedState, int newState)
{
return Interlocked.CompareExchange(ref entry->State, newState, expectedState) == expectedState;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int GetEntryState(Entry* entry)
{
return entry != null ? Volatile.Read(ref entry->State) : 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void SetEntryState(Entry* entry, int newState)
{
if (entry != null) {
Volatile.Write(ref entry->State, newState);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool IsEntryEmpty(Entry* entry)
{
return GetEntryState(entry) == EntryState.Empty;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool IsEntryAllocated(Entry* entry)
{
return GetEntryState(entry) == EntryState.Allocated;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool IsEntryThreadOccupied(Entry* entry)
{
return GetEntryState(entry) == EntryState.ThreadOccupied;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool IsEntryMerging(Entry* entry)
{
return GetEntryState(entry) == EntryState.Merging;
}
// Entry 前驱锁操作(控制PreviousNear字段)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool TryAcquireEntryPrevLock(Entry* entry)
{
if (entry == null) {
return true;
}
int thread = Environment.CurrentManagedThreadId;
return Interlocked.CompareExchange(ref entry->PrevLock, thread, 0) == 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void AcquireEntryPrevLock(Entry* entry)
{
if (entry == null) {
return;
}
int thread = Environment.CurrentManagedThreadId;
if (Volatile.Read(ref entry->PrevLock) != thread)
{
while (Interlocked.CompareExchange(ref entry->PrevLock, thread, 0) != 0) {
Thread.Sleep(0);
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void ReleaseEntryPrevLock(Entry* entry)
{
if (entry == null) {
return;
}
int thread = Environment.CurrentManagedThreadId;
if (Volatile.Read(ref entry->PrevLock) == thread) {
Volatile.Write(ref entry->PrevLock, 0);
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool TryAcquireEntryNextLock(Entry* entry)
{
if (entry == null) {
return true;
}
int thread = Environment.CurrentManagedThreadId;
return Interlocked.CompareExchange(ref entry->NextLock, thread, 0) == 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void AcquireEntryNextLock(Entry* entry)
{
if (entry == null) {
return;
}
int thread = Environment.CurrentManagedThreadId;
if (Volatile.Read(ref entry->NextLock) != thread)
{
while (Interlocked.CompareExchange(ref entry->NextLock, thread, 0) != 0) {
Thread.Sleep(0);
}
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void ReleaseEntryNextLock(Entry* entry)
{
if (entry == null) {
return;
}
int thread = Environment.CurrentManagedThreadId;
if (Volatile.Read(ref entry->NextLock) == thread) {
Volatile.Write(ref entry->NextLock, 0);
}
}
// Entry 双锁组合操作
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool TryAcquireEntryBothLocks(Entry* entry)
{
if (entry == null) {
return true;
}
if (TryAcquireEntryPrevLock(entry))
{
if (TryAcquireEntryNextLock(entry))
{
return true;
} else
{
ReleaseEntryPrevLock(entry); // 回滚
return false;
}
}
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void ReleaseEntryBothLocks(Entry* entry)
{
if (entry == null) {
return;
}
ReleaseEntryPrevLock(entry);
ReleaseEntryNextLock(entry);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void AcquireEntryBothLocks(Entry* entry)
{
if (entry == null) {
return;
}
AcquireEntryPrevLock(entry);
AcquireEntryNextLock(entry);
}
#endregion
}
}
@@ -54,7 +54,7 @@ namespace StgSharp.HighPerformance.Memory
if (byteSize < 64) {
throw new InvalidOperationException();
}
_entries = SlabAllocator<Entry>.Create(64, BufferLayout.Chunked);
_entries = SlabAllocator<Entry>.Create(64, SlabBufferLayout.Chunked, false);
_size = byteSize;
m_Buffer = (byte*)NativeMemory.AlignedAlloc(byteSize, 16);
_align = 16;
@@ -62,12 +62,10 @@ namespace StgSharp.HighPerformance.Memory
InitializeBucketSystem();
_spareMemory = (Entry*)_entries.Allocate();
SetEntryState(_spareMemory, EntryState.ThreadOccupied);
_spareMemory->State = EntryState.Empty;
_spareMemory->PreviousNear = EmptyHandle;
_spareMemory->NextNear = EmptyHandle;
_spareMemory->Position = (nuint)m_Buffer;
_spareMemory->PrevLock = 0;
_spareMemory->NextLock = 0;
_spareMemory->Size = (uint)byteSize;
}
@@ -77,7 +75,7 @@ namespace StgSharp.HighPerformance.Memory
throw new ArgumentException(
"Alignment must be a power of two and at least 8 bytes.");
}
_entries = SlabAllocator<Entry>.Create(64, BufferLayout.Chunked);
_entries = SlabAllocator<Entry>.Create(64, SlabBufferLayout.Chunked);
m_Buffer = (byte*)NativeMemory.AlignedAlloc(byteSize, (nuint)align);
_align = align;
_size = byteSize;
@@ -85,12 +83,10 @@ namespace StgSharp.HighPerformance.Memory
InitializeBucketSystem();
_spareMemory = (Entry*)_entries.Allocate();
SetEntryState(_spareMemory, EntryState.ThreadOccupied);
_spareMemory->State = EntryState.Empty;
_spareMemory->PreviousNear = EmptyHandle;
_spareMemory->NextNear = EmptyHandle;
_spareMemory->Position = (nuint)m_Buffer;
_spareMemory->PrevLock = 0;
_spareMemory->NextLock = 0;
_spareMemory->Size = (uint)byteSize;
}
@@ -1,40 +0,0 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="ISlabAllocatable.cs"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the “Software”), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software,
// and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and
// this permission notice shall be included in all copies
// or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”,
// WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH
// THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StgSharp.HighPerformance.Memory
{
public interface ISlabAllocatable { }
}
@@ -32,21 +32,17 @@ using StgSharp.Threading;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace StgSharp.HighPerformance.Memory
{
internal sealed unsafe class ChunkedSlabAllocator<T> : SlabAllocator<T> where T: unmanaged
{
private readonly BufferExpansionLock _lock = new();
private ConcurrentStackBuffer<nuint> _buffers;
private ConcurrentStackBuffer<nuint> _recycle;
private BufferStack<nuint> _buffers;
private BufferStack<nuint> _recycle;
private readonly int _elementSize = Unsafe.SizeOf<T>();
private nuint _currentBuffer;
@@ -67,7 +63,7 @@ namespace StgSharp.HighPerformance.Memory
span[i] = _currentBuffer + (nuint)(i * _elementSize);
}
_buffers = new(4);
_recycle = new(span);
_recycle = BufferStackBuilder.Create(span);
_currentCapacity = count;
_currentIndex = (nuint)initCount;
}
@@ -77,42 +73,20 @@ namespace StgSharp.HighPerformance.Memory
if (_recycle.TryPop(out nuint handle)) {
return handle;
}
while (true)
nuint bufferHandle = _currentBuffer;
nuint curIndex = _currentIndex;
if (curIndex >= _currentCapacity)
{
nuint bufferHandle = Volatile.Read(ref _currentBuffer);
nuint curIndex = Volatile.Read(ref _currentIndex);
_lock.EnterMetaDataRead();
try
{
if (curIndex >= _currentCapacity)
{
_lock.ExitMetaDataRead();
_lock.EnterExpansionProcess();
try
{
nuint cap = Volatile.Read(ref _currentCapacity);
nuint newBuffer = (nuint)NativeMemory.Alloc(cap * (nuint)_elementSize);
_buffers.Push(bufferHandle);
_ = Interlocked.Exchange(ref _currentBuffer, newBuffer);
_ = Interlocked.Exchange(ref _currentIndex, 0);
}
finally
{
_lock.ExitExpansionProcess();
}
}
curIndex = Volatile.Read(ref _currentIndex);
_ = Interlocked.Add(
ref Unsafe.As<nuint, ulong>(ref _currentIndex), (ulong)_elementSize);
return _currentBuffer + ((nuint)_elementSize * curIndex);
}
finally
{
if (_lock.IsThreadReadingMetaData) {
_lock.ExitMetaDataRead();
}
}
nuint cap = _currentCapacity;
nuint newBuffer = (nuint)NativeMemory.Alloc(cap * (nuint)_elementSize);
_buffers.Push(bufferHandle);
_currentBuffer = newBuffer;
_currentIndex = 0;
}
curIndex = _currentIndex;
_ = Interlocked.Add(
ref Unsafe.As<nuint, ulong>(ref _currentIndex), (ulong)_elementSize);
return _currentBuffer + ((nuint)_elementSize * curIndex);
}
public override void Dispose()
@@ -121,7 +95,6 @@ namespace StgSharp.HighPerformance.Memory
while (_buffers.TryPop(out nuint buffer)) {
NativeMemory.Free((void*)buffer);
}
_lock.Dispose();
_buffers.Dispose();
_recycle.Dispose();
}
@@ -1,161 +0,0 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="SlabAllocator.Sequencial.cs"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the “Software”), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software,
// and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and
// this permission notice shall be included in all copies
// or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”,
// WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH
// THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using StgSharp.Threading;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
namespace StgSharp.HighPerformance.Memory
{
public sealed unsafe class SequentialSlabAllocator<T> : SlabAllocator<T> where T: unmanaged
{
private T* _buffer;
private readonly BufferExpansionLock _lock = new();
private readonly ConcurrentStackBuffer<nuint> _stack;
private readonly int _elementSize;
private nuint _highestAllocated;
private ulong _capacity;
public SequentialSlabAllocator(nuint initialCapacity)
{
_elementSize = Unsafe.SizeOf<T>();
_capacity = initialCapacity;
_highestAllocated = 0;
_buffer = (T*)NativeMemory.Alloc((nuint)_capacity * (nuint)_elementSize);
int initCount = _capacity switch
{
<= 4 => 4,
> 64 => 64,
_ => (int)_capacity / 4
};
Span<nuint> span = stackalloc nuint[initCount];
for (int i = 0; i < initCount; i++) {
span[i] = (nuint)i;
}
_stack = new(span);
_highestAllocated += (nuint)initCount;
}
internal T* BasePointer
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _buffer;
}
public override nuint Allocate()
{
if (_stack.TryPop(out nuint index)) {
return index;
}
while (true)
{
index = Volatile.Read(ref _highestAllocated);
_lock.EnterMetaDataRead();
try
{
if (index >= _capacity)
{
_lock.ExitMetaDataRead();
_lock.EnterExpansionProcess();
try
{
_capacity = Volatile.Read(ref _capacity) * 2;
nuint newSize = (nuint)_capacity * (nuint)_elementSize;
_lock.EnterBufferCopy();
try
{
_buffer = (T*)NativeMemory.Realloc(_buffer, newSize);
}
finally
{
_lock.ExitBufferCopy();
}
}
finally
{
_lock.ExitExpansionProcess();
}
}
_ = Interlocked.Increment(ref Unsafe.As<nuint, ulong>(ref _highestAllocated));
return index;
}
finally
{
if (_lock.IsThreadReadingMetaData) {
_lock.ExitMetaDataRead();
}
}
}
}
public override void Dispose()
{
_stack.Dispose();
NativeMemory.Free(_buffer);
_lock.Dispose();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void EnterBufferReading()
{
_lock.EnterBufferRead();
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override void ExitBufferReading()
{
_lock.ExitBufferRead();
}
public override void Free(nuint index)
{
_stack.Push(index);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override SlabAllocationHandle<T> ReadAllocation()
{
EnterBufferReading();
return new SlabAllocationHandle<T>(this, true);
}
~SequentialSlabAllocator()
{
Dispose();
}
}
}
@@ -28,17 +28,7 @@
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using StgSharp.HighPerformance.Memory;
using StgSharp.Threading;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace StgSharp.HighPerformance.Memory
{
@@ -47,12 +37,62 @@ namespace StgSharp.HighPerformance.Memory
public abstract nuint Allocate();
public static SlabAllocator<T> Create(nuint count, BufferLayout layout)
/// <summary>
/// Creates a new SLAB allocator instance with the specified configuration.
/// </summary>
/// <param name="count">
/// Initial number of objects that can be allocated.
/// </param>
/// <param name="layout">
/// Memory layout strategy for the SLAB allocator.
/// </param>
/// <param name="concurrentSupport">
/// Whether to enable concurrent access support.
/// </param>
/// <returns>
/// A new SLAB allocator instance.
/// </returns>
/// <exception cref="ArgumentOutOfRangeException">
/// Thrown when an invalid layout is specified.
/// </exception>
/// <exception cref="NotSupportedException">
/// Thrown when requesting unsupported configurations.
/// </exception>
/// <remarks>
/// <para><strong> Important Threading Considerations: </strong></para> <para> While SLAB
/// allocators themselves may be designed for single-threaded use, allocated objects are
/// frequently shared across multiple threads in real-world applications. Due to the
/// special implementation of expandable concurrent sequential SLAB allocators, they rely
/// on internal expansion locks to ensure that memory buffer expansions (which involve
/// copying the entire buffer to a new location) do not occur while other threads are
/// reading from allocated regions.</para> <para><strong> Sequential Layout Limitations:
/// </strong><br /> Sequential layout SLAB allocators cannot provide a single-threaded
/// version because:<list type="bullet"><item><description> They use contiguous memory
/// layout that requires buffer expansion when capacity is exceeded
/// </description></item><item><description> Buffer expansion involves copying all
/// existing data to a new memory location </description></item><item><description>
/// Without proper synchronization, concurrent access to allocated objects during
/// expansion would cause memory corruption </description></item><item><description> The
/// expansion lock mechanism is essential even if allocation itself is single-threaded
/// </description></item></list></para> <para><strong> Chunked Layout: </strong><br />
/// Chunked layout allocators can optionally provide single-threaded versions since they
/// don't require buffer expansion and copying. Each chunk is allocated independently,
/// avoiding the memory relocation issues present in sequential layouts.</para>
/// </remarks>
public static SlabAllocator<T> Create(nuint count, SlabBufferLayout layout, bool concurrentSupport = true)
{
if (concurrentSupport) {
return layout switch
{
SlabBufferLayout.Sequential => new ConcurrentSequentialSlabAllocator<T>(count),
SlabBufferLayout.Chunked => new ConcurrentChunkedSlabAllocator<T>(count),
_ => throw new ArgumentOutOfRangeException(nameof(layout), layout, null)
};
}
return layout switch
{
BufferLayout.Sequential => new SequentialSlabAllocator<T>(count),
BufferLayout.Chunked => new ChunkedSlabAllocator<T>(count),
SlabBufferLayout.Sequential => throw new NotSupportedException("Non-concurrent version of sequential layout SLAB is not supported due to expansion lock requirements for memory safety during buffer expansion."),
SlabBufferLayout.Chunked => new ChunkedSlabAllocator<T>(count),
_ => throw new ArgumentOutOfRangeException(nameof(layout), layout, null)
};
}
@@ -69,12 +109,11 @@ namespace StgSharp.HighPerformance.Memory
}
public enum BufferLayout
public enum SlabBufferLayout
{
Sequential,
Chunked,
HybridLayerSegregatedFit
Chunked
}
}
@@ -1,100 +0,0 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="UnmanagedMemoryHandle.cs"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the “Software”), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software,
// and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and
// this permission notice shall be included in all copies
// or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”,
// WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH
// THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Buffers;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace StgSharp.HighPerformance.Memory
{
public unsafe struct UnmanagedMemoryHandle
{
public readonly byte* BufferHandle, AllocatorHandle;
public readonly nuint Size;
public UnmanagedMemoryHandle(byte* pointer, byte* handle, nuint s)
{
BufferHandle = pointer;
AllocatorHandle = handle;
Size = s;
}
public readonly ref byte this[nuint index]
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => ref Unsafe.AddByteOffset(ref Unsafe.AsRef<byte>(BufferHandle), index);
}
public readonly Enumerator GetEnumerator()
{
return new Enumerator(this);
}
public ref struct Enumerator : IEnumerator<byte>
{
private nuint _index = nuint.MaxValue;
private UnmanagedMemoryHandle _handle;
internal Enumerator(UnmanagedMemoryHandle span)
{
_handle = span;
}
public ref byte RefCurrent => ref _handle[_index];
public readonly void Dispose() { }
public bool MoveNext()
{
_index++;
return _index < _handle.Size;
}
public void Reset()
{
_index = 0;
}
object IEnumerator.Current => RefCurrent;
byte IEnumerator<byte>.Current => RefCurrent;
}
}
}
@@ -1,52 +0,0 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="UnmanagedMemoryPool.BucketTLSF.cs"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the “Software”), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software,
// and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and
// this permission notice shall be included in all copies
// or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”,
// WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH
// THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using StgSharp.HighPerformance.Memory;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace StgSharp.HighPerformance.Memory
{
public unsafe partial class UnmanagedMemoryPool
{
private struct Entry : ISlabAllocatable
{
public Entry* NextNear, PreviousNear;
}
}
}
@@ -1,51 +0,0 @@
//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="UnmanagedMemoryPool.cs"
// Project: StgSharp
// AuthorGroup: Nitload Space
// Copyright (c) Nitload Space. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the “Software”), to deal in the Software without restriction,
// including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software,
// and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and
// this permission notice shall be included in all copies
// or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”,
// WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH
// THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using System;
using System.Collections.Concurrent;
using System.Drawing;
using System.Net.Security;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
using System.Transactions;
namespace StgSharp.HighPerformance.Memory
{
public unsafe partial class UnmanagedMemoryPool
{
private byte* m_Buffer;
private SlabAllocator<Entry> _nodes = new(64);
public
}
}