hlsf wrong slicing solving
This commit is contained in:
@@ -80,100 +80,151 @@ namespace StgSharp.HighPerformance.Memory
|
||||
private void SliceMemory(Entry* e, int offset)
|
||||
{
|
||||
Entry* current = e;
|
||||
Entry* n = e->NextNear;
|
||||
Entry* next = e;
|
||||
|
||||
Entry* next = e->NextNear;
|
||||
ulong originOffset = e->Position - (ulong)m_Buffer;
|
||||
ulong finalOffset = originOffset + (ulong)offset;
|
||||
ulong end = e->Position + e->Size;
|
||||
ulong endOffset = end - (ulong)m_Buffer;
|
||||
int remainSize = (int)(e->Size - offset);
|
||||
ulong remainSize = (ulong)e->Size - (ulong)offset;
|
||||
|
||||
// get level of left edge
|
||||
int minLevel = int.Max((BitOperations.TrailingZeroCount(finalOffset) - 6) / 2, 0);
|
||||
int i = minLevel >= levelSizeArray.Length ? levelSizeArray.Length - 1 : minLevel;
|
||||
|
||||
for (; i < levelSizeArray.Length; i++)
|
||||
try
|
||||
{
|
||||
int levelSize = levelSizeArray[i];
|
||||
ulong levelLong = (ulong)levelSize;
|
||||
ulong levelUpper = levelLong * 4;
|
||||
ulong board = (finalOffset + levelUpper - 1) / levelUpper * levelUpper;
|
||||
int blockCount = (int)((board - ulong.Min(finalOffset, endOffset)) / levelLong);
|
||||
if (blockCount == 0)
|
||||
for (; i < levelSizeArray.Length; i++)
|
||||
{
|
||||
continue;
|
||||
int levelSize = levelSizeArray[i];
|
||||
ulong levelLong = (ulong)levelSize;
|
||||
ulong levelUpper = levelLong * 4;
|
||||
|
||||
// smallest board that is larger than finalOffset
|
||||
ulong board = (finalOffset + levelUpper - 1) / levelUpper * levelUpper;
|
||||
int blockCount = (int)((long)ulong.Min(board, endOffset) - (long)finalOffset) / (int)levelLong;
|
||||
if (blockCount == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
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;
|
||||
|
||||
// link new entry
|
||||
newEntry->PreviousNear = current;
|
||||
newEntry->NextNear = next;
|
||||
current->NextNear = newEntry;
|
||||
next->PreviousNear = newEntry;
|
||||
|
||||
newEntry->Level = i;
|
||||
PushLevel(i, Math.Min(blockCount - 1, 2), bucket);
|
||||
|
||||
// set new entry
|
||||
// Entry* old = current;
|
||||
current = newEntry;
|
||||
|
||||
finalOffset += (ulong)s;
|
||||
remainSize -= (ulong)s;
|
||||
if (remainSize <= (ulong)levelSize)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (remainSize <= 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
int s = blockCount * levelSize;
|
||||
|
||||
// create and init new entry
|
||||
Entry* newEntry = (Entry*)_entries.Allocate();
|
||||
newEntry->Position = (nuint)((ulong)m_Buffer + finalOffset);
|
||||
newEntry->Size = (uint)s;
|
||||
BucketNode* bucket = (BucketNode*)newEntry->Position;
|
||||
bucket->EntryRef = newEntry;
|
||||
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 = n;
|
||||
current->NextNear = newEntry;
|
||||
next->PreviousNear = newEntry;
|
||||
|
||||
newEntry->Level = i;
|
||||
PushLevel(i, blockCount - 1, bucket);
|
||||
// link new entry
|
||||
newEntry->PreviousNear = current;
|
||||
newEntry->NextNear = next;
|
||||
current->NextNear = newEntry;
|
||||
if (next != null) {
|
||||
next->PreviousNear = newEntry;
|
||||
}
|
||||
|
||||
// set new entry
|
||||
Entry* old = current;
|
||||
current = newEntry;
|
||||
newEntry->Level = i;
|
||||
PushLevel(i, Math.Min(blockCount - 1, 2), bucket);
|
||||
|
||||
finalOffset += (ulong)s;
|
||||
remainSize -= s;
|
||||
if (remainSize <= 0) {
|
||||
return;
|
||||
// update lock and data
|
||||
finalOffset += (ulong)s;
|
||||
remainSize -= (ulong)s;
|
||||
}
|
||||
if (remainSize <= 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (i -= 1; i >= 0; i--)
|
||||
{
|
||||
int levelSize = levelSizeArray[i];
|
||||
ulong levelLong = (ulong)levelSize;
|
||||
ulong board = endOffset / levelLong * levelLong;
|
||||
int blockCount = (int)(board - finalOffset) / levelSize;
|
||||
if (blockCount <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
int s = blockCount * levelSize;
|
||||
System.Diagnostics.Debug.Assert(finalOffset + (ulong)s <= endOffset);
|
||||
|
||||
// create and init new entry
|
||||
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);
|
||||
|
||||
// set new entry
|
||||
// Entry* old = current;
|
||||
current = newEntry;
|
||||
|
||||
// update lock and data
|
||||
finalOffset += (ulong)s;
|
||||
remainSize -= (ulong)s;
|
||||
if (remainSize <= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i -= 1; i >= 0; i--)
|
||||
finally
|
||||
{
|
||||
int levelSize = levelSizeArray[i];
|
||||
ulong levelLong = (ulong)levelSize;
|
||||
ulong board = endOffset / levelLong * levelLong;
|
||||
int blockCount = (int)((board - finalOffset) / (ulong)levelSize);
|
||||
if (blockCount == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
int s = blockCount * levelSize;
|
||||
|
||||
// create and init new entry
|
||||
Entry* newEntry = (Entry*)_entries.Allocate();
|
||||
newEntry->Position = (nuint)((ulong)m_Buffer + finalOffset);
|
||||
newEntry->Size = (uint)s;
|
||||
BucketNode* bucket = (BucketNode*)newEntry->Position;
|
||||
bucket->EntryRef = newEntry;
|
||||
|
||||
// link new entry
|
||||
newEntry->PreviousNear = current;
|
||||
newEntry->NextNear = n;
|
||||
current->NextNear = newEntry;
|
||||
if (next != null) {
|
||||
next->PreviousNear = newEntry;
|
||||
}
|
||||
|
||||
newEntry->Level = i;
|
||||
PushLevel(i, blockCount - 1, bucket);
|
||||
|
||||
// set new entry
|
||||
Entry* old = current;
|
||||
current = newEntry;
|
||||
|
||||
// update lock and data
|
||||
finalOffset += (ulong)s;
|
||||
remainSize -= s;
|
||||
if (remainSize <= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
e->Size = (uint)(offset);
|
||||
e->Level = GetLevelFromSize((uint)offset);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -66,6 +66,7 @@ namespace StgSharp.HighPerformance.Memory
|
||||
node->NextLevel = head;
|
||||
BucketNode* headNode = head;
|
||||
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}");
|
||||
if (headNode != null)
|
||||
|
||||
@@ -35,7 +35,7 @@ using System.Threading;
|
||||
|
||||
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;
|
||||
@@ -48,6 +48,7 @@ namespace StgSharp.HighPerformance.Memory
|
||||
public ChunkedSlabAllocator(nuint count)
|
||||
{
|
||||
_currentBuffer = (nuint)NativeMemory.Alloc(count * (nuint)_elementSize);
|
||||
|
||||
_currentCapacity = count;
|
||||
int initCount = count switch
|
||||
{
|
||||
@@ -60,6 +61,7 @@ namespace StgSharp.HighPerformance.Memory
|
||||
span[i] = _currentBuffer + (nuint)(i * _elementSize);
|
||||
}
|
||||
_buffers = new(4);
|
||||
_buffers.Push(_currentBuffer);
|
||||
_recycle = BufferStackBuilder.Create(span);
|
||||
_currentCapacity = count;
|
||||
_currentIndex = (nuint)initCount;
|
||||
@@ -70,26 +72,27 @@ namespace StgSharp.HighPerformance.Memory
|
||||
if (_recycle.TryPop(out nuint handle)) {
|
||||
return handle;
|
||||
}
|
||||
nuint bufferHandle = _currentBuffer;
|
||||
|
||||
// nuint bufferHandle = _currentBuffer;
|
||||
nuint curIndex = _currentIndex;
|
||||
if (curIndex >= _currentCapacity)
|
||||
{
|
||||
nuint cap = _currentCapacity;
|
||||
nuint newBuffer = (nuint)NativeMemory.Alloc(cap * (nuint)_elementSize);
|
||||
_buffers.Push(bufferHandle);
|
||||
_buffers.Push(newBuffer);
|
||||
_currentBuffer = newBuffer;
|
||||
_currentIndex = 0;
|
||||
}
|
||||
curIndex = _currentIndex;
|
||||
_ = Interlocked.Add(
|
||||
ref Unsafe.As<nuint, ulong>(ref _currentIndex), (ulong)_elementSize);
|
||||
_currentIndex += 1;
|
||||
return _currentBuffer + ((nuint)_elementSize * curIndex);
|
||||
}
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
NativeMemory.Free((void*)_currentBuffer);
|
||||
while (_buffers.TryPop(out nuint buffer)) {
|
||||
// NativeMemory.Free((void*)_currentBuffer);
|
||||
while (_buffers.TryPop(out nuint buffer))
|
||||
{
|
||||
NativeMemory.Free((void*)buffer);
|
||||
}
|
||||
_buffers.Dispose();
|
||||
|
||||
@@ -29,7 +29,7 @@ using System;
|
||||
|
||||
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();
|
||||
@@ -56,29 +56,32 @@ namespace StgSharp.HighPerformance.Memory
|
||||
/// Thrown when requesting unsupported configurations.
|
||||
/// </exception>
|
||||
/// <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
|
||||
/// 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> ///
|
||||
/// 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 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>
|
||||
/// 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)
|
||||
public static SlabAllocator<T> Create(
|
||||
nuint count,
|
||||
SlabBufferLayout layout,
|
||||
bool concurrentSupport = true)
|
||||
{
|
||||
if (concurrentSupport) {
|
||||
return layout switch
|
||||
|
||||
Reference in New Issue
Block a user