hlsf error is solving

This commit is contained in:
Youmu Liu
2025-11-28 18:18:08 +08:00
parent a3570bf20a
commit 0c90461d35
8 changed files with 253 additions and 281 deletions
@@ -26,8 +26,10 @@
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
using System; using System;
using System.ComponentModel;
using System.Diagnostics.Tracing;
using System.Numerics; using System.Numerics;
using System.Runtime.InteropServices;
using hlsfHandle = StgSharp.HighPerformance.Memory.HybridLayerSegregatedFitAllocationHandle; using hlsfHandle = StgSharp.HighPerformance.Memory.HybridLayerSegregatedFitAllocationHandle;
namespace StgSharp.HighPerformance.Memory namespace StgSharp.HighPerformance.Memory
@@ -37,7 +39,15 @@ namespace StgSharp.HighPerformance.Memory
public hlsfHandle Alloc(uint size) public hlsfHandle Alloc(uint size)
{ {
ArgumentOutOfRangeException.ThrowIfGreaterThan<nuint>(size, (nuint)levelSizeArray[^1] * 2); if (size > 32U * 1024U * 1024U)
{
Entry* e = (Entry*)_entries.Allocate();
e->Size = size;
e->Position = (nuint)NativeMemory.AlignedAlloc(size, (nuint)_align);
e->State = EntryState.Large;
return new hlsfHandle(e, size);
}
int level = GetLevelFromSize(size); int level = GetLevelFromSize(size);
int segmentIndex = DetermineSegmentIndex(size, level); int segmentIndex = DetermineSegmentIndex(size, level);
@@ -63,174 +73,144 @@ namespace StgSharp.HighPerformance.Memory
if (handle != null) if (handle != null)
{ {
int requestedOffset = (segmentIndex + 1) * levelSizeArray[level]; int requestedOffset = (segmentIndex + 1) * levelSizeArray[level];
if ((ulong)requestedOffset > handle->Size) if (requestedOffset > handle->Size) {
{
// spare返回的块不足以切出目标大小,避免进入切片导致下溢/越界
throw new OverflowException("Out of memory: insufficient block size returned from spare memory."); throw new OverflowException("Out of memory: insufficient block size returned from spare memory.");
} }
Entry* extra = (Entry*)_entries.Allocate(); long remainder = handle->Size - requestedOffset;
extra->Position = handle->Position + (nuint)requestedOffset; if (remainder > 0)
extra->Size = handle->Size - (uint)requestedOffset; {
extra->State = EntryState.Empty; Entry* extra = (Entry*)_entries.Allocate();
extra->NextNear = handle->NextNear; extra->Position = handle->Position + (nuint)requestedOffset;
extra->PreviousNear = handle; extra->Size = (uint)remainder;
extra->NextNear->PreviousNear = extra; extra->State = EntryState.Empty;
extra->PreviousNear->NextNear = extra; extra->NextNear = handle->NextNear;
SliceMemory(extra); extra->PreviousNear = handle;
extra->NextNear->PreviousNear = extra;
extra->PreviousNear->NextNear = extra;
SliceMemory(extra);
}
handle->State = EntryState.Alloc;
handle->Size = requestedOffset;
handle->Level = level;
} }
handle->State = EntryState.Allocated;
handle->Level = level;
return new hlsfHandle(handle, size); return new hlsfHandle(handle, size);
} }
/// <summary>
/// Slice the memory block into smaller blocks and add them to the appropriate buckets
/// Notice: this method is not responsible for freeing the original BucketNode
/// </summary>
/// <param name="e">
///
/// </param>
private void SliceMemory(Entry* e) private void SliceMemory(Entry* e)
{ {
Entry* current = e; Entry* cur = e->PreviousNear;
Entry* next = e->NextNear; Entry* next = e->NextNear;
ulong originOffset = e->Position - (ulong)m_Buffer; long remain = e->Size;
ulong finalOffset = originOffset; long offset = (long)e->Position - (long)m_Buffer;
ulong end = e->Position + e->Size; long end = offset + remain;
ulong endOffset = end - (ulong)m_Buffer; _entries.Free((nuint)e);
ulong remainSize = e->Size;
if (finalOffset >= endOffset || remainSize == 0) {
return;
}
// get level of left edge
int minLevel = int.Max((BitOperations.TrailingZeroCount(finalOffset) - 6) / 2, 0);
int i = minLevel >= levelSizeArray.Length ? levelSizeArray.Length - 1 : minLevel;
int i = (BitOperations.TrailingZeroCount(remain) - 6) / 2;
i = int.Min(10, i);
for (; i < levelSizeArray.Length; i++) for (; i < levelSizeArray.Length; i++)
{ {
int levelSize = levelSizeArray[i]; int size = levelSizeArray[i];
ulong levelLong = (ulong)levelSize; int upper = size * 4;
ulong levelUpper = levelLong * 4; long edge = (offset + upper - 1) / upper * upper;
int count = (int)((long.Min(edge, end) - offset) / size);
// smallest board that is larger than finalOffset if (count > 0)
ulong board = (finalOffset + levelUpper - 1) / levelUpper * levelUpper;
long span = (long)ulong.Min(board, endOffset) - (long)finalOffset;
int blockCount = (span <= 0) ? 0 : (int)(span / (long)levelLong);
if (blockCount == 0)
{ {
continue; int newSize = count * size;
}
int s = blockCount * levelSize;
// create and init new entry
Entry* newEntry = (Entry*)_entries.Allocate();
newEntry->State = EntryState.Empty;
newEntry->Position = (nuint)m_Buffer + (nuint)finalOffset;
newEntry->Size = (uint)s;
BucketNode* bucket = (BucketNode*)newEntry->Position;
bucket->EntryRef = newEntry;
newEntry->Level = i;
// link new entry
newEntry->PreviousNear = current;
newEntry->NextNear = next;
current->NextNear = newEntry;
next->PreviousNear = newEntry;
// set new entry
current = newEntry;
PushLevel(i, Math.Min(blockCount - 1, 2), bucket);
finalOffset += (ulong)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;
long span = (long)board - (long)finalOffset;
int blockCount = span > 0 ? (int)(span / (long)levelSize) : 0;
if (blockCount > 0)
{
int s = blockCount * levelSize;
Entry* newEntry = (Entry*)_entries.Allocate(); Entry* newEntry = (Entry*)_entries.Allocate();
newEntry->State = EntryState.Empty; newEntry->PreviousNear = cur;
newEntry->Position = (nuint)((ulong)m_Buffer + finalOffset);
newEntry->Size = (uint)s;
BucketNode* bucket = (BucketNode*)newEntry->Position;
bucket->EntryRef = newEntry;
newEntry->Level = 10;
// link new entry
newEntry->PreviousNear = current;
newEntry->NextNear = next; newEntry->NextNear = next;
current->NextNear = newEntry; cur->NextNear = newEntry;
if (next != null) { next->PreviousNear = newEntry;
next->PreviousNear = newEntry; newEntry->Position = (nuint)offset + (nuint)m_Buffer;
} newEntry->State = EntryState.Empty;
newEntry->Size = (uint)newSize;
newEntry->Level = i; newEntry->Level = i;
PushLevel(i, Math.Min(blockCount - 1, 2), bucket);
finalOffset += (ulong)s; BucketNode* b = (BucketNode*)newEntry->Position;
remainSize -= (ulong)s; b->EntryRef = newEntry;
PushLevel(i, count - 1, b);
offset += newSize;
remain -= (long)newSize;
cur = newEntry;
} }
if (remainSize == 0) { if (remain <= 0) {
return; return;
} }
} }
for (i -= 1; i >= 0; i--) if (i > MaxLevel)
{ {
int levelSize = levelSizeArray[i]; int size = levelSizeArray[^1];
ulong levelLong = (ulong)levelSize; int upper = size * 4;
ulong board = endOffset / levelLong * levelLong; long edge = end / upper * upper;
long span = (long)board - (long)finalOffset; int count = (int)((long.Min(edge, end) - offset) / upper);
int blockCount = span > 0 ? (int)(span / levelSize) : 0; if (count > 0)
if (blockCount <= 0)
{ {
continue; int newSize = count * upper;
}
int s = blockCount * levelSize;
System.Diagnostics.Debug.Assert(finalOffset + (ulong)s <= endOffset);
// create and init new entry Entry* newEntry = (Entry*)_entries.Allocate();
Entry* newEntry = (Entry*)_entries.Allocate(); newEntry->PreviousNear = cur;
newEntry->State = EntryState.Empty; newEntry->NextNear = next;
newEntry->Position = (nuint)((ulong)m_Buffer + finalOffset); cur->NextNear = newEntry;
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; next->PreviousNear = newEntry;
} newEntry->Position = (nuint)offset + (nuint)m_Buffer;
newEntry->State = EntryState.Empty;
newEntry->Size = (uint)newSize;
newEntry->Level = i;
newEntry->Level = i; BucketNode* b = (BucketNode*)newEntry->Position;
PushLevel(i, Math.Min(blockCount - 1, 2), bucket); b->EntryRef = newEntry;
PushLevel(i, count - 1, b);
cur = newEntry;
offset += newSize;
remain -= (long)newSize;
if (remain <= 0) {
return;
}
}
}
// set new entry for (i--; i >= 0; i--)
current = newEntry; {
int size = levelSizeArray[i];
long edge = end / size * size;
int count = (int)((long.Min(edge, end) - offset) / size);
if (count > 0)
{
int newSize = count * size;
Entry* newEntry = (Entry*)_entries.Allocate();
newEntry->PreviousNear = cur;
newEntry->NextNear = next;
cur->NextNear = newEntry;
next->PreviousNear = newEntry;
newEntry->Position = (nuint)offset + (nuint)m_Buffer;
newEntry->State = EntryState.Empty;
newEntry->Size = (uint)newSize;
newEntry->Level = i;
// update lock and data BucketNode* b = (BucketNode*)newEntry->Position;
finalOffset += (ulong)s; b->EntryRef = newEntry;
remainSize -= (ulong)s; b->NextLevel = null;
if (remainSize == 0) b->PreviousLevel = null;
{ PushLevel(i, count - 1, b);
break;
cur = newEntry;
offset += newSize;
remain -= (long)newSize;
}
if (remain <= 0) {
return;
} }
} }
RemoveFromPositionChain(e);
} }
/// <summary> /// <summary>
@@ -245,51 +225,32 @@ namespace StgSharp.HighPerformance.Memory
private bool TryAllocateNew16MBBlock(out Entry* handle) private bool TryAllocateNew16MBBlock(out Entry* handle)
{ {
nuint pos = _spareMemory->Position; nuint pos = _spareMemory->Position;
nuint bufferEnd = (nuint)m_Buffer + _size; long remain = _spareMemory->Size;
if (remain <= 0)
if (pos >= bufferEnd || _spareMemory->Size == 0)
{
handle = null;
return false; // No spare memory available
}
nuint desired = (nuint)levelSizeArray[^1] * 4;
nuint remain = bufferEnd - pos;
nuint blockSize = desired <= remain ? desired : remain;
if (blockSize == 0)
{ {
handle = null; handle = null;
return false; return false;
} }
long size = remain > 16 * 1024 * 1024L ? 16 * 1024 * 1024L : remain;
nuint newPos = pos + blockSize;
// Try to allocate new Entry from slab allocator // build new entry
handle = (Entry*)_entries.Allocate(); Entry* e = (Entry*)_entries.Allocate();
handle->Position = pos; e->Position = pos;
handle->Size = (uint)blockSize; e->Size = size;
handle->Level = GetLevelFromSize((uint)blockSize); Entry* prev = _spareMemory->PreviousNear;
e->NextNear = _spareMemory;
e->PreviousNear = prev;
prev->NextNear = e;
_spareMemory->PreviousNear = e;
e->State = EntryState.Empty;
e->Level = GetLevelFromSize(size);
// Update spare memory position & remaining size // set fields of spare memory
_spareMemory->Position = newPos; _spareMemory->Size -= size;
_spareMemory->Size = (uint)(bufferEnd - newPos); _spareMemory->Position += (nuint)size;
BucketNode* b = (BucketNode*)handle->Position;
b->EntryRef = handle;
// update position linked list
Entry* previous = _spareMemory->PreviousNear;
handle->NextNear = _spareMemory;
handle->PreviousNear = previous;
_spareMemory->PreviousNear = handle; // Replace Volatile.Write with direct access
if (previous != null)
{
previous->NextNear = handle;
} else
{
_positionHead = handle;
}
handle = e;
return true; return true;
} }
@@ -35,16 +35,6 @@ namespace StgSharp.HighPerformance.Memory
public unsafe partial class HybridLayerSegregatedFitAllocator public unsafe partial class HybridLayerSegregatedFitAllocator
{ {
#region BucketNode Operations
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool IsNullOrEmpty(BucketNode* node)
{
return node == null;
}
#endregion
private void PushLevel(int levelIndex, int sizeIndex, BucketNode* node) private void PushLevel(int levelIndex, int sizeIndex, BucketNode* node)
{ {
if (node == null || node->EntryRef == null) { if (node == null || node->EntryRef == null) {
@@ -52,7 +42,7 @@ namespace StgSharp.HighPerformance.Memory
} }
Index index = (levelIndex > MaxLevel) ? ^1 : (3 * levelIndex) + sizeIndex; Index index = (levelIndex > MaxLevel) ? ^1 : (3 * levelIndex) + sizeIndex;
if (_bucketHeads[index] == (BucketNode*)null) if (_bucketHeads[index] is null)
{ {
_bucketHeads[index] = node; _bucketHeads[index] = node;
node->IsInBucket = true; node->IsInBucket = true;
@@ -78,10 +68,29 @@ namespace StgSharp.HighPerformance.Memory
} }
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void RemoveFromBucket(BucketNode* node)
{
BucketNode* previous = node->PreviousLevel;
BucketNode* next = node->NextLevel;
if (previous != null) {
previous->NextLevel = next;
}
if (next != null) {
next->PreviousLevel = previous;
}
node->NextLevel = (BucketNode*)null;
node->PreviousLevel = (BucketNode*)null;
node->IsInBucket = false;
return;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void RemoveFromBucket(int levelIndex, int sizeIndex, BucketNode* node) private void RemoveFromBucket(int levelIndex, int sizeIndex, BucketNode* node)
{ {
Index index = (levelIndex > MaxLevel) ? ^1 : (3 * levelIndex) + sizeIndex; Index index = (levelIndex > MaxLevel) ? ^1 : (3 * levelIndex) + sizeIndex;
if (_bucketHeads[index] == node) if (_bucketHeads[index] == node)
{ {
if (TryPopLevel(levelIndex, sizeIndex, out _)) { if (TryPopLevel(levelIndex, sizeIndex, out _)) {
@@ -107,7 +116,7 @@ namespace StgSharp.HighPerformance.Memory
private bool TryPopLevel(int levelIndex, int sizeIndex, out BucketNode* node) private bool TryPopLevel(int levelIndex, int sizeIndex, out BucketNode* node)
{ {
int index = (3 * levelIndex) + sizeIndex; Index index = levelIndex > MaxLevel ? ^1 : (3 * levelIndex) + sizeIndex;
BucketNode* head = _bucketHeads[index]; BucketNode* head = _bucketHeads[index];
@@ -139,8 +148,10 @@ namespace StgSharp.HighPerformance.Memory
private bool TryRemoveFromBucket(int levelIndex, int sizeIndex, BucketNode* node) private bool TryRemoveFromBucket(int levelIndex, int sizeIndex, BucketNode* node)
{ {
Index index = levelIndex > 9 ? ^1 : (3 * levelIndex) + sizeIndex; Index index = levelIndex > 9 ? ^1 : (3 * levelIndex) + sizeIndex;
if (_bucketHeads[index] == node) { if (_bucketHeads[index] == node)
return TryPopLevel(levelIndex, sizeIndex, out _); {
bool ok = TryPopLevel(levelIndex, sizeIndex, out _);
return ok;
} }
BucketNode* previous = node->PreviousLevel; BucketNode* previous = node->PreviousLevel;
@@ -157,7 +168,6 @@ namespace StgSharp.HighPerformance.Memory
node->PreviousLevel = null; node->PreviousLevel = null;
node->IsInBucket = false; node->IsInBucket = false;
node->EntryRef = null; node->EntryRef = null;
return true; return true;
} }
@@ -33,6 +33,14 @@ using System.Threading.Tasks;
namespace StgSharp.HighPerformance.Memory namespace StgSharp.HighPerformance.Memory
{ {
public sealed class HLSFPositionChainBreakException : Exception
{
public HLSFPositionChainBreakException()
: base("Position chain of an HLSF allocator is broken") { }
}
public unsafe partial class HybridLayerSegregatedFitAllocator public unsafe partial class HybridLayerSegregatedFitAllocator
{ {
@@ -72,7 +80,7 @@ namespace StgSharp.HighPerformance.Memory
private static string EntryToString(Entry* e) private static string EntryToString(Entry* e)
{ {
BucketNode* b = (BucketNode*)e->Position; BucketNode* b = (BucketNode*)e->Position;
return $"{(ulong)e->PreviousNear}<-- Position{(ulong)e} Bucket:{{{e->Position},{e->Level},{e->Size}}} -->{(ulong)e->NextNear}"; return $"{(ulong)e->PreviousNear}<-- Position{(ulong)e} Bucket:{{{(ulong)e->Position},{e->Level},{e->Size}}} -->{(ulong)e->NextNear}";
} }
} }
@@ -28,7 +28,10 @@
using System; using System;
using System.Buffers; using System.Buffers;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing;
using System.Linq; using System.Linq;
using System.Reflection.Emit;
using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -45,7 +48,7 @@ namespace StgSharp.HighPerformance.Memory
/// </summary> /// </summary>
public void Collect() public void Collect()
{ {
Entry* cur = _positionHead; Entry* cur = _spareMemory->NextNear;
while (cur != null && cur != _spareMemory) while (cur != null && cur != _spareMemory)
{ {
if (cur->State != EntryState.Empty) if (cur->State != EntryState.Empty)
@@ -53,36 +56,19 @@ namespace StgSharp.HighPerformance.Memory
cur = cur->NextNear; cur = cur->NextNear;
continue; continue;
} }
RemoveFromBucket(cur->Level, DetermineSegmentIndex(cur->Size, cur->Level), (BucketNode*)cur->Position);
Entry* next = cur->NextNear;
Entry* runHead = cur; while (next is not null &&
Entry* next = runHead->NextNear;
// Remove head from bucket if currently registered
int headLevel = runHead->Level;
int headSeg = DetermineSegmentIndex(runHead->Size, headLevel);
TryRemoveFromBucket(headLevel, headSeg, (BucketNode*)runHead->Position);
while (next != null &&
next != _spareMemory &&
next->State == EntryState.Empty && next->State == EntryState.Empty &&
IsAdjacentTo(runHead, next)) AssertAdjacentTo(cur, next))
{ {
int lvl = next->Level; RemoveFromBucket(next->Level, DetermineSegmentIndex(next->Size, next->Level), (BucketNode*)next->Position);
int seg = DetermineSegmentIndex(next->Size, lvl); cur->Size += next->Size;
TryRemoveFromBucket(lvl, seg, (BucketNode*)next->Position);
runHead->Size += next->Size;
RemoveFromPositionChain(next); RemoveFromPositionChain(next);
next = runHead->NextNear; next = cur->NextNear;
} }
SliceMemory(cur);
// Recompute level for merged block and push once
runHead->Level = GetLevelFromSize(runHead->Size);
int finalLevel = runHead->Level;
int finalSeg = DetermineSegmentIndex(runHead->Size, finalLevel);
((BucketNode*)runHead->Position)->EntryRef = runHead; // ensure consistency
PushLevel(finalLevel, finalSeg, (BucketNode*)runHead->Position);
cur = next; cur = next;
} }
} }
@@ -99,7 +85,13 @@ namespace StgSharp.HighPerformance.Memory
if (e == null) { if (e == null) {
return; return;
} }
if (e->State != EntryState.Allocated) { if (e->State == EntryState.Large)
{
NativeMemory.AlignedFree((void*)e->Position);
_entries.Free((nuint)e);
return;
}
if (e->State != EntryState.Alloc) {
return; return;
} }
@@ -110,12 +102,15 @@ namespace StgSharp.HighPerformance.Memory
MergeAndRemoveFromBuckets(e); MergeAndRemoveFromBuckets(e);
// Calculate final level and segment for the merged block // Calculate final level and segment for the merged block
uint finalSize = e->Size; long finalSize = e->Size;
int finalLevel = GetLevelFromSize(finalSize); int finalLevel = GetLevelFromSize(finalSize);
e->Level = finalLevel;
int finalSegment = DetermineSegmentIndex(finalSize, finalLevel); int finalSegment = DetermineSegmentIndex(finalSize, finalLevel);
// Push the merged entry back to appropriate bucket // Push the merged entry back to appropriate bucket
PushLevel(finalLevel, finalSegment, (BucketNode*)e->Position); PushLevel(finalLevel, finalSegment, (BucketNode*)e->Position);
// Console.WriteLine("one block freed");
} }
} }
@@ -59,20 +59,26 @@ namespace StgSharp.HighPerformance.Memory
return int.Min(blockSize, _align); return int.Min(blockSize, _align);
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private int GetLevelFromSize(long size)
{
return GetLevelFromSize((uint)size);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private int GetLevelFromSize(uint size) private int GetLevelFromSize(uint size)
{ {
/* /*
* Level sizes: [64, 256, 1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216] * Level sizes: [64, 256, 1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216]
* Pattern: 64 * 4^level = 2^6 * 2^(2*level) = 2^(6+2*level) * Pattern: 64 * 4^level = 2^6 * 2^(2*level) = 2^(6+2*level)
* *
* For size S, we need: 2^(6+2*level) >= S * For size S, we need: 2^(6+2*level) >= S
* So: 6+2*level >= log2(S) * So: 6+2*level >= log2(S)
* Therefore: level >= (log2(S) - 6) / 2 * Therefore: level >= (log2(S) - 6) / 2
* *
* We use hardware LZCNT instruction via BitOperations.LeadingZeroCount * We use hardware LZCNT instruction via BitOperations.LeadingZeroCount
* log2(S) = 31 - LeadingZeroCount(S) for 32-bit values * log2(S) = 31 - LeadingZeroCount(S) for 32-bit values
*/ */
int log2Size = 31 - BitOperations.LeadingZeroCount(size); int log2Size = 31 - BitOperations.LeadingZeroCount(size);
// Calculate level: level = max(0, (log2Size - 6) / 2) // Calculate level: level = max(0, (log2Size - 6) / 2)
@@ -34,6 +34,17 @@ namespace StgSharp.HighPerformance.Memory
public unsafe partial class HybridLayerSegregatedFitAllocator public unsafe partial class HybridLayerSegregatedFitAllocator
{ {
/// <summary>
/// Check if two Entries are adjacent in memory
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool AssertAdjacentTo(Entry* current, Entry* other)
{
// Only a predicate; never throw here. Callers rely on false to skip merging.
return other->Position + (nuint)other->Size == current->Position ||
current->Position + (nuint)current->Size == other->Position;
}
/// <summary> /// <summary>
/// check out if a bucket is within the same higher-level boundary as currentOffset /// check out if a bucket is within the same higher-level boundary as currentOffset
/// </summary> /// </summary>
@@ -50,21 +61,10 @@ namespace StgSharp.HighPerformance.Memory
/// Determine segment index based on size and originLevel /// Determine segment index based on size and originLevel
/// </summary> /// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private int DetermineSegmentIndex(uint size, int level) private int DetermineSegmentIndex(long size, int level)
{ {
level = int.Min(level, MaxLevel); level = int.Min(level, MaxLevel);
return (int)(size - 1) / levelSizeArray[level]; return (int)((size - 1) / levelSizeArray[level]);
}
/// <summary>
/// Check if two Entries are adjacent in memory
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool IsAdjacentTo(Entry* current, Entry* other)
{
// Check if this immediately follows other
return other->Position + other->Size == current->Position ||
current->Position + current->Size == other->Position;
} }
/// <summary> /// <summary>
@@ -80,19 +80,16 @@ namespace StgSharp.HighPerformance.Memory
return; return;
} }
Entry* result = entry; long size = entry->Size;
uint size = entry->Size;
int originLevel = entry->Level; int originLevel = entry->Level;
int segment = DetermineSegmentIndex(size, originLevel); int segment = DetermineSegmentIndex(size, originLevel);
do do
{ {
// previous direction merge // previous direction merge
Entry* p = entry->PreviousNear; Entry* p = entry->PreviousNear;
if (p == null || if (p == null ||
p->State != EntryState.Empty || p->State != EntryState.Empty ||
!IsAdjacentTo(result, p) || !AssertAdjacentTo(entry, p) ||
p->Level != entry->Level || p->Level != entry->Level ||
!AssertBoundary(p, entry, originLevel)) !AssertBoundary(p, entry, originLevel))
{ {
@@ -112,7 +109,7 @@ namespace StgSharp.HighPerformance.Memory
Entry* n = entry->NextNear; Entry* n = entry->NextNear;
if (n == null || if (n == null ||
n->State != EntryState.Empty || n->State != EntryState.Empty ||
!IsAdjacentTo(result, n) || !AssertAdjacentTo(entry, n) ||
n->Level != entry->Level || n->Level != entry->Level ||
!AssertBoundary(n, entry, originLevel)) !AssertBoundary(n, entry, originLevel))
{ {
@@ -133,24 +130,16 @@ namespace StgSharp.HighPerformance.Memory
} }
/// <summary> /// <summary>
/// Remove node from position linked list /// Remove node from position linked list, this method does not recycle a BucketNode from
/// size bucket
/// </summary> /// </summary>
private void RemoveFromPositionChain(Entry* entry) private void RemoveFromPositionChain(Entry* entry)
{ {
Entry* prev = entry->PreviousNear; Entry* prev = entry->PreviousNear;
Entry* next = entry->NextNear; Entry* next = entry->NextNear;
if (prev != null) prev->NextNear = next;
{ next->PreviousNear = prev;
prev->NextNear = entry->NextNear;
} else
{
_positionHead = entry->NextNear;
}
if (next != null) {
next->PreviousNear = entry->PreviousNear;
}
// Clear node pointers // Clear node pointers
entry->NextNear = null; entry->NextNear = null;
@@ -164,25 +153,28 @@ namespace StgSharp.HighPerformance.Memory
/// Entry structure - streamlined version (only position and state information) Stored in /// Entry structure - streamlined version (only position and state information) Stored in
/// external SLAB /// external SLAB
/// </summary> /// </summary>
[StructLayout(LayoutKind.Explicit, Size = 36)] [StructLayout(LayoutKind.Explicit, Size = 40)]
internal struct Entry internal struct Entry
{ {
[FieldOffset(8)] internal Entry* NextNear; // Position linked list: next pointer [FieldOffset(0)] internal Entry* NextNear; // Position linked list: next pointer
[FieldOffset(16)] internal Entry* PreviousNear; // Position linked list: previous pointer [FieldOffset(8)] internal Entry* PreviousNear; // Position linked list: previous pointer
[FieldOffset(28)] internal int _Level; // Level of the block in the allocator [FieldOffset(24)] internal int _Level; // Level of the block in the allocator
[FieldOffset(24)] internal uint Size; // Block size information [FieldOffset(16)] internal long Size; // Block size information
[FieldOffset(32)] public EntryState State; [FieldOffset(36)] public EntryState State;
[FieldOffset(0)] public nuint Position; // Memory block position [FieldOffset(28)] public nuint Position; // Memory block position
public int Level public int Level
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _Level; get => _Level;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
set set
{ {
if (value > 15 || value < 0) { int size = 64 * (1 << (value * 2));
throw new InvalidOperationException(); if (Size / size > 3 && Size < 64 * 1024 * 1024L) {
throw new HLSFPositionChainBreakException();
} }
_Level = value; _Level = value;
} }
@@ -195,7 +187,8 @@ namespace StgSharp.HighPerformance.Memory
Spare, Spare,
Empty, Empty,
Allocated Alloc,
Large
} }
@@ -39,9 +39,7 @@ namespace StgSharp.HighPerformance.Memory
// Bucket system field - directly in main allocator class // Bucket system field - directly in main allocator class
private BucketNode*[] _bucketHeads; private BucketNode*[] _bucketHeads;
// Head of the position linked list (first Entry in memory order) // _spareMemory is also the beginning position of allocator
private Entry* _positionHead;
private Entry* _spareMemory; private Entry* _spareMemory;
private readonly byte* m_Buffer; private readonly byte* m_Buffer;
private bool disposedValue; private bool disposedValue;
@@ -63,8 +61,8 @@ namespace StgSharp.HighPerformance.Memory
_spareMemory = (Entry*)_entries.Allocate(); _spareMemory = (Entry*)_entries.Allocate();
_spareMemory->State = EntryState.Spare; _spareMemory->State = EntryState.Spare;
_spareMemory->PreviousNear = null; _spareMemory->PreviousNear = _spareMemory;
_spareMemory->NextNear = null; _spareMemory->NextNear = _spareMemory;
_spareMemory->Position = (nuint)m_Buffer; _spareMemory->Position = (nuint)m_Buffer;
_spareMemory->Size = (uint)byteSize; _spareMemory->Size = (uint)byteSize;
} }
@@ -73,19 +71,19 @@ namespace StgSharp.HighPerformance.Memory
{ {
if (align < 16 || (align & (align - 1)) != 0) { if (align < 16 || (align & (align - 1)) != 0) {
throw new ArgumentException( throw new ArgumentException(
"Alignment must be a power of two and at least 8 bytes."); "Alignment must be a power of two and at least 16 bytes.");
} }
_entries = SlabAllocator<Entry>.Create(64, SlabBufferLayout.Chunked); _entries = SlabAllocator<Entry>.Create(64, SlabBufferLayout.Chunked);
m_Buffer = (byte*)NativeMemory.AlignedAlloc(byteSize, (nuint)align); m_Buffer = (byte*)NativeMemory.AlignedAlloc(byteSize, (nuint)align);
_align = align; _align = align;
_size = byteSize; _size = byteSize;
_bucketHeads = new BucketNode*[levelSizeArray.Length * 3]; _bucketHeads = new BucketNode*[levelSizeArray.Length * 3 + 1];
_spareMemory = (Entry*)_entries.Allocate(); _spareMemory = (Entry*)_entries.Allocate();
_spareMemory->State = EntryState.Empty; _spareMemory->State = EntryState.Empty;
_spareMemory->PreviousNear = null; _spareMemory->PreviousNear = _spareMemory;
_spareMemory->NextNear = null; _spareMemory->NextNear = _spareMemory;
_spareMemory->Position = (nuint)m_Buffer; _spareMemory->Position = (nuint)m_Buffer;
_spareMemory->Size = (uint)byteSize; _spareMemory->Size = (uint)byteSize;
} }
+2 -1
View File
@@ -2,7 +2,8 @@
"profiles": { "profiles": {
"StgSharpDebug": { "StgSharpDebug": {
"commandName": "Project", "commandName": "Project",
"nativeDebugging": true "nativeDebugging": true,
"hotReloadEnabled": false
} }
} }
} }