clear merging conflict

This commit is contained in:
Youmu Liu
2025-08-25 10:11:20 +08:00
parent 11b630ffa2
commit 88e9ee97e9
112 changed files with 1711 additions and 6912 deletions
@@ -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;
@@ -57,9 +57,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);
if (TryPopLevelForAllocation(i, s, out Entry* handle)) {
return new hlsfHandle(handle, size);
}
@@ -82,10 +80,8 @@ namespace StgSharp.HighPerformance.Memory
}
}
if (handle != null)
{
if (handle != null) {
SliceMemory(handle, (segmentIndex + 1) * levelSizeArray[level]);
SetEntryState(handle, EntryState.Allocated);
}
return new hlsfHandle(handle, size);
@@ -106,14 +102,14 @@ namespace StgSharp.HighPerformance.Memory
throw new InvalidOperationException("Entry position offset is not aligned to 64 bytes.");
}
int minLevel = Math.Max((BitOperations.TrailingZeroCount(originOffset) - 6) / 2, 0);
int minLevel = Math.Max((BitOperations.TrailingZeroCount(finalOffset) - 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) / (ulong)levelSize);
int blockCount = (int)((ulong.Min(board, end) - board) / (ulong)levelSize);
if (blockCount == 0)
{
break;
@@ -126,7 +122,6 @@ namespace StgSharp.HighPerformance.Memory
newEntry->Size = (uint)s;
BucketNode* bucket = (BucketNode*)newEntry->Position;
bucket->EntryRef = newEntry;
newEntry->State = EntryState.ThreadOccupied;
// link new entry
newEntry->PreviousNear = (ulong)current;
@@ -155,7 +150,7 @@ namespace StgSharp.HighPerformance.Memory
{
int levelSize = levelSizeArray[i];
ulong board = (finalOffset + (ulong)levelSize - 1) & ~((ulong)levelSize - 1);
int blockCount = (int)(ulong.Min(board, end) / (ulong)levelSize);
int blockCount = (int)((ulong.Min(board, end) - finalOffset) / (ulong)levelSize);
if (blockCount == 0)
{
continue;
@@ -168,7 +163,6 @@ namespace StgSharp.HighPerformance.Memory
newEntry->Size = (uint)s;
BucketNode* bucket = (BucketNode*)newEntry->Position;
bucket->EntryRef = newEntry;
newEntry->State = EntryState.ThreadOccupied;
// link new entry
newEntry->PreviousNear = (ulong)current;
@@ -229,7 +223,6 @@ namespace StgSharp.HighPerformance.Memory
handle = (Entry*)_entries.Allocate();
handle->Position = pos;
handle->Size = (uint)blockSize;
SetEntryState(handle, EntryState.ThreadOccupied);
handle->Level = GetLevelFromSize((uint)blockSize);
// Update spare memory position
@@ -48,13 +48,13 @@ namespace StgSharp.HighPerformance.Memory
return;
}
if (GetEntryState(e) != EntryState.Allocated) {
if (e->State != EntryState.Allocated) {
return;
}
// Set to ThreadOccupied to prevent other operations
SetEntryState(e, EntryState.ThreadOccupied);
e->State = EntryState.Empty;
// Try merge with adjacent free blocks and remove them from buckets
@@ -68,7 +68,7 @@ namespace StgSharp.HighPerformance.Memory
// Set the merged entry state to Empty (ready to be reused)
SetEntryState(e, EntryState.Empty);
e->State = EntryState.Empty;
// Push the merged entry back to appropriate bucket
@@ -91,8 +91,6 @@ namespace StgSharp.HighPerformance.Memory
RemoveFromBucket(originLevel, segment, (BucketNode*)entry->Position);
SetEntryState(entry, EntryState.Merging);
while (originLevel >= entry->Level)
{
originLevel = entry->Level;
@@ -101,12 +99,11 @@ namespace StgSharp.HighPerformance.Memory
// backward merging - simplified to single attempt
ulong p = entry->PreviousNear;
Entry* prev = (Entry*)p;
if (prev != null ||
prev != _spareMemory ||
GetEntryState(prev) == EntryState.Empty ||
if (prev == null ||
prev == _spareMemory ||
!AssertBackwardBoundary(entry->Position, prev, originLevel) ||
IsAdjacentTo(entry, prev) ||
entry->Level == prev->Level ||
entry->Level != prev->Level ||
!TryRemoveFromBucket(originLevel, DetermineSegmentIndex(prev->Size, originLevel), (BucketNode*)prev->Position))
{
break;
@@ -130,12 +127,11 @@ namespace StgSharp.HighPerformance.Memory
// forward merging - simplified to single attempt
ulong n = entry->NextNear;
Entry* next = (Entry*)n;
if (next != null ||
next != _spareMemory ||
GetEntryState(next) == EntryState.Empty ||
if (next == null ||
next == _spareMemory ||
!AssertForwardBoundary(entry->Position + entry->Size, next, entry->Level) ||
IsAdjacentTo(entry, next) ||
entry->Level == next->Level ||
entry->Level != next->Level ||
!TryRemoveFromBucket(originLevel, DetermineSegmentIndex(next->Size, originLevel), (BucketNode*)next->Position))
{
break;
@@ -148,14 +144,12 @@ namespace StgSharp.HighPerformance.Memory
}
entry->NextNear = (ulong)nextNext;
// Merge
entry->Size += next->Size;
RecycleEntry(next);
}
entry->Level = GetLevelFromSize(entry->Size);
}
SetEntryState(entry, EntryState.Empty);
}
private void RecycleEntry(Entry* entry)
@@ -164,7 +158,6 @@ namespace StgSharp.HighPerformance.Memory
return;
}
entry->State = EntryState.Empty;
entry->PreviousNear = EmptyHandle;
entry->NextNear = EmptyHandle;
_entries.Free((nuint)entry);
@@ -199,27 +192,24 @@ namespace StgSharp.HighPerformance.Memory
/// Entry structure - streamlined version (only position and state information) Stored in
/// external SLAB
/// </summary>
[StructLayout(LayoutKind.Explicit, Size = 32)]
[StructLayout(LayoutKind.Explicit, Size = 36)]
internal struct Entry
{
[FieldOffset(28)] internal int Level; // Level of the block in the allocator
[FieldOffset(20)] internal int State; // Node state (using int for atomic operations)
[FieldOffset(24)] internal uint Size; // Block size information
[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
}
@@ -237,56 +227,6 @@ namespace StgSharp.HighPerformance.Memory
return entry == null;
}
// Entry 状态操作
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool TrySetEntryState(Entry* entry, int expectedState, int newState)
{
if (entry != null && entry->State == expectedState)
{
entry->State = newState;
return true;
}
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int GetEntryState(Entry* entry)
{
return entry != null ? entry->State : 0;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static void SetEntryState(Entry* entry, int newState)
{
if (entry != null) {
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;
}
#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,7 +62,7 @@ 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;
@@ -75,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;
@@ -83,7 +83,7 @@ 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;
@@ -32,12 +32,9 @@ 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
{
@@ -45,8 +42,8 @@ namespace StgSharp.HighPerformance.Memory
{
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 +64,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;
}
@@ -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
}
}
+7 -5
View File
@@ -29,17 +29,21 @@
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
using StgSharp.HighPerformance;
using StgSharp.HighPerformance.Memory;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
using hlsfHandle = StgSharp.HighPerformance.Memory.HybridLayerSegregatedFitAllocationHandle;
namespace StgSharp.Mathematics
{
public abstract unsafe partial class Matrix
public abstract unsafe partial class Matrix<T> where T: unmanaged, INumber<T>
{
public abstract ref float this[int column, int row]
@@ -49,10 +53,8 @@ namespace StgSharp.Mathematics
public abstract MatrixKernel* GetMatrixKernel(int column, int row);
[Obsolete(
"This method does not contain boarder check, so be careful on size or use indexer instead.",
false)]
public abstract float* UnsafeIndex(int column, int row);
[Obsolete("This method does not contain boarder check, so be careful on size or use indexer instead.", false)]
public abstract T* UnsafeIndex(int column, int row);
}
}
@@ -1,97 +0,0 @@
//-----------------------------------------------------------------------
//-----------------------------------------------------------------------
// file="VecAny.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;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace StgSharp.Mathematics
{
[CollectionBuilder(typeof(VecAnyBuilder), nameof(VecAnyBuilder.Build))]
public class VecAny : IEnumerable<float>
{
private float[] _values;
internal VecAny(ReadOnlySpan<float> values)
{
_values = values.ToArray();
}
public VecAny(int size)
{
if (size <= 0) {
throw new ArgumentOutOfRangeException(
nameof(size), "Size must be greater than zero.");
}
_values = new float[size];
}
public VecAny? this[ReadOnlySpan<MatrixIndexLabel> span]
{
get
{
if (span.Length == 0) {
return null;
}
int size = 0;
foreach (MatrixIndexLabel label in span) {
size += label.Length(_values.Length);
}
return new VecAny(size);
}
}
public IEnumerator<float> GetEnumerator()
{
return ((IEnumerable<float>)_values).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return _values.GetEnumerator();
}
}
public class VecAnyBuilder
{
public static VecAny Build(ReadOnlySpan<float> span)
{
return new VecAny(span);
}
}
}
+28 -13
View File
@@ -6,7 +6,7 @@
<LangVersion>Preview</LangVersion>
<ProduceReferenceAssembly>True</ProduceReferenceAssembly>
<GenerateDocumentationFile>True</GenerateDocumentationFile>
<Platforms>AnyCPU;x64</Platforms>
<Platforms>x64;ARM64</Platforms>
<GeneratePackageOnBuild>False</GeneratePackageOnBuild>
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
<PackageLicenseExpression> MIT</PackageLicenseExpression>
@@ -21,6 +21,10 @@
<Optimize>True</Optimize>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
<Optimize>True</Optimize>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Optimize>False</Optimize>
<DebugType>portable</DebugType>
@@ -28,6 +32,13 @@
<Deterministic>False</Deterministic>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|ARM64'">
<Optimize>False</Optimize>
<DebugType>portable</DebugType>
<NoWarn>1701;1702;0649;1591;0169</NoWarn>
<Deterministic>False</Deterministic>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Benchmark|x64'">
<Optimize>True</Optimize>
<DebugType>embedded</DebugType>
@@ -35,18 +46,11 @@
<Deterministic>False</Deterministic>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DebugType>portable</DebugType>
<Optimize>False</Optimize>
<NoWarn>1701;1702;0649;1591;0169</NoWarn>
<Deterministic>False</Deterministic>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DebugType>none</DebugType>
<NoWarn>1701;1702;0649;1591;0169</NoWarn>
<Deterministic>False</Deterministic>
<Optimize>True</Optimize>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Benchmark|ARM64'">
<Optimize>True</Optimize>
<DebugType>embedded</DebugType>
<NoWarn>1701;1702;0649;1591;0169</NoWarn>
<Deterministic>False</Deterministic>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
@@ -56,6 +60,13 @@
<Optimize>True</Optimize>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|ARM64'">
<DebugType>none</DebugType>
<NoWarn>1701;1702;0649;1591;0169</NoWarn>
<Deterministic>False</Deterministic>
<Optimize>True</Optimize>
</PropertyGroup>
<PropertyGroup>
<IsWindows Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true'">true</IsWindows>
<IsOSX Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' == 'true'">true</IsOSX>
@@ -107,6 +118,10 @@
<Deterministic>False</Deterministic>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='NoOps|ARM64'">
<Deterministic>False</Deterministic>
</PropertyGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="xcopy /E /I /Y &quot;$(OutDir)&quot; &quot;$(SolutionDir)build&quot;" />
</Target>
+3 -1
View File
@@ -1,4 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup />
<PropertyGroup>
<_LastSelectedProfileId>D:\NitloadIndustry\研究项目\1-Flagship\TH2020\ProjectFile\内核源代码\StgSharp.Common\Properties\PublishProfiles\FolderProfile.pubxml</_LastSelectedProfileId>
</PropertyGroup>
</Project>
@@ -45,7 +45,7 @@ namespace StgSharp.Timing
public sealed class TimeSpanAwaitingToken : IDisposable
{
private static readonly ConcurrentStackBuffer<int> s_unusedID = new(1);
private static readonly ConcurrentBufferStack<int> s_unusedID = new(1);
private static int s_maxID;