hlsf wrong slicing solving
This commit is contained in:
@@ -80,26 +80,27 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
private void SliceMemory(Entry* e, int offset)
|
private void SliceMemory(Entry* e, int offset)
|
||||||
{
|
{
|
||||||
Entry* current = e;
|
Entry* current = e;
|
||||||
Entry* n = e->NextNear;
|
Entry* next = e->NextNear;
|
||||||
Entry* next = e;
|
|
||||||
|
|
||||||
ulong originOffset = e->Position - (ulong)m_Buffer;
|
ulong originOffset = e->Position - (ulong)m_Buffer;
|
||||||
ulong finalOffset = originOffset + (ulong)offset;
|
ulong finalOffset = originOffset + (ulong)offset;
|
||||||
ulong end = e->Position + e->Size;
|
ulong end = e->Position + e->Size;
|
||||||
ulong endOffset = end - (ulong)m_Buffer;
|
ulong endOffset = end - (ulong)m_Buffer;
|
||||||
int remainSize = (int)(e->Size - offset);
|
ulong remainSize = (ulong)e->Size - (ulong)offset;
|
||||||
|
|
||||||
// get level of left edge
|
// get level of left edge
|
||||||
int minLevel = int.Max((BitOperations.TrailingZeroCount(finalOffset) - 6) / 2, 0);
|
int minLevel = int.Max((BitOperations.TrailingZeroCount(finalOffset) - 6) / 2, 0);
|
||||||
int i = minLevel >= levelSizeArray.Length ? levelSizeArray.Length - 1 : minLevel;
|
int i = minLevel >= levelSizeArray.Length ? levelSizeArray.Length - 1 : minLevel;
|
||||||
|
try
|
||||||
|
{
|
||||||
for (; i < levelSizeArray.Length; i++)
|
for (; i < levelSizeArray.Length; i++)
|
||||||
{
|
{
|
||||||
int levelSize = levelSizeArray[i];
|
int levelSize = levelSizeArray[i];
|
||||||
ulong levelLong = (ulong)levelSize;
|
ulong levelLong = (ulong)levelSize;
|
||||||
ulong levelUpper = levelLong * 4;
|
ulong levelUpper = levelLong * 4;
|
||||||
|
|
||||||
|
// smallest board that is larger than finalOffset
|
||||||
ulong board = (finalOffset + levelUpper - 1) / levelUpper * levelUpper;
|
ulong board = (finalOffset + levelUpper - 1) / levelUpper * levelUpper;
|
||||||
int blockCount = (int)((board - ulong.Min(finalOffset, endOffset)) / levelLong);
|
int blockCount = (int)((long)ulong.Min(board, endOffset) - (long)finalOffset) / (int)levelLong;
|
||||||
if (blockCount == 0)
|
if (blockCount == 0)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
@@ -108,26 +109,68 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
|
|
||||||
// create and init new entry
|
// create and init new entry
|
||||||
Entry* newEntry = (Entry*)_entries.Allocate();
|
Entry* newEntry = (Entry*)_entries.Allocate();
|
||||||
newEntry->Position = (nuint)((ulong)m_Buffer + finalOffset);
|
newEntry->State = EntryState.Empty;
|
||||||
|
newEntry->Position = (nuint)m_Buffer + (nuint)finalOffset;
|
||||||
newEntry->Size = (uint)s;
|
newEntry->Size = (uint)s;
|
||||||
BucketNode* bucket = (BucketNode*)newEntry->Position;
|
BucketNode* bucket = (BucketNode*)newEntry->Position;
|
||||||
bucket->EntryRef = newEntry;
|
bucket->EntryRef = newEntry;
|
||||||
|
|
||||||
// link new entry
|
// link new entry
|
||||||
newEntry->PreviousNear = current;
|
newEntry->PreviousNear = current;
|
||||||
newEntry->NextNear = n;
|
newEntry->NextNear = next;
|
||||||
current->NextNear = newEntry;
|
current->NextNear = newEntry;
|
||||||
next->PreviousNear = newEntry;
|
next->PreviousNear = newEntry;
|
||||||
|
|
||||||
newEntry->Level = i;
|
newEntry->Level = i;
|
||||||
PushLevel(i, blockCount - 1, bucket);
|
PushLevel(i, Math.Min(blockCount - 1, 2), bucket);
|
||||||
|
|
||||||
// set new entry
|
// set new entry
|
||||||
Entry* old = current;
|
// Entry* old = current;
|
||||||
current = newEntry;
|
current = newEntry;
|
||||||
|
|
||||||
finalOffset += (ulong)s;
|
finalOffset += (ulong)s;
|
||||||
remainSize -= s;
|
remainSize -= (ulong)s;
|
||||||
|
if (remainSize <= (ulong)levelSize)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (remainSize <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i >= 10)
|
||||||
|
{
|
||||||
|
int levelSize = levelSizeArray[^1] * 4;
|
||||||
|
ulong levelLong = (ulong)levelSize;
|
||||||
|
ulong board = endOffset / levelLong * levelLong;
|
||||||
|
int blockCount = (int)((board - finalOffset) / (ulong)levelSize);
|
||||||
|
if (blockCount > 0)
|
||||||
|
{
|
||||||
|
int s = blockCount * levelSize;
|
||||||
|
Entry* newEntry = (Entry*)_entries.Allocate();
|
||||||
|
newEntry->State = EntryState.Empty;
|
||||||
|
newEntry->Position = (nuint)((ulong)m_Buffer + finalOffset);
|
||||||
|
newEntry->Size = (uint)s;
|
||||||
|
BucketNode* bucket = (BucketNode*)newEntry->Position;
|
||||||
|
bucket->EntryRef = newEntry;
|
||||||
|
|
||||||
|
|
||||||
|
// link new entry
|
||||||
|
newEntry->PreviousNear = current;
|
||||||
|
newEntry->NextNear = next;
|
||||||
|
current->NextNear = newEntry;
|
||||||
|
if (next != null) {
|
||||||
|
next->PreviousNear = newEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
newEntry->Level = i;
|
||||||
|
PushLevel(i, Math.Min(blockCount - 1, 2), bucket);
|
||||||
|
|
||||||
|
// update lock and data
|
||||||
|
finalOffset += (ulong)s;
|
||||||
|
remainSize -= (ulong)s;
|
||||||
|
}
|
||||||
if (remainSize <= 0) {
|
if (remainSize <= 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -138,15 +181,17 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
int levelSize = levelSizeArray[i];
|
int levelSize = levelSizeArray[i];
|
||||||
ulong levelLong = (ulong)levelSize;
|
ulong levelLong = (ulong)levelSize;
|
||||||
ulong board = endOffset / levelLong * levelLong;
|
ulong board = endOffset / levelLong * levelLong;
|
||||||
int blockCount = (int)((board - finalOffset) / (ulong)levelSize);
|
int blockCount = (int)(board - finalOffset) / levelSize;
|
||||||
if (blockCount == 0)
|
if (blockCount <= 0)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
int s = blockCount * levelSize;
|
int s = blockCount * levelSize;
|
||||||
|
System.Diagnostics.Debug.Assert(finalOffset + (ulong)s <= endOffset);
|
||||||
|
|
||||||
// create and init new entry
|
// create and init new entry
|
||||||
Entry* newEntry = (Entry*)_entries.Allocate();
|
Entry* newEntry = (Entry*)_entries.Allocate();
|
||||||
|
newEntry->State = EntryState.Empty;
|
||||||
newEntry->Position = (nuint)((ulong)m_Buffer + finalOffset);
|
newEntry->Position = (nuint)((ulong)m_Buffer + finalOffset);
|
||||||
newEntry->Size = (uint)s;
|
newEntry->Size = (uint)s;
|
||||||
BucketNode* bucket = (BucketNode*)newEntry->Position;
|
BucketNode* bucket = (BucketNode*)newEntry->Position;
|
||||||
@@ -154,28 +199,34 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
|
|
||||||
// link new entry
|
// link new entry
|
||||||
newEntry->PreviousNear = current;
|
newEntry->PreviousNear = current;
|
||||||
newEntry->NextNear = n;
|
newEntry->NextNear = next;
|
||||||
current->NextNear = newEntry;
|
current->NextNear = newEntry;
|
||||||
if (next != null) {
|
if (next != null) {
|
||||||
next->PreviousNear = newEntry;
|
next->PreviousNear = newEntry;
|
||||||
}
|
}
|
||||||
|
|
||||||
newEntry->Level = i;
|
newEntry->Level = i;
|
||||||
PushLevel(i, blockCount - 1, bucket);
|
PushLevel(i, Math.Min(blockCount - 1, 2), bucket);
|
||||||
|
|
||||||
// set new entry
|
// set new entry
|
||||||
Entry* old = current;
|
// Entry* old = current;
|
||||||
current = newEntry;
|
current = newEntry;
|
||||||
|
|
||||||
// update lock and data
|
// update lock and data
|
||||||
finalOffset += (ulong)s;
|
finalOffset += (ulong)s;
|
||||||
remainSize -= s;
|
remainSize -= (ulong)s;
|
||||||
if (remainSize <= 0)
|
if (remainSize <= 0)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
e->Size = (uint)(offset);
|
||||||
|
e->Level = GetLevelFromSize((uint)offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Try to allocate a new 16MB block when all buckets are empty
|
/// Try to allocate a new 16MB block when all buckets are empty
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
node->NextLevel = head;
|
node->NextLevel = head;
|
||||||
BucketNode* headNode = head;
|
BucketNode* headNode = head;
|
||||||
node->IsInBucket = true;
|
node->IsInBucket = true;
|
||||||
|
_bucketHeads[index] = node;
|
||||||
|
|
||||||
// Console.WriteLine($"Push to head of level of {levelIndex} {sizeIndex}, bucket pos {(ulong)node}, previous head is at {head}");
|
// Console.WriteLine($"Push to head of level of {levelIndex} {sizeIndex}, bucket pos {(ulong)node}, previous head is at {head}");
|
||||||
if (headNode != null)
|
if (headNode != null)
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ using System.Threading;
|
|||||||
|
|
||||||
namespace StgSharp.HighPerformance.Memory
|
namespace StgSharp.HighPerformance.Memory
|
||||||
{
|
{
|
||||||
internal sealed unsafe class ChunkedSlabAllocator<T> : SlabAllocator<T> where T: unmanaged
|
internal sealed unsafe class ChunkedSlabAllocator<T> : SlabAllocator<T> where T : unmanaged
|
||||||
{
|
{
|
||||||
|
|
||||||
private BufferStack<nuint> _buffers;
|
private BufferStack<nuint> _buffers;
|
||||||
@@ -48,6 +48,7 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
public ChunkedSlabAllocator(nuint count)
|
public ChunkedSlabAllocator(nuint count)
|
||||||
{
|
{
|
||||||
_currentBuffer = (nuint)NativeMemory.Alloc(count * (nuint)_elementSize);
|
_currentBuffer = (nuint)NativeMemory.Alloc(count * (nuint)_elementSize);
|
||||||
|
|
||||||
_currentCapacity = count;
|
_currentCapacity = count;
|
||||||
int initCount = count switch
|
int initCount = count switch
|
||||||
{
|
{
|
||||||
@@ -60,6 +61,7 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
span[i] = _currentBuffer + (nuint)(i * _elementSize);
|
span[i] = _currentBuffer + (nuint)(i * _elementSize);
|
||||||
}
|
}
|
||||||
_buffers = new(4);
|
_buffers = new(4);
|
||||||
|
_buffers.Push(_currentBuffer);
|
||||||
_recycle = BufferStackBuilder.Create(span);
|
_recycle = BufferStackBuilder.Create(span);
|
||||||
_currentCapacity = count;
|
_currentCapacity = count;
|
||||||
_currentIndex = (nuint)initCount;
|
_currentIndex = (nuint)initCount;
|
||||||
@@ -70,26 +72,27 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
if (_recycle.TryPop(out nuint handle)) {
|
if (_recycle.TryPop(out nuint handle)) {
|
||||||
return handle;
|
return handle;
|
||||||
}
|
}
|
||||||
nuint bufferHandle = _currentBuffer;
|
|
||||||
|
// nuint bufferHandle = _currentBuffer;
|
||||||
nuint curIndex = _currentIndex;
|
nuint curIndex = _currentIndex;
|
||||||
if (curIndex >= _currentCapacity)
|
if (curIndex >= _currentCapacity)
|
||||||
{
|
{
|
||||||
nuint cap = _currentCapacity;
|
nuint cap = _currentCapacity;
|
||||||
nuint newBuffer = (nuint)NativeMemory.Alloc(cap * (nuint)_elementSize);
|
nuint newBuffer = (nuint)NativeMemory.Alloc(cap * (nuint)_elementSize);
|
||||||
_buffers.Push(bufferHandle);
|
_buffers.Push(newBuffer);
|
||||||
_currentBuffer = newBuffer;
|
_currentBuffer = newBuffer;
|
||||||
_currentIndex = 0;
|
_currentIndex = 0;
|
||||||
}
|
}
|
||||||
curIndex = _currentIndex;
|
curIndex = _currentIndex;
|
||||||
_ = Interlocked.Add(
|
_currentIndex += 1;
|
||||||
ref Unsafe.As<nuint, ulong>(ref _currentIndex), (ulong)_elementSize);
|
|
||||||
return _currentBuffer + ((nuint)_elementSize * curIndex);
|
return _currentBuffer + ((nuint)_elementSize * curIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Dispose()
|
public override void Dispose()
|
||||||
{
|
{
|
||||||
NativeMemory.Free((void*)_currentBuffer);
|
// NativeMemory.Free((void*)_currentBuffer);
|
||||||
while (_buffers.TryPop(out nuint buffer)) {
|
while (_buffers.TryPop(out nuint buffer))
|
||||||
|
{
|
||||||
NativeMemory.Free((void*)buffer);
|
NativeMemory.Free((void*)buffer);
|
||||||
}
|
}
|
||||||
_buffers.Dispose();
|
_buffers.Dispose();
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ using System;
|
|||||||
|
|
||||||
namespace StgSharp.HighPerformance.Memory
|
namespace StgSharp.HighPerformance.Memory
|
||||||
{
|
{
|
||||||
public abstract class SlabAllocator<T> : IDisposable where T: unmanaged
|
public abstract class SlabAllocator<T> : IDisposable where T : unmanaged
|
||||||
{
|
{
|
||||||
|
|
||||||
public abstract nuint Allocate();
|
public abstract nuint Allocate();
|
||||||
@@ -56,29 +56,32 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
/// Thrown when requesting unsupported configurations.
|
/// Thrown when requesting unsupported configurations.
|
||||||
/// </exception>
|
/// </exception>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// <para><strong> Important Threading Considerations: </strong></para> <para>While SLAB
|
/// <para><strong> Important Threading Considerations: </strong></para> <para> While SLAB
|
||||||
/// /// allocators themselves may be designed for single-threaded use, allocated objects
|
/// /// allocators themselves may be designed for single-threaded use, allocated objects
|
||||||
/// are /// frequently shared across multiple threads in real-world applications. Due to
|
/// are /// frequently shared across multiple threads in real-world applications. Due to
|
||||||
/// the /// special implementation of expandable concurrent sequential SLAB allocators,
|
/// the /// special implementation of expandable concurrent sequential SLAB allocators,
|
||||||
/// they rely /// on internal expansion locks to ensure that memory buffer expansions
|
/// 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
|
/// (which involve /// copying the entire buffer to a new location) do not occur while
|
||||||
/// other threads are /// reading from allocated regions. </para>
|
/// other threads are /// reading from allocated regions.</para> <para><strong>
|
||||||
/// <para><strong>Sequential Layout Limitations: ///</strong><br /> Sequential layout SLAB
|
/// Sequential Layout Limitations: /// </strong><br /> Sequential layout SLAB allocators
|
||||||
/// allocators cannot provide a single-threaded /// version because:<list
|
/// cannot provide a single-threaded /// version because:<list
|
||||||
/// type="bullet"><item><description>They use contiguous memory /// layout that requires
|
/// type="bullet"><item><description> They use contiguous memory /// layout that
|
||||||
/// buffer expansion when capacity is exceeded
|
/// requires buffer expansion when capacity is exceeded ///
|
||||||
/// ///</description></item><item><description>Buffer expansion involves copying all ///
|
/// </description></item><item><description> Buffer expansion involves copying all ///
|
||||||
/// existing data to a new memory location</description></item><item><description> ///
|
/// existing data to a new memory location </description></item><item><description>///
|
||||||
/// Without proper synchronization, concurrent access to allocated objects during ///
|
/// Without proper synchronization, concurrent access to allocated objects during ///
|
||||||
/// expansion would cause memory corruption</description></item><item><description>The
|
/// expansion would cause memory corruption </description></item><item><description> The
|
||||||
/// /// expansion lock mechanism is essential even if allocation itself is single- ///
|
/// /// expansion lock mechanism is essential even if allocation itself is single- ///
|
||||||
/// threaded </description></item></list></para> <para><strong>Chunked Layout:
|
/// threaded</description></item></list></para> <para><strong> Chunked Layout: ///
|
||||||
/// ///</strong><br /> Chunked layout allocators can optionally provide single-threaded
|
/// </strong><br /> Chunked layout allocators can optionally provide single-threaded ///
|
||||||
/// /// versions since they don't require buffer expansion and copying. Each chunk is
|
/// versions since they don't require buffer expansion and copying. Each chunk is ///
|
||||||
/// /// allocated independently, avoiding the memory relocation issues present in
|
/// allocated independently, avoiding the memory relocation issues present in sequential
|
||||||
/// sequential /// layouts. </para>
|
/// /// layouts.</para>
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public static SlabAllocator<T> Create(nuint count, SlabBufferLayout layout, bool concurrentSupport = true)
|
public static SlabAllocator<T> Create(
|
||||||
|
nuint count,
|
||||||
|
SlabBufferLayout layout,
|
||||||
|
bool concurrentSupport = true)
|
||||||
{
|
{
|
||||||
if (concurrentSupport) {
|
if (concurrentSupport) {
|
||||||
return layout switch
|
return layout switch
|
||||||
|
|||||||
@@ -25,10 +25,6 @@
|
|||||||
//
|
//
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
// -----------------------------------------------------------------------
|
|
||||||
// file="HlsfStressTest" (performance benchmark)
|
|
||||||
// Project: StgSharp
|
|
||||||
// -----------------------------------------------------------------------
|
|
||||||
using StgSharp.HighPerformance.Memory;
|
using StgSharp.HighPerformance.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -46,7 +42,7 @@ namespace StgSharp
|
|||||||
int.TryParse(mbStr, out int mb) &&
|
int.TryParse(mbStr, out int mb) &&
|
||||||
mb >= 64 ?
|
mb >= 64 ?
|
||||||
(ulong)mb * 1024UL * 1024UL :
|
(ulong)mb * 1024UL * 1024UL :
|
||||||
3UL * 1024UL * 1024UL * 1024UL;
|
512UL * 1024UL * 1024UL; // lower default to 512MB
|
||||||
|
|
||||||
private static readonly uint[] SizeSweep = {
|
private static readonly uint[] SizeSweep = {
|
||||||
64, 128, 192, 256, 512, 768, 1024, 2048, 3072, 4096, 8192, 12288, 16384, 32768, 49152, 65536, 131072, 196608, 262144, 524288, 786432, 1_048_576, 2_097_152, 3_145_728, 4_194_304, 8_388_608, 12_582_912, 16_777_216, 25_165_824, 33_554_432
|
64, 128, 192, 256, 512, 768, 1024, 2048, 3072, 4096, 8192, 12288, 16384, 32768, 49152, 65536, 131072, 196608, 262144, 524288, 786432, 1_048_576, 2_097_152, 3_145_728, 4_194_304, 8_388_608, 12_582_912, 16_777_216, 25_165_824, 33_554_432
|
||||||
@@ -56,12 +52,14 @@ namespace StgSharp
|
|||||||
{
|
{
|
||||||
Console.WriteLine("=== HLSF Allocator Single-Thread Performance Benchmark ===");
|
Console.WriteLine("=== HLSF Allocator Single-Thread Performance Benchmark ===");
|
||||||
Console.WriteLine($"Arena size: {ArenaBytes / (1024 * 1024)} MiB (env override: HLSF_ARENA_MB)");
|
Console.WriteLine($"Arena size: {ArenaBytes / (1024 * 1024)} MiB (env override: HLSF_ARENA_MB)");
|
||||||
using HybridLayerSegregatedFitAllocator allocator = new HybridLayerSegregatedFitAllocator((nuint)ArenaBytes);
|
|
||||||
|
|
||||||
Warmup(allocator);
|
|
||||||
|
|
||||||
Console.WriteLine();
|
Console.WriteLine();
|
||||||
Console.WriteLine("[1] Allocation throughput (no touch)");
|
Console.WriteLine("[1] Allocation throughput (no touch)");
|
||||||
|
using (HybridLayerSegregatedFitAllocator allocator = new HybridLayerSegregatedFitAllocator((nuint)ArenaBytes))
|
||||||
|
{
|
||||||
|
Warmup(allocator);
|
||||||
|
|
||||||
foreach (uint sz in SizeSweep)
|
foreach (uint sz in SizeSweep)
|
||||||
{
|
{
|
||||||
uint size = sz > MaxAllocSize ? MaxAllocSize : sz;
|
uint size = sz > MaxAllocSize ? MaxAllocSize : sz;
|
||||||
@@ -69,9 +67,14 @@ namespace StgSharp
|
|||||||
GC.Collect();
|
GC.Collect();
|
||||||
GC.WaitForPendingFinalizers();
|
GC.WaitForPendingFinalizers();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Console.WriteLine();
|
Console.WriteLine();
|
||||||
Console.WriteLine("[2] Allocation throughput (touch first byte)");
|
Console.WriteLine("[2] Allocation throughput (touch first byte)");
|
||||||
|
using (HybridLayerSegregatedFitAllocator allocator = new HybridLayerSegregatedFitAllocator((nuint)ArenaBytes))
|
||||||
|
{
|
||||||
|
Warmup(allocator);
|
||||||
|
|
||||||
foreach (uint sz in SizeSweep)
|
foreach (uint sz in SizeSweep)
|
||||||
{
|
{
|
||||||
uint size = sz > MaxAllocSize ? MaxAllocSize : sz;
|
uint size = sz > MaxAllocSize ? MaxAllocSize : sz;
|
||||||
@@ -79,19 +82,31 @@ namespace StgSharp
|
|||||||
GC.Collect();
|
GC.Collect();
|
||||||
GC.WaitForPendingFinalizers();
|
GC.WaitForPendingFinalizers();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Console.WriteLine();
|
Console.WriteLine();
|
||||||
|
using (HybridLayerSegregatedFitAllocator allocator = new HybridLayerSegregatedFitAllocator((nuint)ArenaBytes))
|
||||||
|
{
|
||||||
|
Warmup(allocator);
|
||||||
|
|
||||||
Console.WriteLine("[3] Free throughput after pre-allocation (256k blocks of 1KB)");
|
Console.WriteLine("[3] Free throughput after pre-allocation (256k blocks of 1KB)");
|
||||||
BenchFreeThroughput(allocator, 256 * 1024, 1024);
|
BenchFreeThroughput(allocator, 256 * 1024, 1024);
|
||||||
|
}
|
||||||
Console.WriteLine();
|
Console.WriteLine();
|
||||||
Console.WriteLine("[4] Alloc+Immediate Free pairs (5,000,000 x 256B)");
|
Console.WriteLine("[4] Alloc+Immediate Free pairs (5,000,000 x 256B)");
|
||||||
BenchAllocFreePairs(allocator, 5_000_000, 256);
|
using (HybridLayerSegregatedFitAllocator allocator = new HybridLayerSegregatedFitAllocator((nuint)ArenaBytes))
|
||||||
|
{
|
||||||
|
Warmup(allocator);
|
||||||
|
|
||||||
|
BenchAllocFreePairs(allocator, 5_000_000, 256);
|
||||||
|
}
|
||||||
Console.WriteLine();
|
Console.WriteLine();
|
||||||
Console.WriteLine("[5] Fragmentation cycle (allocate ~1024MB random, free half, reallocate)");
|
Console.WriteLine("[5] Fragmentation cycle (allocate ~1024MB random, free half, reallocate)");
|
||||||
BenchFragmentationCycle(allocator, 1024);
|
using (HybridLayerSegregatedFitAllocator allocator = new HybridLayerSegregatedFitAllocator((nuint)ArenaBytes))
|
||||||
|
{
|
||||||
|
Warmup(allocator);
|
||||||
|
|
||||||
|
BenchFragmentationCycle(allocator, 512); // target within 500MB arena
|
||||||
|
}
|
||||||
Console.WriteLine();
|
Console.WriteLine();
|
||||||
Console.WriteLine("=== Benchmark Complete ===");
|
Console.WriteLine("=== Benchmark Complete ===");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,30 +25,8 @@
|
|||||||
//
|
//
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
using StgSharp.Collections;
|
|
||||||
using StgSharp.Controlling.UsrActivity;
|
|
||||||
using StgSharp.Geometries;
|
|
||||||
using StgSharp.Graphics;
|
|
||||||
using StgSharp.HighPerformance;
|
|
||||||
using StgSharp.HighPerformance.Memory;
|
|
||||||
using StgSharp.Internal;
|
using StgSharp.Internal;
|
||||||
using StgSharp.Mathematics;
|
|
||||||
using StgSharp.Mathematics.Graphic;
|
|
||||||
using StgSharp.Mathematics.Numeric;
|
using StgSharp.Mathematics.Numeric;
|
||||||
using StgSharp.Model.Step;
|
|
||||||
using StgSharp.MVVM;
|
|
||||||
using StgSharp.MVVM.ViewModel;
|
|
||||||
using StgSharp.Script;
|
|
||||||
using StgSharp.Script.Express;
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.Intrinsics;
|
|
||||||
using System.Runtime.Intrinsics.X86;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Xml.XPath;
|
|
||||||
|
|
||||||
namespace StgSharp
|
namespace StgSharp
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
//-----------------------------------------------------------------------
|
||||||
|
// -----------------------------------------------------------------------
|
||||||
|
// file="SlabStressTest"
|
||||||
|
// Project: StgSharp
|
||||||
|
// AuthorGroup: Nitload
|
||||||
|
// Copyright (c) Nitload. 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.Diagnostics;
|
||||||
|
|
||||||
|
namespace StgSharp
|
||||||
|
{
|
||||||
|
internal static class SlabStressTest
|
||||||
|
{
|
||||||
|
|
||||||
|
// Exercise the Nonconcurrent Chunked SLAB used by HLSF (allocation/free only)
|
||||||
|
public static unsafe void Run()
|
||||||
|
{
|
||||||
|
Console.WriteLine("=== SLAB Allocator (Chunked) Single-Thread Stress Test ===");
|
||||||
|
|
||||||
|
// Use SLAB with element size of pointer (nuint) to simulate Entry handles
|
||||||
|
SlabAllocator<BunchOfBytes> slab = SlabAllocator<BunchOfBytes>.Create(64, SlabBufferLayout.Chunked, false);
|
||||||
|
|
||||||
|
int cycles = 10;
|
||||||
|
int allocsPerCycle = 200_000; // large enough to force internal growth
|
||||||
|
|
||||||
|
Stopwatch sw = new Stopwatch();
|
||||||
|
|
||||||
|
for (int c = 0; c < cycles; c++)
|
||||||
|
{
|
||||||
|
Console.WriteLine($"--- Cycle {c+1} ---");
|
||||||
|
nuint[] ptrs = new nuint[allocsPerCycle];
|
||||||
|
|
||||||
|
sw.Restart();
|
||||||
|
for (int i = 0; i < allocsPerCycle; i++) {
|
||||||
|
ptrs[i] = slab.Allocate();
|
||||||
|
}
|
||||||
|
sw.Stop();
|
||||||
|
Console.WriteLine($"Allocate: {allocsPerCycle} in {sw.Elapsed.TotalSeconds:0.00}s");
|
||||||
|
|
||||||
|
sw.Restart();
|
||||||
|
for (int i = 0; i < allocsPerCycle; i++) {
|
||||||
|
slab.Free(ptrs[i]);
|
||||||
|
}
|
||||||
|
sw.Stop();
|
||||||
|
Console.WriteLine($"Free: {allocsPerCycle} in {sw.Elapsed.TotalSeconds:0.00}s");
|
||||||
|
}
|
||||||
|
|
||||||
|
slab.Dispose();
|
||||||
|
Console.WriteLine("=== SLAB Stress Complete ===");
|
||||||
|
}
|
||||||
|
|
||||||
|
internal unsafe struct BunchOfBytes
|
||||||
|
{
|
||||||
|
|
||||||
|
public fixed byte Data[128];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user