hlsf gong on rewrite again
This commit is contained in:
@@ -60,28 +60,48 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (handle != null) {
|
if (handle != null)
|
||||||
SliceMemory(handle, (segmentIndex + 1) * levelSizeArray[level]);
|
{
|
||||||
|
int requestedOffset = (segmentIndex + 1) * levelSizeArray[level];
|
||||||
|
if ((ulong)requestedOffset > handle->Size)
|
||||||
|
{
|
||||||
|
// spare返回的块不足以切出目标大小,避免进入切片导致下溢/越界
|
||||||
|
throw new OverflowException("Out of memory: insufficient block size returned from spare memory.");
|
||||||
|
}
|
||||||
|
Entry* extra = (Entry*)_entries.Allocate();
|
||||||
|
extra->Position = handle->Position + (nuint)requestedOffset;
|
||||||
|
extra->Size = handle->Size - (uint)requestedOffset;
|
||||||
|
extra->State = EntryState.Empty;
|
||||||
|
extra->NextNear = handle->NextNear;
|
||||||
|
extra->PreviousNear = handle;
|
||||||
|
extra->NextNear->PreviousNear = extra;
|
||||||
|
extra->PreviousNear->NextNear = extra;
|
||||||
|
SliceMemory(extra);
|
||||||
}
|
}
|
||||||
handle->State = EntryState.Allocated;
|
handle->State = EntryState.Allocated;
|
||||||
|
handle->Level = level;
|
||||||
return new hlsfHandle(handle, size);
|
return new hlsfHandle(handle, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SliceMemory(Entry* e, int offset)
|
private void SliceMemory(Entry* e)
|
||||||
{
|
{
|
||||||
Entry* current = e;
|
Entry* current = e;
|
||||||
Entry* next = e->NextNear;
|
Entry* next = e->NextNear;
|
||||||
ulong originOffset = e->Position - (ulong)m_Buffer;
|
ulong originOffset = e->Position - (ulong)m_Buffer;
|
||||||
ulong finalOffset = originOffset + (ulong)offset;
|
ulong finalOffset = originOffset;
|
||||||
ulong end = e->Position + e->Size;
|
ulong end = e->Position + e->Size;
|
||||||
ulong endOffset = end - (ulong)m_Buffer;
|
ulong endOffset = end - (ulong)m_Buffer;
|
||||||
ulong remainSize = (ulong)e->Size - (ulong)offset;
|
ulong remainSize = e->Size;
|
||||||
|
|
||||||
|
|
||||||
|
if (finalOffset >= endOffset || remainSize == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 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];
|
||||||
@@ -90,7 +110,8 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
|
|
||||||
// smallest board that is larger than finalOffset
|
// smallest board that is larger than finalOffset
|
||||||
ulong board = (finalOffset + levelUpper - 1) / levelUpper * levelUpper;
|
ulong board = (finalOffset + levelUpper - 1) / levelUpper * levelUpper;
|
||||||
int blockCount = (int)((long)ulong.Min(board, endOffset) - (long)finalOffset) / (int)levelLong;
|
long span = (long)ulong.Min(board, endOffset) - (long)finalOffset;
|
||||||
|
int blockCount = (span <= 0) ? 0 : (int)(span / (long)levelLong);
|
||||||
if (blockCount == 0)
|
if (blockCount == 0)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
@@ -104,6 +125,7 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
newEntry->Size = (uint)s;
|
newEntry->Size = (uint)s;
|
||||||
BucketNode* bucket = (BucketNode*)newEntry->Position;
|
BucketNode* bucket = (BucketNode*)newEntry->Position;
|
||||||
bucket->EntryRef = newEntry;
|
bucket->EntryRef = newEntry;
|
||||||
|
newEntry->Level = i;
|
||||||
|
|
||||||
// link new entry
|
// link new entry
|
||||||
newEntry->PreviousNear = current;
|
newEntry->PreviousNear = current;
|
||||||
@@ -112,21 +134,17 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
next->PreviousNear = newEntry;
|
next->PreviousNear = newEntry;
|
||||||
|
|
||||||
// set new entry
|
// set new entry
|
||||||
// Entry* old = current;
|
|
||||||
current = newEntry;
|
current = newEntry;
|
||||||
|
|
||||||
newEntry->Level = i;
|
|
||||||
PushLevel(i, Math.Min(blockCount - 1, 2), bucket);
|
PushLevel(i, Math.Min(blockCount - 1, 2), bucket);
|
||||||
|
|
||||||
// Console.WriteLine($"{(ulong)current->PreviousNear}<->{(ulong)current->NextNear},\t{current->Size}");
|
|
||||||
|
|
||||||
finalOffset += (ulong)s;
|
finalOffset += (ulong)s;
|
||||||
remainSize -= (ulong)s;
|
remainSize -= (ulong)s;
|
||||||
if (remainSize <= (ulong)levelSize)
|
if (remainSize <= (ulong)levelSize)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (remainSize <= 0) {
|
if (remainSize == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -136,7 +154,8 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
int levelSize = levelSizeArray[^1] * 4;
|
int levelSize = levelSizeArray[^1] * 4;
|
||||||
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);
|
long span = (long)board - (long)finalOffset;
|
||||||
|
int blockCount = span > 0 ? (int)(span / (long)levelSize) : 0;
|
||||||
if (blockCount > 0)
|
if (blockCount > 0)
|
||||||
{
|
{
|
||||||
int s = blockCount * levelSize;
|
int s = blockCount * levelSize;
|
||||||
@@ -146,7 +165,7 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
newEntry->Size = (uint)s;
|
newEntry->Size = (uint)s;
|
||||||
BucketNode* bucket = (BucketNode*)newEntry->Position;
|
BucketNode* bucket = (BucketNode*)newEntry->Position;
|
||||||
bucket->EntryRef = newEntry;
|
bucket->EntryRef = newEntry;
|
||||||
|
newEntry->Level = 10;
|
||||||
|
|
||||||
// link new entry
|
// link new entry
|
||||||
newEntry->PreviousNear = current;
|
newEntry->PreviousNear = current;
|
||||||
@@ -159,11 +178,10 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
newEntry->Level = i;
|
newEntry->Level = i;
|
||||||
PushLevel(i, Math.Min(blockCount - 1, 2), bucket);
|
PushLevel(i, Math.Min(blockCount - 1, 2), bucket);
|
||||||
|
|
||||||
// update lock and data
|
|
||||||
finalOffset += (ulong)s;
|
finalOffset += (ulong)s;
|
||||||
remainSize -= (ulong)s;
|
remainSize -= (ulong)s;
|
||||||
}
|
}
|
||||||
if (remainSize <= 0) {
|
if (remainSize == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -173,7 +191,8 @@ 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) / levelSize;
|
long span = (long)board - (long)finalOffset;
|
||||||
|
int blockCount = span > 0 ? (int)(span / levelSize) : 0;
|
||||||
if (blockCount <= 0)
|
if (blockCount <= 0)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
@@ -201,27 +220,17 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
PushLevel(i, Math.Min(blockCount - 1, 2), bucket);
|
PushLevel(i, Math.Min(blockCount - 1, 2), bucket);
|
||||||
|
|
||||||
// set new entry
|
// set new entry
|
||||||
// Entry* old = current;
|
|
||||||
current = newEntry;
|
current = newEntry;
|
||||||
|
|
||||||
// Console.WriteLine($"{(ulong)current->PreviousNear}<->{(ulong)current->NextNear},\t{current->Size}");
|
|
||||||
|
|
||||||
// update lock and data
|
// update lock and data
|
||||||
finalOffset += (ulong)s;
|
finalOffset += (ulong)s;
|
||||||
remainSize -= (ulong)s;
|
remainSize -= (ulong)s;
|
||||||
if (remainSize <= 0)
|
if (remainSize == 0)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
RemoveFromPositionChain(e);
|
||||||
finally
|
|
||||||
{
|
|
||||||
e->Size = (uint)(offset);
|
|
||||||
e->Level = GetLevelFromSize((uint)offset);
|
|
||||||
|
|
||||||
// Console.WriteLine("Alloc ok");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -236,42 +245,49 @@ 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 blockSize = (nuint)levelSizeArray[^1] * 2; // Use last level size (16MB)
|
nuint bufferEnd = (nuint)m_Buffer + _size;
|
||||||
|
|
||||||
Entry* previous;
|
if (pos >= bufferEnd || _spareMemory->Size == 0)
|
||||||
previous = _spareMemory->PreviousNear;
|
|
||||||
if (_spareMemory->Size <= 0)
|
|
||||||
{
|
{
|
||||||
handle = null;
|
handle = null;
|
||||||
return false; // No spare memory available
|
return false; // No spare memory available
|
||||||
}
|
}
|
||||||
nuint newPos = pos + blockSize;
|
|
||||||
|
|
||||||
// Check if we have enough space
|
nuint desired = (nuint)levelSizeArray[^1] * 4;
|
||||||
if (newPos > ((nuint)m_Buffer) + _size)
|
nuint remain = bufferEnd - pos;
|
||||||
|
nuint blockSize = desired <= remain ? desired : remain;
|
||||||
|
if (blockSize == 0)
|
||||||
{
|
{
|
||||||
newPos = (nuint)m_Buffer + _size; // Clamp to maximum buffer size
|
handle = null;
|
||||||
blockSize = _spareMemory->Size;
|
return false;
|
||||||
previous = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nuint newPos = pos + blockSize;
|
||||||
|
|
||||||
// Try to allocate new Entry from slab allocator
|
// Try to allocate new Entry from slab allocator
|
||||||
handle = (Entry*)_entries.Allocate();
|
handle = (Entry*)_entries.Allocate();
|
||||||
handle->Position = pos;
|
handle->Position = pos;
|
||||||
handle->Size = (uint)blockSize;
|
handle->Size = (uint)blockSize;
|
||||||
handle->Level = GetLevelFromSize((uint)blockSize);
|
handle->Level = GetLevelFromSize((uint)blockSize);
|
||||||
|
|
||||||
// Update spare memory position
|
// Update spare memory position & remaining size
|
||||||
_spareMemory->Position = newPos;
|
_spareMemory->Position = newPos;
|
||||||
|
_spareMemory->Size = (uint)(bufferEnd - newPos);
|
||||||
|
|
||||||
BucketNode* b = (BucketNode*)handle->Position;
|
BucketNode* b = (BucketNode*)handle->Position;
|
||||||
b->EntryRef = handle;
|
b->EntryRef = handle;
|
||||||
|
|
||||||
// update position linked list
|
// update position linked list
|
||||||
|
Entry* previous = _spareMemory->PreviousNear;
|
||||||
handle->NextNear = _spareMemory;
|
handle->NextNear = _spareMemory;
|
||||||
handle->PreviousNear = previous;
|
handle->PreviousNear = previous;
|
||||||
_spareMemory->PreviousNear = handle; // Replace Volatile.Write with direct access
|
_spareMemory->PreviousNear = handle; // Replace Volatile.Write with direct access
|
||||||
if (previous != null) {
|
if (previous != null)
|
||||||
|
{
|
||||||
previous->NextNear = handle;
|
previous->NextNear = handle;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
_positionHead = handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
|
|
||||||
private bool TryRemoveFromBucket(int levelIndex, int sizeIndex, BucketNode* node)
|
private bool TryRemoveFromBucket(int levelIndex, int sizeIndex, BucketNode* node)
|
||||||
{
|
{
|
||||||
int index = (3 * levelIndex) + sizeIndex;
|
Index index = levelIndex > 9 ? ^1 : (3 * levelIndex) + sizeIndex;
|
||||||
if (_bucketHeads[index] == node) {
|
if (_bucketHeads[index] == node) {
|
||||||
return TryPopLevel(levelIndex, sizeIndex, out _);
|
return TryPopLevel(levelIndex, sizeIndex, out _);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,54 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
public unsafe partial class HybridLayerSegregatedFitAllocator
|
public unsafe partial class HybridLayerSegregatedFitAllocator
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Minimal collect: linear scan, merge contiguous empty Blocks into one, push merged head
|
||||||
|
/// back to bucket. Caller-responsible design: no slicing or redistribution.
|
||||||
|
/// </summary>
|
||||||
|
public void Collect()
|
||||||
|
{
|
||||||
|
Entry* cur = _positionHead;
|
||||||
|
while (cur != null && cur != _spareMemory)
|
||||||
|
{
|
||||||
|
if (cur->State != EntryState.Empty)
|
||||||
|
{
|
||||||
|
cur = cur->NextNear;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entry* runHead = cur;
|
||||||
|
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 &&
|
||||||
|
IsAdjacentTo(runHead, next))
|
||||||
|
{
|
||||||
|
int lvl = next->Level;
|
||||||
|
int seg = DetermineSegmentIndex(next->Size, lvl);
|
||||||
|
TryRemoveFromBucket(lvl, seg, (BucketNode*)next->Position);
|
||||||
|
|
||||||
|
runHead->Size += next->Size;
|
||||||
|
RemoveFromPositionChain(next);
|
||||||
|
next = runHead->NextNear;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Free allocated memory block and perform adjacent block merging
|
/// Free allocated memory block and perform adjacent block merging
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -55,7 +103,7 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set to ThreadOccupied to prevent other operations
|
// Mark as empty so it can be merged
|
||||||
e->State = EntryState.Empty;
|
e->State = EntryState.Empty;
|
||||||
|
|
||||||
// Try merge with adjacent free blocks and remove them from buckets
|
// Try merge with adjacent free blocks and remove them from buckets
|
||||||
@@ -66,9 +114,6 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
int finalLevel = GetLevelFromSize(finalSize);
|
int finalLevel = GetLevelFromSize(finalSize);
|
||||||
int finalSegment = DetermineSegmentIndex(finalSize, finalLevel);
|
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
|
// Push the merged entry back to appropriate bucket
|
||||||
PushLevel(finalLevel, finalSegment, (BucketNode*)e->Position);
|
PushLevel(finalLevel, finalSegment, (BucketNode*)e->Position);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,47 +37,13 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
/// <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>
|
||||||
/// <param name="currentOffset">
|
private bool AssertBoundary(Entry* previous, Entry* current, int level)
|
||||||
///
|
|
||||||
/// </param>
|
|
||||||
/// <param name="target">
|
|
||||||
///
|
|
||||||
/// </param>
|
|
||||||
/// <param name="level">
|
|
||||||
///
|
|
||||||
/// </param>
|
|
||||||
/// <returns>
|
|
||||||
///
|
|
||||||
/// </returns>
|
|
||||||
private bool AssertBackwardBoundary(ulong currentOffset, Entry* target, int level)
|
|
||||||
{
|
{
|
||||||
level = int.Min(level, MaxLevel);
|
level = int.Min(level, MaxLevel);
|
||||||
int nextLevelSize = levelSizeArray[level] * 4;
|
int nextLevelSize = levelSizeArray[level] * 4;
|
||||||
ulong targetHead = target->Position - (ulong)m_Buffer;
|
ulong prevHead = previous->Position - (ulong)m_Buffer,
|
||||||
return (currentOffset / (ulong)nextLevelSize) == (targetHead / (ulong)nextLevelSize);
|
currHead = current->Position - (ulong)m_Buffer;
|
||||||
}
|
return (currHead / (ulong)nextLevelSize) == (prevHead / (ulong)nextLevelSize);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// check out if a bucket is within the same higher-level boundary as currentOffset
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="currentOffset">
|
|
||||||
///
|
|
||||||
/// </param>
|
|
||||||
/// <param name="target">
|
|
||||||
///
|
|
||||||
/// </param>
|
|
||||||
/// <param name="level">
|
|
||||||
///
|
|
||||||
/// </param>
|
|
||||||
/// <returns>
|
|
||||||
///
|
|
||||||
/// </returns>
|
|
||||||
private bool AssertForwardBoundary(ulong currentOffset, Entry* target, int level)
|
|
||||||
{
|
|
||||||
level = int.Min(level, MaxLevel);
|
|
||||||
int nextLevelSize = levelSizeArray[level] * 4;
|
|
||||||
ulong targetTail = target->Position - (ulong)m_Buffer + target->Size - 1;
|
|
||||||
return (currentOffset / (ulong)nextLevelSize) == (targetTail / (ulong)nextLevelSize);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -108,90 +74,62 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void MergeAndRemoveFromBuckets(Entry* entry)
|
private void MergeAndRemoveFromBuckets(Entry* entry)
|
||||||
{
|
{
|
||||||
// TODO
|
// TODO 后续提供HLSF.Collect全量整理API,从头遍历邻接块进行彻底的合并
|
||||||
/*
|
|
||||||
* 1.有限回收,最多向上收集2层(算上当前总共3层)
|
|
||||||
* 2.单层内就可以通过前后链表遍历解决,很容易做到
|
|
||||||
* 3.由于SLAB分配器内部包含快速复用路径,因而可以考虑直接进行Free掉被合并的Entry。复用成本很低(因为SLAB既没有Version也没有IEnumerable,所以不会有额外的操作计数损耗)
|
|
||||||
* 4.后续提供HLSF.Collect全量整理API,从头遍历邻接块进行彻底的合并
|
|
||||||
*/
|
|
||||||
if (entry == null)
|
if (entry == null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Entry* result = entry;
|
Entry* result = entry;
|
||||||
|
|
||||||
uint 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);
|
||||||
|
|
||||||
// RemoveFromBucket(originLevel, segment, (BucketNode*)entry->Position);
|
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
originLevel = entry->Level;
|
// previous direction merge
|
||||||
while (true)
|
Entry* p = entry->PreviousNear;
|
||||||
{
|
if (p == null ||
|
||||||
// backward merging - simplified to single attempt
|
p->State != EntryState.Empty ||
|
||||||
Entry* prev = entry->PreviousNear;
|
!IsAdjacentTo(result, p) ||
|
||||||
|
p->Level != entry->Level ||
|
||||||
// compute offset of current block and base position
|
!AssertBoundary(p, entry, originLevel))
|
||||||
ulong currentOffset = entry->Position - (ulong)m_Buffer;
|
|
||||||
|
|
||||||
if (prev != null &&
|
|
||||||
prev != _spareMemory &&
|
|
||||||
AssertBackwardBoundary(currentOffset, prev, originLevel) &&
|
|
||||||
IsAdjacentTo(entry, prev) &&
|
|
||||||
TryRemoveFromBucket(originLevel, DetermineSegmentIndex(prev->Size, originLevel), (BucketNode*)prev->Position))
|
|
||||||
{
|
|
||||||
// Merge
|
|
||||||
entry->Position = prev->Position;
|
|
||||||
entry->Size += prev->Size;
|
|
||||||
RemoveFromPositionChain(prev);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update linked list
|
|
||||||
/*
|
|
||||||
Entry* prevPrev = prev->PreviousNear;
|
|
||||||
if (prevPrev != null) {
|
|
||||||
prevPrev->NextNear = entry;
|
|
||||||
}
|
|
||||||
entry->PreviousNear = prevPrev;
|
|
||||||
/**/
|
|
||||||
}
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
// forward merging - simplified to single attempt
|
|
||||||
Entry* next = entry->NextNear;
|
|
||||||
|
|
||||||
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update linked list
|
// Console.WriteLine($"{(ulong)p->Size} merged to {(ulong)entry->Size} previous");
|
||||||
/*
|
entry->Size += p->Size;
|
||||||
Entry* nextNext = next->NextNear;
|
entry->Position = p->Position;
|
||||||
if (nextNext != null) {
|
RemoveFromBucket(originLevel, DetermineSegmentIndex(p->Size, originLevel), (BucketNode*)p->Position);
|
||||||
nextNext->PreviousNear = entry;
|
RemoveFromPositionChain(p);
|
||||||
}
|
} while (true);
|
||||||
entry->NextNear = nextNext;
|
|
||||||
/**/
|
|
||||||
|
|
||||||
// Merge
|
do
|
||||||
entry->Size += next->Size;
|
{
|
||||||
RemoveFromPositionChain(next);
|
// next direction merge
|
||||||
|
Entry* n = entry->NextNear;
|
||||||
|
if (n == null ||
|
||||||
|
n->State != EntryState.Empty ||
|
||||||
|
!IsAdjacentTo(result, n) ||
|
||||||
|
n->Level != entry->Level ||
|
||||||
|
!AssertBoundary(n, entry, originLevel))
|
||||||
|
{
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
entry->Level = GetLevelFromSize(entry->Size);
|
|
||||||
} while (originLevel < entry->Level);
|
// Console.WriteLine($"{(ulong)n->Size} merged to {(ulong)entry->Size} next");
|
||||||
|
entry->Size += n->Size;
|
||||||
|
RemoveFromBucket(originLevel, DetermineSegmentIndex(n->Size, originLevel), (BucketNode*)n->Position);
|
||||||
|
RemoveFromPositionChain(n);
|
||||||
|
} while (true);
|
||||||
|
originLevel = int.Min(9, originLevel);
|
||||||
|
entry->Level += entry->Size >= levelSizeArray[originLevel] * 4 ? 1 : 0;
|
||||||
|
entry->Level &= 15;
|
||||||
|
|
||||||
|
BucketNode* bucket = (BucketNode*)entry->Position;
|
||||||
|
bucket->EntryRef = entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -202,9 +140,14 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
Entry* prev = entry->PreviousNear;
|
Entry* prev = entry->PreviousNear;
|
||||||
Entry* next = entry->NextNear;
|
Entry* next = entry->NextNear;
|
||||||
|
|
||||||
if (prev != null) {
|
if (prev != null)
|
||||||
|
{
|
||||||
prev->NextNear = entry->NextNear;
|
prev->NextNear = entry->NextNear;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
_positionHead = entry->NextNear;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (next != null) {
|
if (next != null) {
|
||||||
next->PreviousNear = entry->PreviousNear;
|
next->PreviousNear = entry->PreviousNear;
|
||||||
}
|
}
|
||||||
@@ -228,11 +171,23 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
[FieldOffset(8)] internal Entry* NextNear; // Position linked list: next pointer
|
[FieldOffset(8)] internal Entry* NextNear; // Position linked list: next pointer
|
||||||
[FieldOffset(16)] internal Entry* PreviousNear; // Position linked list: previous pointer
|
[FieldOffset(16)] internal Entry* PreviousNear; // Position linked list: previous pointer
|
||||||
|
|
||||||
[FieldOffset(28)] internal int Level; // Level of the block in the allocator
|
[FieldOffset(28)] internal int _Level; // Level of the block in the allocator
|
||||||
[FieldOffset(24)] internal uint Size; // Block size information
|
[FieldOffset(24)] internal uint Size; // Block size information
|
||||||
[FieldOffset(32)] public EntryState State;
|
[FieldOffset(32)] public EntryState State;
|
||||||
[FieldOffset(0)] public nuint Position; // Memory block position
|
[FieldOffset(0)] public nuint Position; // Memory block position
|
||||||
|
|
||||||
|
public int Level
|
||||||
|
{
|
||||||
|
get => _Level;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value > 15 || value < 0) {
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
_Level = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal enum EntryState : uint
|
internal enum EntryState : uint
|
||||||
|
|||||||
@@ -39,6 +39,9 @@ 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)
|
||||||
|
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;
|
||||||
|
|||||||
@@ -42,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 :
|
||||||
1024UL * 1024UL * 1024UL; // lower default to 512MB
|
3UL * 1024UL * 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
|
||||||
@@ -64,6 +64,7 @@ namespace StgSharp
|
|||||||
{
|
{
|
||||||
uint size = sz > MaxAllocSize ? MaxAllocSize : sz;
|
uint size = sz > MaxAllocSize ? MaxAllocSize : sz;
|
||||||
BenchAllocThroughput(allocator, size, touch:false);
|
BenchAllocThroughput(allocator, size, touch:false);
|
||||||
|
allocator.Collect();
|
||||||
GC.Collect();
|
GC.Collect();
|
||||||
GC.WaitForPendingFinalizers();
|
GC.WaitForPendingFinalizers();
|
||||||
}
|
}
|
||||||
@@ -79,6 +80,7 @@ namespace StgSharp
|
|||||||
{
|
{
|
||||||
uint size = sz > MaxAllocSize ? MaxAllocSize : sz;
|
uint size = sz > MaxAllocSize ? MaxAllocSize : sz;
|
||||||
BenchAllocThroughput(allocator, size, touch:true);
|
BenchAllocThroughput(allocator, size, touch:true);
|
||||||
|
allocator.Collect();
|
||||||
GC.Collect();
|
GC.Collect();
|
||||||
GC.WaitForPendingFinalizers();
|
GC.WaitForPendingFinalizers();
|
||||||
}
|
}
|
||||||
@@ -90,6 +92,7 @@ namespace StgSharp
|
|||||||
|
|
||||||
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);
|
||||||
|
allocator.Collect();
|
||||||
}
|
}
|
||||||
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)");
|
||||||
@@ -98,6 +101,7 @@ namespace StgSharp
|
|||||||
Warmup(allocator);
|
Warmup(allocator);
|
||||||
|
|
||||||
BenchAllocFreePairs(allocator, 5_000_000, 256);
|
BenchAllocFreePairs(allocator, 5_000_000, 256);
|
||||||
|
allocator.Collect();
|
||||||
}
|
}
|
||||||
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)");
|
||||||
@@ -106,6 +110,7 @@ namespace StgSharp
|
|||||||
Warmup(allocator);
|
Warmup(allocator);
|
||||||
|
|
||||||
BenchFragmentationCycle(allocator, 512); // target within 500MB arena
|
BenchFragmentationCycle(allocator, 512); // target within 500MB arena
|
||||||
|
allocator.Collect();
|
||||||
}
|
}
|
||||||
Console.WriteLine();
|
Console.WriteLine();
|
||||||
Console.WriteLine("=== Benchmark Complete ===");
|
Console.WriteLine("=== Benchmark Complete ===");
|
||||||
@@ -156,7 +161,8 @@ namespace StgSharp
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
list.Add(h);
|
list.Add(h);
|
||||||
Console.WriteLine(i);
|
|
||||||
|
// Console.WriteLine(i);
|
||||||
}
|
}
|
||||||
sw.Stop();
|
sw.Stop();
|
||||||
double allocOpsPerSec = ops / sw.Elapsed.TotalSeconds;
|
double allocOpsPerSec = ops / sw.Elapsed.TotalSeconds;
|
||||||
|
|||||||
@@ -45,9 +45,9 @@ namespace StgSharp
|
|||||||
.InitializeModule();
|
.InitializeModule();
|
||||||
|
|
||||||
// TestRender();
|
// TestRender();
|
||||||
TestHlsf();
|
// TestHlsf();
|
||||||
|
|
||||||
// HlsfStressTest.Run();
|
HlsfStressTest.Run();
|
||||||
/*
|
/*
|
||||||
HybridLayerSegregatedFitAllocator hlsf = new HybridLayerSegregatedFitAllocator((nuint)(64 * (ulong)10000 * 10000));
|
HybridLayerSegregatedFitAllocator hlsf = new HybridLayerSegregatedFitAllocator((nuint)(64 * (ulong)10000 * 10000));
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user