Remove hlsf, TLSF.alloc is ok
This commit is contained in:
+2
-1
@@ -28,4 +28,5 @@ build.ninja
|
|||||||
.ninja_log
|
.ninja_log
|
||||||
.cache/
|
.cache/
|
||||||
|
|
||||||
/StgSharp.Native/lib/
|
/StgSharp.Native/lib/
|
||||||
|
!/StgSharp.Native/lib/**/CMakeLists.txt
|
||||||
@@ -58,7 +58,7 @@ namespace StgSharp.Benchmark
|
|||||||
private readonly List<HlsfHandle> _hlsfLocalList = new();
|
private readonly List<HlsfHandle> _hlsfLocalList = new();
|
||||||
private readonly List<nint> _libcLocalList = new();
|
private readonly List<nint> _libcLocalList = new();
|
||||||
private Random _random = new(42);
|
private Random _random = new(42);
|
||||||
private HybridLayerSegregatedFitAllocator _hlsfAllocator = null!;
|
private TwoLayerSegregatedFitAllocator _hlsfAllocator = null!;
|
||||||
|
|
||||||
[GlobalCleanup]
|
[GlobalCleanup]
|
||||||
public void Cleanup()
|
public void Cleanup()
|
||||||
@@ -92,7 +92,7 @@ namespace StgSharp.Benchmark
|
|||||||
{
|
{
|
||||||
_hlsfAllocator?.Dispose();
|
_hlsfAllocator?.Dispose();
|
||||||
_random = new Random(42);
|
_random = new Random(42);
|
||||||
_hlsfAllocator = new HybridLayerSegregatedFitAllocator(GetArenaBytes(), align:(int)Alignment);
|
_hlsfAllocator = new TwoLayerSegregatedFitAllocator(GetArenaBytes(), align:(int)Alignment);
|
||||||
BuildIterationWorkload();
|
BuildIterationWorkload();
|
||||||
_hlsfLocalList.Clear();
|
_hlsfLocalList.Clear();
|
||||||
_libcLocalList.Clear();
|
_libcLocalList.Clear();
|
||||||
@@ -147,7 +147,7 @@ namespace StgSharp.Benchmark
|
|||||||
|
|
||||||
private void RunHlsf(
|
private void RunHlsf(
|
||||||
int batch,
|
int batch,
|
||||||
HybridLayerSegregatedFitAllocator allocator,
|
TwoLayerSegregatedFitAllocator allocator,
|
||||||
FreePolicy policy
|
FreePolicy policy
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,117 +0,0 @@
|
|||||||
//-----------------------------------------------------------------------
|
|
||||||
// -----------------------------------------------------------------------
|
|
||||||
// file="HLSFAllocationHandle"
|
|
||||||
// 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 System;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Drawing;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
using Allocator = StgSharp.HighPerformance.Memory.HybridLayerSegregatedFitAllocator;
|
|
||||||
|
|
||||||
namespace StgSharp.HighPerformance.Memory
|
|
||||||
{
|
|
||||||
public readonly unsafe struct HlsfHandle
|
|
||||||
{
|
|
||||||
|
|
||||||
internal readonly Allocator.Entry* EntryHandle;
|
|
||||||
public readonly nuint AllocSize;
|
|
||||||
|
|
||||||
internal HlsfHandle(
|
|
||||||
Allocator.Entry* handle,
|
|
||||||
nuint s
|
|
||||||
)
|
|
||||||
{
|
|
||||||
EntryHandle = handle;
|
|
||||||
Pointer = (byte*)handle->Position;
|
|
||||||
AllocSize = s;
|
|
||||||
|
|
||||||
// Console.WriteLine($"{(ulong)handle->PreviousNear}<->{(ulong)handle->NextNear},\t{handle->Size}");
|
|
||||||
}
|
|
||||||
|
|
||||||
public readonly ref byte this[
|
|
||||||
uint index
|
|
||||||
]
|
|
||||||
{
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
get
|
|
||||||
{
|
|
||||||
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(index, AllocSize);
|
|
||||||
return ref Pointer[index];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public readonly byte* Pointer { get; init; }
|
|
||||||
|
|
||||||
public readonly Span<byte> BufferHandle => new(Pointer, (int)AllocSize);
|
|
||||||
|
|
||||||
public readonly Enumerator GetEnumerator()
|
|
||||||
{
|
|
||||||
return new Enumerator(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ref struct Enumerator : IEnumerator<byte>
|
|
||||||
{
|
|
||||||
|
|
||||||
private HlsfHandle _handle;
|
|
||||||
|
|
||||||
private uint _index = ~0u;
|
|
||||||
|
|
||||||
internal Enumerator(
|
|
||||||
HlsfHandle span
|
|
||||||
)
|
|
||||||
{
|
|
||||||
_handle = span;
|
|
||||||
Span = span.BufferHandle;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ref byte RefCurrent => ref _handle[_index];
|
|
||||||
|
|
||||||
public Span<byte> Span { get; init; }
|
|
||||||
|
|
||||||
public readonly void Dispose() { }
|
|
||||||
|
|
||||||
public bool MoveNext()
|
|
||||||
{
|
|
||||||
_index++;
|
|
||||||
return _index < _handle.AllocSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Reset()
|
|
||||||
{
|
|
||||||
_index = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
object IEnumerator.Current => RefCurrent;
|
|
||||||
|
|
||||||
byte IEnumerator<byte>.Current => RefCurrent;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
using StgSharp.HighPerformance.ProcessorAbstraction;
|
using StgSharp.HighPerformance.ProcessorAbstraction;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
using Hlsf = StgSharp.HighPerformance.Memory.HybridLayerSegregatedFitAllocator;
|
using Tlsf = StgSharp.HighPerformance.Memory.TwoLayerSegregatedFitAllocator;
|
||||||
|
|
||||||
namespace StgSharp.HighPerformance.Memory
|
namespace StgSharp.HighPerformance.Memory
|
||||||
{
|
{
|
||||||
@@ -51,34 +51,33 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Explicit, Size = 64, Pack = 16)]
|
[StructLayout(LayoutKind.Explicit, Size = 64, Pack = 64)]
|
||||||
private unsafe struct CacheLineHead
|
private unsafe struct CacheLineHead
|
||||||
{
|
{
|
||||||
|
|
||||||
[FieldOffset(0)] internal Hlsf.Entry AllocatorEntry;
|
[FieldOffset(0)] internal Tlsf.Handle AllocatorEntry;
|
||||||
[FieldOffset(0)] internal readonly M512 Buffer;
|
[FieldOffset(0)] internal readonly M512 Buffer;
|
||||||
|
|
||||||
#region reference counting and state information
|
#region reference counting and state information
|
||||||
|
|
||||||
[FieldOffset(52)] internal int RefCount;
|
[FieldOffset(24)] internal int RefCount;
|
||||||
[FieldOffset(60)] private readonly int Reverse;
|
[FieldOffset(28)] internal readonly int Id;
|
||||||
[FieldOffset(56)] internal readonly int Id;
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
#region hlsf entry alias
|
#region hlsf entry alias
|
||||||
|
|
||||||
[FieldOffset(24)] public readonly nuint Address;
|
[FieldOffset(8)] public readonly Ptr64 Address;
|
||||||
[FieldOffset(16)] public readonly uint Size;
|
[FieldOffset(16)] public readonly Ptr64 Size;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region cache line information
|
#region cache line information
|
||||||
|
|
||||||
[FieldOffset(50)] internal short Predictor;
|
[FieldOffset(32)] internal short Predictor;
|
||||||
[FieldOffset(48)] internal ushort Profile;
|
[FieldOffset(34)] internal ushort Profile;
|
||||||
[FieldOffset(40)] internal nuint Origin;
|
[FieldOffset(36)] internal nuint Origin;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ using System.Runtime.Intrinsics.X86;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
using HlsfEntry = StgSharp.HighPerformance.Memory.HybridLayerSegregatedFitAllocator.Entry;
|
using Tlsf = StgSharp.HighPerformance.Memory.TwoLayerSegregatedFitAllocator;
|
||||||
|
|
||||||
namespace StgSharp.HighPerformance.Memory
|
namespace StgSharp.HighPerformance.Memory
|
||||||
{
|
{
|
||||||
@@ -87,8 +87,11 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
int id
|
int id
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
HlsfEntry* entry = (HlsfEntry*)(_entryManager._basePtr + id);
|
throw new NotImplementedException();
|
||||||
|
/*
|
||||||
|
Tlsf* entry = (Tlsf*)(_entryManager._basePtr + id);
|
||||||
_bufferAllocator.Free(entry);
|
_bufferAllocator.Free(entry);
|
||||||
|
/**/
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
@@ -105,7 +108,7 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
int id = head->Predictor;
|
int id = head->Predictor;
|
||||||
IL4Predict predict = predictorSpan[id] ??
|
IL4Predict predict = predictorSpan[id] ??
|
||||||
throw new L4PredictUnregisteredException(id);
|
throw new L4PredictUnregisteredException(id);
|
||||||
predict.WriteBack(head->Origin, head->Profile, new ReadOnlySpan<byte>((byte*)head->Address, (int)head->Size));
|
predict.WriteBack(head->Origin, head->Profile, new ReadOnlySpan<byte>((byte*)(ulong)head->Address, (int)(ulong)head->Size));
|
||||||
RecycleLine(index);
|
RecycleLine(index);
|
||||||
(*_mapCount)[index] = 0;
|
(*_mapCount)[index] = 0;
|
||||||
(*_predictCount)[index] = int.MaxValue;
|
(*_predictCount)[index] = int.MaxValue;
|
||||||
@@ -121,12 +124,14 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
|
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (!_bufferAllocator.TryAlloc(size, out HlsfHandle handle))
|
if (!_bufferAllocator.TryAlloc(size, out Tlsf.Handle handle))
|
||||||
{
|
{
|
||||||
index = 0;
|
index = 0;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
CacheLineHead* entry = (CacheLineHead*)handle.EntryHandle;
|
|
||||||
|
// origin :CacheLineHead* entry = (CacheLineHead*)handle.EntryHandle;
|
||||||
|
CacheLineHead* entry = (CacheLineHead*)handle.Pointer;
|
||||||
index = entry->Id;
|
index = entry->Id;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
(*_mapCount)[index] = 1;
|
(*_mapCount)[index] = 1;
|
||||||
|
|
||||||
// pre fetch them here
|
// pre fetch them here
|
||||||
predict.Prefetch(head->Origin, head->Profile, new Span<byte>((byte*)head->Address, (int)head->Size));
|
predict.Prefetch(head->Origin, head->Profile, new Span<byte>((byte*)(ulong)head->Address, (int)(ulong)head->Size));
|
||||||
_ = _map.TryAddOrSet(address, (nuint)head, out _);
|
_ = _map.TryAddOrSet(address, (nuint)head, out _);
|
||||||
}
|
}
|
||||||
_ = Interlocked.Add(ref _aheadCount, actualCount);
|
_ = Interlocked.Add(ref _aheadCount, actualCount);
|
||||||
|
|||||||
@@ -31,14 +31,14 @@ using System.Linq;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
using Hlsf = StgSharp.HighPerformance.Memory.HybridLayerSegregatedFitAllocator;
|
using Tlsf = StgSharp.HighPerformance.Memory.TwoLayerSegregatedFitAllocator;
|
||||||
|
|
||||||
namespace StgSharp.HighPerformance.Memory
|
namespace StgSharp.HighPerformance.Memory
|
||||||
{
|
{
|
||||||
public partial class L4
|
public partial class L4
|
||||||
{
|
{
|
||||||
|
|
||||||
private sealed unsafe class Slab : SlabAllocator<Hlsf.Entry>
|
private sealed unsafe class Slab : SlabAllocator<Tlsf.Metadata>
|
||||||
{
|
{
|
||||||
|
|
||||||
private const int Capacity = L4.CacheLineCount;
|
private const int Capacity = L4.CacheLineCount;
|
||||||
@@ -91,7 +91,7 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
_recycleCount++;
|
_recycleCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override SlabAllocationHandle<Hlsf.Entry> ReadAllocation()
|
public override SlabAllocationHandle<Tlsf.Metadata> ReadAllocation()
|
||||||
{
|
{
|
||||||
return new(this, false);
|
return new(this, false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
// file="L4"
|
// file="L4"
|
||||||
// Project: StgSharp
|
// Project: StgSharp
|
||||||
// AuthorGroup: Nitload Space
|
// AuthorGroup: Nitload
|
||||||
// Copyright (c) Nitload Space. All rights reserved.
|
// Copyright (c) Nitload. All rights reserved.
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -31,7 +31,7 @@ using StgSharp.HighPerformance.ProcessorAbstraction;
|
|||||||
using System.Data;
|
using System.Data;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
using HLSF = StgSharp.HighPerformance.Memory.HybridLayerSegregatedFitAllocator;
|
using HLSF = StgSharp.HighPerformance.Memory.TwoLayerSegregatedFitAllocator;
|
||||||
|
|
||||||
namespace StgSharp.HighPerformance.Memory
|
namespace StgSharp.HighPerformance.Memory
|
||||||
{
|
{
|
||||||
@@ -62,7 +62,12 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
private readonly Slab _entryManager;
|
private readonly Slab _entryManager;
|
||||||
private readonly SwissTable _map;
|
private readonly SwissTable _map;
|
||||||
|
|
||||||
public L4(int bindedNuma, nuint bufferSize = 8192 * 8192, int align = 16, nuint maxCacheLineSize = 8192)
|
public L4(
|
||||||
|
int bindedNuma,
|
||||||
|
nuint bufferSize = 8192 * 8192,
|
||||||
|
int align = 16,
|
||||||
|
nuint maxCacheLineSize = 8192
|
||||||
|
)
|
||||||
{
|
{
|
||||||
_bindedNuma = bindedNuma;
|
_bindedNuma = bindedNuma;
|
||||||
|
|
||||||
@@ -85,8 +90,8 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
// init all components with the buffer, and move the buffer pointer forward.
|
// init all components with the buffer, and move the buffer pointer forward.
|
||||||
nuint _buffer = _baseAddress + normalizedBufferSize;
|
nuint _buffer = _baseAddress + normalizedBufferSize;
|
||||||
_entryManager = new Slab(_buffer);
|
_entryManager = new Slab(_buffer);
|
||||||
_bufferAllocator = new HLSF(entrySize, align, maxCacheLineSize, (void*)_baseAddress, static _ => { },
|
_bufferAllocator = new HLSF(entrySize, align, maxCacheLineSize, (void*)_baseAddress,
|
||||||
_entryManager);
|
static _ => { }/*, _entryManager/**/);
|
||||||
ptr += entrySize;
|
ptr += entrySize;
|
||||||
_head = (CacheLineHead*)ptr;
|
_head = (CacheLineHead*)ptr;
|
||||||
ptr += headSize;
|
ptr += headSize;
|
||||||
@@ -112,7 +117,9 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
// public IL4Predict PrefetchPredict { get; set; }
|
// public IL4Predict PrefetchPredict { get; set; }
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void ContinueOnce(L4CacheLine line)
|
public void ContinueOnce(
|
||||||
|
L4CacheLine line
|
||||||
|
)
|
||||||
{
|
{
|
||||||
if (line.Address > 0) {
|
if (line.Address > 0) {
|
||||||
_ = Interlocked.Decrement(ref _aheadCount);
|
_ = Interlocked.Decrement(ref _aheadCount);
|
||||||
@@ -150,7 +157,9 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public L4CacheLine Map(ref CacheLineDescription prediction)
|
public L4CacheLine Map(
|
||||||
|
ref CacheLineDescription prediction
|
||||||
|
)
|
||||||
{
|
{
|
||||||
if ((_aheadCount < PredictionLeastAhead) && !_exhausted)
|
if ((_aheadCount < PredictionLeastAhead) && !_exhausted)
|
||||||
{
|
{
|
||||||
@@ -182,7 +191,7 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
_ = Interlocked.Increment(ref _activeCount);
|
_ = Interlocked.Increment(ref _activeCount);
|
||||||
_ = Interlocked.Decrement(ref _aheadCount);
|
_ = Interlocked.Decrement(ref _aheadCount);
|
||||||
_ = Interlocked.Decrement(ref (*_mapCount)[entry->Id]);
|
_ = Interlocked.Decrement(ref (*_mapCount)[entry->Id]);
|
||||||
return new L4CacheLine(entry->Address, ref _activeCount, ref entry->RefCount);
|
return new L4CacheLine((nuint)entry->Address, ref _activeCount, ref entry->RefCount);
|
||||||
} else
|
} else
|
||||||
{
|
{
|
||||||
_ = Interlocked.Decrement(ref entry->RefCount);
|
_ = Interlocked.Decrement(ref entry->RefCount);
|
||||||
@@ -203,7 +212,11 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
private ref int _refCount;
|
private ref int _refCount;
|
||||||
private nuint _handle;
|
private nuint _handle;
|
||||||
|
|
||||||
internal L4CacheLine(nuint handle, ref int activeCount, ref int cacheRefCount)
|
internal L4CacheLine(
|
||||||
|
nuint handle,
|
||||||
|
ref int activeCount,
|
||||||
|
ref int cacheRefCount
|
||||||
|
)
|
||||||
{
|
{
|
||||||
_handle = handle;
|
_handle = handle;
|
||||||
_refCount = ref cacheRefCount;
|
_refCount = ref cacheRefCount;
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
// file="TwoLayerSegregatedFitAllocator"
|
// file="TwoLayerSegregatedFitAllocator"
|
||||||
// Project: StgSharp
|
// Project: StgSharp
|
||||||
// AuthorGroup: Nitload Space
|
// AuthorGroup: Nitload
|
||||||
// Copyright (c) Nitload Space. All rights reserved.
|
// Copyright (c) Nitload. All rights reserved.
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
// of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -29,6 +29,7 @@ using StgSharp.HighPerformance.Memory;
|
|||||||
using StgSharp.HighPerformance.ProcessorAbstraction;
|
using StgSharp.HighPerformance.ProcessorAbstraction;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Drawing;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Reflection.Metadata;
|
using System.Reflection.Metadata;
|
||||||
@@ -44,13 +45,13 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
|
|
||||||
private const ulong Mask48Bits = 0x0000FFFFFFFFFFFF;
|
private const ulong Mask48Bits = 0x0000FFFFFFFFFFFF;
|
||||||
|
|
||||||
private readonly Metadata*[] _bucket;
|
private readonly int[] _bucket;
|
||||||
private readonly Metadata* _spareMemory;
|
private readonly Metadata* _spareMemory;
|
||||||
private readonly Action<nuint> _freeHandle;
|
private readonly Action<nuint> _freeHandle;
|
||||||
private bool disposedValue;
|
private bool disposedValue;
|
||||||
private readonly int _align;
|
private readonly int _align;
|
||||||
|
|
||||||
private int _allocEnd;
|
private int _metadataEnd;
|
||||||
private readonly Ptr64 _basePtr;
|
private readonly Ptr64 _basePtr;
|
||||||
private readonly ulong _maxAllocSize;
|
private readonly ulong _maxAllocSize;
|
||||||
private readonly ulong _totalSize;
|
private readonly ulong _totalSize;
|
||||||
@@ -60,7 +61,8 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
int align,
|
int align,
|
||||||
ulong maxBlockSize,
|
ulong maxBlockSize,
|
||||||
Func<nuint, int, nuint> allocator,
|
Func<nuint, int, nuint> allocator,
|
||||||
Action<nuint> freeHandle)
|
Action<nuint> freeHandle
|
||||||
|
)
|
||||||
: this(byteSize, align, maxBlockSize, (void*)allocator(byteSize, align), freeHandle) { }
|
: this(byteSize, align, maxBlockSize, (void*)allocator(byteSize, align), freeHandle) { }
|
||||||
|
|
||||||
internal TwoLayerSegregatedFitAllocator(
|
internal TwoLayerSegregatedFitAllocator(
|
||||||
@@ -68,16 +70,17 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
int align,
|
int align,
|
||||||
ulong maxBlockSize,
|
ulong maxBlockSize,
|
||||||
void* externalPointer,
|
void* externalPointer,
|
||||||
Action<nuint> freeHandle)
|
Action<nuint> freeHandle
|
||||||
|
)
|
||||||
{
|
{
|
||||||
_totalSize = byteSize;
|
_totalSize = byteSize;
|
||||||
_basePtr = (Ptr64)(ulong)externalPointer;
|
_basePtr = (Ptr64)(ulong)externalPointer;
|
||||||
_align = align;
|
_align = align;
|
||||||
_maxAllocSize = ulong.Max(64, (~64UL) & (ulong.Min(maxBlockSize + 63, Mask48Bits)));
|
_maxAllocSize = ulong.Max(64, (~64UL) & (ulong.Min(maxBlockSize + 63, Mask48Bits)));
|
||||||
_bucket = new Metadata*[58 - BitOperations.LeadingZeroCount(_maxAllocSize)];
|
_bucket = new int[GetLevel(_maxAllocSize) + 1];
|
||||||
_spareMemory = &((Metadata*)(ulong)(_basePtr + _totalSize))[-1];
|
_spareMemory = &((Metadata*)(ulong)(_basePtr + _totalSize))[-1];
|
||||||
_freeHandle = freeHandle ?? (static_ => { });
|
_freeHandle = freeHandle ?? (static_ => { });
|
||||||
_allocEnd = 0;
|
_metadataEnd = 0;
|
||||||
|
|
||||||
// init spare memory node
|
// init spare memory node
|
||||||
|
|
||||||
@@ -89,59 +92,128 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
_spareMemory->PreviousNear = 0;
|
_spareMemory->PreviousNear = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TwoLayerSegregatedFitAllocator(nuint byteSize)
|
public TwoLayerSegregatedFitAllocator(
|
||||||
|
nuint byteSize
|
||||||
|
)
|
||||||
: this(byteSize, 16, 32U * 1024U * 1024U, NativeMemory.AlignedAlloc(byteSize, 16),
|
: this(byteSize, 16, 32U * 1024U * 1024U, NativeMemory.AlignedAlloc(byteSize, 16),
|
||||||
ptr => NativeMemory.AlignedFree((void*)ptr)) { }
|
ptr => NativeMemory.AlignedFree((void*)ptr)) { }
|
||||||
|
|
||||||
public TwoLayerSegregatedFitAllocator(nuint byteSize, uint maxBlockSize)
|
public TwoLayerSegregatedFitAllocator(
|
||||||
|
nuint byteSize,
|
||||||
|
uint maxBlockSize
|
||||||
|
)
|
||||||
: this(byteSize, 16, maxBlockSize, NativeMemory.AlignedAlloc(byteSize, 16),
|
: this(byteSize, 16, maxBlockSize, NativeMemory.AlignedAlloc(byteSize, 16),
|
||||||
ptr => NativeMemory.AlignedFree((void*)ptr)) { }
|
ptr => NativeMemory.AlignedFree((void*)ptr)) { }
|
||||||
|
|
||||||
public TwoLayerSegregatedFitAllocator(nuint byteSize, int align)
|
public TwoLayerSegregatedFitAllocator(
|
||||||
|
nuint byteSize,
|
||||||
|
int align
|
||||||
|
)
|
||||||
: this(byteSize, align, 32U * 1024U * 1024U, NativeMemory.AlignedAlloc(byteSize, 16),
|
: this(byteSize, align, 32U * 1024U * 1024U, NativeMemory.AlignedAlloc(byteSize, 16),
|
||||||
ptr => NativeMemory.AlignedFree((void*)ptr)) { }
|
ptr => NativeMemory.AlignedFree((void*)ptr)) { }
|
||||||
|
|
||||||
public TwoLayerSegregatedFitAllocator(nuint byteSize, int align, nuint maxBlockSize)
|
public TwoLayerSegregatedFitAllocator(
|
||||||
|
nuint byteSize,
|
||||||
|
int align,
|
||||||
|
nuint maxBlockSize
|
||||||
|
)
|
||||||
: this(byteSize, align, maxBlockSize, NativeMemory.AlignedAlloc(byteSize, 16),
|
: this(byteSize, align, maxBlockSize, NativeMemory.AlignedAlloc(byteSize, 16),
|
||||||
ptr => NativeMemory.AlignedFree((void*)ptr)) { }
|
ptr => NativeMemory.AlignedFree((void*)ptr)) { }
|
||||||
|
|
||||||
public Handle Alloc(nuint size)
|
public Handle Alloc(
|
||||||
|
nuint size
|
||||||
|
)
|
||||||
{
|
{
|
||||||
ulong allocSize = nuint.Max(64, (~64U) & (size + 63));
|
ulong allocSize = ulong.Max(64, (~63UL) & ((ulong)size + 63));
|
||||||
if (allocSize > _maxAllocSize) {
|
if (allocSize > _maxAllocSize) {
|
||||||
throw new OverflowException("Requested allocation size exceeds the maximum block size.");
|
throw new OverflowException("Requested allocation size exceeds the maximum block size.");
|
||||||
}
|
}
|
||||||
int level = 57 - BitOperations.LeadingZeroCount(allocSize);
|
int level = GetLevel(allocSize);
|
||||||
Metadata* entry = null;
|
Metadata* entry = null;
|
||||||
for (int i = level; i < _bucket.Length; i++)
|
for (int i = level; i < _bucket.Length; i++)
|
||||||
{
|
{
|
||||||
entry = _bucket[i];
|
int idx = _bucket[i];
|
||||||
if (entry is not null)
|
if (idx != 0)
|
||||||
{
|
{
|
||||||
_bucket[i] = entry->NextLevel != 0 ? &_spareMemory[-entry->NextLevel] : null;
|
entry = &_spareMemory[-idx];
|
||||||
|
_bucket[i] = entry->NextLevel;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (entry is null && !TryGetNewMetadata(allocSize, out entry)) {
|
if (entry is null && !TryAllocNewChunk(allocSize, out entry)) {
|
||||||
throw new OverflowException("No available ");
|
throw new OverflowException("No available ");
|
||||||
}
|
}
|
||||||
ulong remain = entry->Size - allocSize;
|
int entryIdx = (int)(_spareMemory - entry);
|
||||||
|
ulong entrySize = entry->Size;
|
||||||
|
ulong remain = entrySize - allocSize;
|
||||||
|
|
||||||
throw new NotImplementedException();
|
// slice tail
|
||||||
|
if (remain > (entrySize >> 7) && TryGetNewMetadata(out Metadata* newEntry))
|
||||||
|
{
|
||||||
|
int newEntryIdx = (int)(_spareMemory - newEntry);
|
||||||
|
newEntry->Position = entry->Position + allocSize;
|
||||||
|
newEntry->Size = remain;
|
||||||
|
int newLevel = GetLevel(remain);
|
||||||
|
int bucketHeadIdx = _bucket[newLevel];
|
||||||
|
if (bucketHeadIdx != 0)
|
||||||
|
{
|
||||||
|
// if there is already an entry in the bucket, link it to the new entry
|
||||||
|
Metadata* bucketHead = &_spareMemory[-bucketHeadIdx];
|
||||||
|
newEntry->NextLevel = bucketHeadIdx;
|
||||||
|
bucketHead->PreviousLevel = newEntryIdx;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
newEntry->NextLevel = 0;
|
||||||
|
}
|
||||||
|
newEntry->PreviousLevel = 0;
|
||||||
|
_bucket[newLevel] = newEntryIdx;
|
||||||
|
|
||||||
|
// push to position chain
|
||||||
|
int afterIdx = entry->NextNear;
|
||||||
|
Metadata* after = &_spareMemory[-afterIdx];
|
||||||
|
newEntry->NextNear = afterIdx;
|
||||||
|
after->PreviousNear = newEntryIdx;
|
||||||
|
entry->NextNear = newEntryIdx;
|
||||||
|
newEntry->PreviousNear = entryIdx;
|
||||||
|
}
|
||||||
|
return new Handle(entry, _basePtr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Collect() { }
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
Dispose(disposing:true);
|
Dispose(disposing:true);
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Free(Handle handle)
|
public void Free(
|
||||||
|
Handle handle,
|
||||||
|
int collectScanWindow = 4
|
||||||
|
)
|
||||||
|
{
|
||||||
|
Metadata* head = handle._meta;
|
||||||
|
for (int i = 0; i < collectScanWindow; i++)
|
||||||
|
{
|
||||||
|
// forward scan
|
||||||
|
}
|
||||||
|
for (int i = 0; i < collectScanWindow; i++)
|
||||||
|
{
|
||||||
|
// backward scan
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryAlloc(
|
||||||
|
nuint size,
|
||||||
|
out Handle h
|
||||||
|
)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
protected virtual void Dispose(
|
||||||
|
bool disposing
|
||||||
|
)
|
||||||
{
|
{
|
||||||
if (!disposedValue)
|
if (!disposedValue)
|
||||||
{
|
{
|
||||||
@@ -153,7 +225,18 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TryGetNewMetadata(ulong size, out Metadata* meta)
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
private static int GetLevel(
|
||||||
|
ulong size
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return 57 - BitOperations.LeadingZeroCount(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryAllocNewChunk(
|
||||||
|
ulong size,
|
||||||
|
out Metadata* meta
|
||||||
|
)
|
||||||
{
|
{
|
||||||
int idx = _spareMemory->NextLevel;
|
int idx = _spareMemory->NextLevel;
|
||||||
if (idx != 0)
|
if (idx != 0)
|
||||||
@@ -163,29 +246,57 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
ulong spareSize = _spareMemory->Size;
|
ulong spareSize = _spareMemory->Size;
|
||||||
if (spareSize < 32UL + size)
|
if (spareSize < Metadata.SizeOf + size)
|
||||||
{
|
{
|
||||||
meta = null;
|
meta = null;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
_allocEnd++;
|
_metadataEnd++;
|
||||||
meta = &_spareMemory[-_allocEnd];
|
meta = &_spareMemory[-_metadataEnd];
|
||||||
spareSize -= 32UL - size;
|
spareSize -= Metadata.SizeOf + size;
|
||||||
_spareMemory->Size = spareSize;
|
_spareMemory->Size = spareSize;
|
||||||
_spareMemory->Position += size;
|
_spareMemory->Position += size;
|
||||||
meta->Size = size;
|
meta->Size = size;
|
||||||
|
|
||||||
|
// insert the new chunk to position chain
|
||||||
|
int nearIdx = _spareMemory->PreviousNear;
|
||||||
|
Metadata* near = &_spareMemory[-nearIdx];
|
||||||
|
near->NextNear = _metadataEnd;
|
||||||
|
meta->PreviousNear = nearIdx;
|
||||||
|
_spareMemory->PreviousNear = _metadataEnd;
|
||||||
|
meta->NextNear = 0;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Sequential)]
|
private bool TryGetNewMetadata(
|
||||||
|
out Metadata* meta
|
||||||
|
)
|
||||||
|
{
|
||||||
|
ulong spareSize = _spareMemory->Size;
|
||||||
|
if (spareSize < Metadata.SizeOf)
|
||||||
|
{
|
||||||
|
meta = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_metadataEnd++;
|
||||||
|
meta = &_spareMemory[-_metadataEnd];
|
||||||
|
spareSize -= Metadata.SizeOf;
|
||||||
|
_spareMemory->Size = spareSize;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Explicit)]
|
||||||
public readonly struct Handle
|
public readonly struct Handle
|
||||||
{
|
{
|
||||||
|
|
||||||
internal readonly Metadata* _meta;
|
[FieldOffset(0)]internal readonly Metadata* _meta;
|
||||||
public readonly void* Pointer;
|
[FieldOffset(8)]public readonly void* Pointer;
|
||||||
public readonly Ptr64 Size;
|
[FieldOffset(16)]public readonly Ptr64 Size;
|
||||||
|
|
||||||
internal Handle(Metadata* meta, Ptr64 baseAddress)
|
internal Handle(
|
||||||
|
Metadata* meta,
|
||||||
|
Ptr64 baseAddress
|
||||||
|
)
|
||||||
{
|
{
|
||||||
_meta = meta;
|
_meta = meta;
|
||||||
Pointer = (void*)(ulong)(meta->Position + baseAddress);
|
Pointer = (void*)(ulong)(meta->Position + baseAddress);
|
||||||
@@ -195,7 +306,26 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
[Obsolete("You should not create instance from default. Alloc from allocator please")]
|
[Obsolete("You should not create instance from default. Alloc from allocator please")]
|
||||||
public Handle() { }
|
public Handle() { }
|
||||||
|
|
||||||
public ref byte this[int offset] => ref ((byte*)Pointer)[offset];
|
public ref byte this[
|
||||||
|
int offset
|
||||||
|
] => ref ((byte*)Pointer)[offset];
|
||||||
|
|
||||||
|
public Span<byte> AsSpan(
|
||||||
|
int range = -1
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return new Span<byte>(Pointer, range < 0 ? (int)(ulong)Size : range);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
internal enum AllocationState : short
|
||||||
|
{
|
||||||
|
|
||||||
|
Free = 0,
|
||||||
|
Used = 1,
|
||||||
|
Split = 2,
|
||||||
|
Merged = 3
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -206,18 +336,17 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
[FieldOffset(24)] private ulong PositionMask;
|
[FieldOffset(24)] private ulong PositionMask;
|
||||||
[FieldOffset(16)] private ulong SizeMask;
|
[FieldOffset(16)] private ulong SizeMask;
|
||||||
|
|
||||||
|
[FieldOffset(30)] internal AllocationState State;
|
||||||
|
|
||||||
[FieldOffset(0)] public int NextLevel;
|
[FieldOffset(0)] public int NextLevel;
|
||||||
[FieldOffset(4)] public int NextNear;
|
[FieldOffset(4)] public int NextNear;
|
||||||
[FieldOffset(8)] public int PreviousLevel;
|
[FieldOffset(8)] public int PreviousLevel;
|
||||||
[FieldOffset(12)] public int PreviousNear;
|
[FieldOffset(12)] public int PreviousNear;
|
||||||
|
|
||||||
[FieldOffset(22)] public ushort Mask1;
|
|
||||||
[FieldOffset(30)] public ushort Mask2;
|
|
||||||
|
|
||||||
public Ptr64 Position
|
public Ptr64 Position
|
||||||
{
|
{
|
||||||
readonly get => (Ptr64)(PositionMask & Mask48Bits);
|
readonly get => PositionMask;
|
||||||
set => PositionMask = (PositionMask & ~Mask48Bits) | ((ulong)value & Mask48Bits);
|
set => PositionMask = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Ptr64 Size
|
public Ptr64 Size
|
||||||
@@ -228,7 +357,7 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
|
|
||||||
public Ptr64 SizeForSpare
|
public Ptr64 SizeForSpare
|
||||||
{
|
{
|
||||||
get => SizeMask;
|
readonly get => SizeMask;
|
||||||
set => SizeMask = value;
|
set => SizeMask = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,239 +0,0 @@
|
|||||||
//-----------------------------------------------------------------------
|
|
||||||
// -----------------------------------------------------------------------
|
|
||||||
// file="HLSFAllocator.Alloc"
|
|
||||||
// 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 System;
|
|
||||||
using System.IO;
|
|
||||||
using System.Numerics;
|
|
||||||
using System.Reflection.Metadata;
|
|
||||||
using hlsfHandle = StgSharp.HighPerformance.Memory.HlsfHandle;
|
|
||||||
|
|
||||||
namespace StgSharp.HighPerformance.Memory
|
|
||||||
{
|
|
||||||
public unsafe partial class HybridLayerSegregatedFitAllocator
|
|
||||||
{
|
|
||||||
|
|
||||||
public partial HlsfHandle Alloc(
|
|
||||||
nuint size
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (size > _maxAllocSize) {
|
|
||||||
throw new InvalidOperationException("Requested size exceeds maximum allocatable size.");
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
int level = GetLevelFromSize(size);
|
|
||||||
int segmentIndex = GetSegmentFromLevel(size, level);
|
|
||||||
|
|
||||||
int i = level, s = segmentIndex;
|
|
||||||
/**/
|
|
||||||
|
|
||||||
Entry* entry = null;
|
|
||||||
nuint requestedOffset = GetAllocSize(size);
|
|
||||||
(int level, int segment) = GetIndexPairOnce(size);
|
|
||||||
int index = int.Min((3 * level) + segment, _bucketHeads.Length);
|
|
||||||
int i = index;
|
|
||||||
while ((i < _bucketHeads.Length) && !TryPopLevelForAllocation(i, out entry)) {
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
if ((i >= _bucketHeads.Length) && !TryAllocateNewBlock(requestedOffset, out entry)) {
|
|
||||||
throw new OverflowException($"Out of memory: unable to satisfy request of {size} B (aligned {requestedOffset} B) from existing buckets or spare memory.");
|
|
||||||
}
|
|
||||||
if (entry != null)
|
|
||||||
{
|
|
||||||
if (requestedOffset > entry->Size) {
|
|
||||||
throw new OverflowException("Out of memory: insufficient block size returned from spare memory.");
|
|
||||||
}
|
|
||||||
nuint remainder = entry->Size - requestedOffset;
|
|
||||||
SetEntryLevel(entry, entry->Position, (uint)requestedOffset);
|
|
||||||
entry->State = EntryState.Alloc;
|
|
||||||
if (remainder > 0) {
|
|
||||||
OrganizeMemory(entry->NextNear, remainder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new(entry, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool TryAlloc(
|
|
||||||
nuint size,
|
|
||||||
out HlsfHandle handle
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (size > _maxAllocSize)
|
|
||||||
{
|
|
||||||
handle = default;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
int level = GetLevelFromSize(size);
|
|
||||||
int segmentIndex = GetSegmentFromLevel(size, level);
|
|
||||||
|
|
||||||
int i = level, s = segmentIndex;
|
|
||||||
/**/
|
|
||||||
|
|
||||||
Entry* entry = null;
|
|
||||||
nuint requestedOffset = GetAllocSize(size);
|
|
||||||
(int level, int segment) = GetIndexPairOnce(size);
|
|
||||||
int index = int.Min((3 * level) + segment, _bucketHeads.Length);
|
|
||||||
int i = index;
|
|
||||||
while ((i < _bucketHeads.Length) && !TryPopLevelForAllocation(i, out entry)) {
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
if ((i >= _bucketHeads.Length) && !TryAllocateNewBlock(requestedOffset, out entry)) {
|
|
||||||
throw new OverflowException($"Out of memory: unable to satisfy request of {size} B (aligned {requestedOffset} B) from existing buckets or spare memory.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entry != null)
|
|
||||||
{
|
|
||||||
if (requestedOffset > entry->Size) {
|
|
||||||
throw new OverflowException("Out of memory: insufficient block size returned from spare memory.");
|
|
||||||
}
|
|
||||||
nuint remainder = entry->Size - requestedOffset;
|
|
||||||
SetEntryLevel(entry, entry->Position, (uint)requestedOffset);
|
|
||||||
entry->State = EntryState.Alloc;
|
|
||||||
if (remainder > 0) {
|
|
||||||
OrganizeMemory(entry->NextNear, remainder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
handle = new(entry, size);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <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">
|
|
||||||
/// The next entry nearby the part to slice
|
|
||||||
/// </param>
|
|
||||||
/// <param name="sizeToSlice">
|
|
||||||
///
|
|
||||||
/// </param>
|
|
||||||
private void OrganizeMemory(
|
|
||||||
Entry* e,
|
|
||||||
nuint sizeToSlice
|
|
||||||
)
|
|
||||||
{
|
|
||||||
Entry* next = e;
|
|
||||||
nuint remain = sizeToSlice; // remained size to slice
|
|
||||||
nuint offset = e->Position - sizeToSlice - ((nuint)m_Buffer); // offset of buffer to slice
|
|
||||||
nuint position = e->Position - sizeToSlice; // position of buffer to slice
|
|
||||||
nuint end = e->Position - ((nuint)m_Buffer); // end offset of buffer to slice
|
|
||||||
|
|
||||||
int i = GetBucketIndexOnce(sizeToSlice);
|
|
||||||
|
|
||||||
Entry* entry = (Entry*)_entries.Allocate();
|
|
||||||
SetEntryLevel(entry, position, remain);
|
|
||||||
entry->State = EntryState.Empty;
|
|
||||||
BucketNode* bucketNode = (BucketNode*)entry->Position;
|
|
||||||
bucketNode->EntryRef = entry;
|
|
||||||
PushNodeToBucket(i, bucketNode);
|
|
||||||
|
|
||||||
Entry* prev = next->PreviousNear;
|
|
||||||
prev->NextNear = entry;
|
|
||||||
entry->PreviousNear = prev;
|
|
||||||
entry->NextNear = next;
|
|
||||||
next->PreviousNear = entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Try to allocate a new 16MB block when all buckets are empty
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="size">
|
|
||||||
/// Size of memory block to be allocated. Notice: Size is assumed as times of 64.
|
|
||||||
/// </param>
|
|
||||||
/// <param name="handle">
|
|
||||||
/// Output handle to the allocated Entry
|
|
||||||
/// </param>
|
|
||||||
/// <returns>
|
|
||||||
/// True if allocation succeeded
|
|
||||||
/// </returns>
|
|
||||||
private bool TryAllocateNewBlock(
|
|
||||||
nuint size,
|
|
||||||
out Entry* handle
|
|
||||||
)
|
|
||||||
{
|
|
||||||
nuint pos = _spareMemory->Position;
|
|
||||||
nuint remain = _spareMemory->Size;
|
|
||||||
if (remain <= 0)
|
|
||||||
{
|
|
||||||
handle = null;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
size = nuint.Min(size, remain);
|
|
||||||
|
|
||||||
|
|
||||||
// build new entry
|
|
||||||
Entry* e = (Entry*)_entries.Allocate();
|
|
||||||
SetEntryLevel(e, pos, size);
|
|
||||||
InsertEntryBefore(e, _spareMemory);
|
|
||||||
e->State = EntryState.Empty;
|
|
||||||
|
|
||||||
// set fields of spare memory
|
|
||||||
_spareMemory->Size -= size;
|
|
||||||
_spareMemory->Position += size;
|
|
||||||
|
|
||||||
|
|
||||||
handle = e;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Helper method for allocation - converts BucketNode to Entry
|
|
||||||
/// </summary>
|
|
||||||
private bool TryPopLevelForAllocation(
|
|
||||||
int bucketIndex,
|
|
||||||
out Entry* entry
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (TryPopBucket(bucketIndex, out BucketNode* bucketNode))
|
|
||||||
{
|
|
||||||
entry = bucketNode->EntryRef;
|
|
||||||
return entry != null;
|
|
||||||
}
|
|
||||||
entry = null;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Helper method for allocation - converts BucketNode to Entry
|
|
||||||
/// </summary>
|
|
||||||
private bool TryPopLevelForAllocation(
|
|
||||||
int levelIndex,
|
|
||||||
int sizeIndex,
|
|
||||||
out Entry* entry
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (TryPopLevel(levelIndex, sizeIndex, out BucketNode* bucketNode))
|
|
||||||
{
|
|
||||||
entry = bucketNode->EntryRef;
|
|
||||||
return entry != null;
|
|
||||||
}
|
|
||||||
entry = null;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,214 +0,0 @@
|
|||||||
//-----------------------------------------------------------------------
|
|
||||||
// -----------------------------------------------------------------------
|
|
||||||
// file="HLSFAllocator.BucketNode"
|
|
||||||
// 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 System;
|
|
||||||
using System.Reflection.Metadata;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace StgSharp.HighPerformance.Memory
|
|
||||||
{
|
|
||||||
public unsafe partial class HybridLayerSegregatedFitAllocator
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* All operations with style of (level, segment)
|
|
||||||
* DO NOT assume that the level are valid.
|
|
||||||
* They are just used to calculate the bucket index,
|
|
||||||
* which may exceed the range of _bucketHeads.
|
|
||||||
*/
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private void PushBucketToLevel(
|
|
||||||
int levelIndex,
|
|
||||||
int sizeIndex,
|
|
||||||
BucketNode* node
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if ((node == null) || (node->EntryRef == null)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int index = Math.Min((3 * levelIndex) + sizeIndex, _bucketHeads.Length - 1);
|
|
||||||
PushNodeToBucket(index, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void PushNodeToBucket(
|
|
||||||
int index,
|
|
||||||
BucketNode* node
|
|
||||||
)
|
|
||||||
{
|
|
||||||
index = Math.Min(index, _bucketHeads.Length - 1);
|
|
||||||
if (_bucketHeads[index] is null)
|
|
||||||
{
|
|
||||||
_bucketHeads[index] = node;
|
|
||||||
node->IsInBucket = true;
|
|
||||||
node->NextLevel = null;
|
|
||||||
node->PreviousLevel = null;
|
|
||||||
|
|
||||||
// Console.WriteLine($"Push to empty level of {levelIndex} {sizeIndex}, bucket pos {(ulong)node}");
|
|
||||||
return;
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
BucketNode* head = _bucketHeads[index];
|
|
||||||
node->NextLevel = 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}");
|
|
||||||
|
|
||||||
head->PreviousLevel = node;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private void RemoveFromBucket(
|
|
||||||
int levelIndex,
|
|
||||||
int sizeIndex,
|
|
||||||
BucketNode* node
|
|
||||||
)
|
|
||||||
{
|
|
||||||
int index = Math.Min((3 * levelIndex) + sizeIndex, _bucketHeads.Length - 1);
|
|
||||||
if (_bucketHeads[index] == node)
|
|
||||||
{
|
|
||||||
if (TryPopBucket(index, out _)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool TryPopBucket(
|
|
||||||
int index,
|
|
||||||
out BucketNode* node
|
|
||||||
)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Attention:
|
|
||||||
* This method is UNSAFE. Index may exceed range of _bucketHeads.
|
|
||||||
* Be careful when calling this method.
|
|
||||||
*/
|
|
||||||
BucketNode* head = _bucketHeads[index];
|
|
||||||
|
|
||||||
if (head == null)
|
|
||||||
{
|
|
||||||
node = null;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Console.WriteLine($"level popped from level {levelIndex} {sizeIndex}, node is at {(ulong)head}");
|
|
||||||
BucketNode* next = head->NextLevel;
|
|
||||||
|
|
||||||
if (next == null)
|
|
||||||
{
|
|
||||||
_bucketHeads[index] = null;
|
|
||||||
node = head;
|
|
||||||
return true;
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
next->PreviousLevel = null;
|
|
||||||
}
|
|
||||||
_bucketHeads[index] = head->NextLevel;
|
|
||||||
node = head;
|
|
||||||
node->IsInBucket = false;
|
|
||||||
node->NextLevel = null;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private bool TryPopLevel(
|
|
||||||
int levelIndex,
|
|
||||||
int sizeIndex,
|
|
||||||
out BucketNode* node
|
|
||||||
)
|
|
||||||
{
|
|
||||||
int index = int.Min((3 * levelIndex) + sizeIndex, _bucketHeads.Length - 1);
|
|
||||||
return TryPopBucket(index, out node);
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool TryRemoveFromBucket(
|
|
||||||
int levelIndex,
|
|
||||||
int sizeIndex,
|
|
||||||
BucketNode* node
|
|
||||||
)
|
|
||||||
{
|
|
||||||
int index = (3 * levelIndex) + sizeIndex;
|
|
||||||
index = (index >= _bucketHeads.Length) ? _bucketHeads.Length : index;
|
|
||||||
if (_bucketHeads[index] == node)
|
|
||||||
{
|
|
||||||
bool ok = TryPopLevel(levelIndex, sizeIndex, out _);
|
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
BucketNode* previous = node->PreviousLevel;
|
|
||||||
BucketNode* next = node->NextLevel;
|
|
||||||
|
|
||||||
if (previous != null) {
|
|
||||||
previous->NextLevel = next;
|
|
||||||
}
|
|
||||||
if (next != null) {
|
|
||||||
next->PreviousLevel = previous;
|
|
||||||
}
|
|
||||||
|
|
||||||
node->NextLevel = null;
|
|
||||||
node->PreviousLevel = null;
|
|
||||||
node->IsInBucket = false;
|
|
||||||
node->EntryRef = null;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// BucketNode structure - specialized node for bucket linked lists Stored in main
|
|
||||||
/// allocation area (user-writable region)
|
|
||||||
/// </summary>
|
|
||||||
[StructLayout(LayoutKind.Explicit)]
|
|
||||||
internal struct BucketNode
|
|
||||||
{
|
|
||||||
|
|
||||||
[FieldOffset(0)] internal Entry* EntryRef; // Pointer to corresponding Entry
|
|
||||||
// [FieldOffset(16)] internal ulong LockBuffer;
|
|
||||||
[FieldOffset(8)] internal BucketNode* NextLevel; // Bucket stack linked list: next pointer
|
|
||||||
[FieldOffset(24)] internal BucketNode* PreviousLevel; // Bucket stack linked list: previous pointer
|
|
||||||
[FieldOffset(32)] internal bool IsInBucket; // Whether in bucket
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,130 +0,0 @@
|
|||||||
//-----------------------------------------------------------------------
|
|
||||||
// -----------------------------------------------------------------------
|
|
||||||
// file="HLSFAllocator.Debug"
|
|
||||||
// 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 System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
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
|
|
||||||
{
|
|
||||||
|
|
||||||
private object _lock = new object();
|
|
||||||
|
|
||||||
public void DisplayEntryLayout(
|
|
||||||
bool singleStep
|
|
||||||
)
|
|
||||||
{
|
|
||||||
lock (_lock)
|
|
||||||
{
|
|
||||||
Entry* cur = _spareMemory;
|
|
||||||
while (cur != null)
|
|
||||||
{
|
|
||||||
Console.WriteLine(EntryToString(cur));
|
|
||||||
cur = cur->PreviousNear;
|
|
||||||
if (singleStep) {
|
|
||||||
Console.ReadKey();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void DumpPositionChain(
|
|
||||||
bool singleStep = false,
|
|
||||||
int maxCount = 4096
|
|
||||||
)
|
|
||||||
{
|
|
||||||
lock (_lock)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Entry* cur = _spareMemory;
|
|
||||||
int cnt = 0;
|
|
||||||
if (cur != null) {
|
|
||||||
cur = cur->NextNear;
|
|
||||||
}
|
|
||||||
Console.WriteLine(EntryToString(_spareMemory));
|
|
||||||
while ((cur != null) && (cur != _spareMemory) && (cnt < maxCount))
|
|
||||||
{
|
|
||||||
Console.WriteLine(EntryToString(cur));
|
|
||||||
cnt++;
|
|
||||||
if (singleStep) {
|
|
||||||
Console.ReadKey();
|
|
||||||
}
|
|
||||||
|
|
||||||
cur = cur->NextNear;
|
|
||||||
}
|
|
||||||
if (cur == _spareMemory)
|
|
||||||
{
|
|
||||||
Console.WriteLine("(reached sentinel)");
|
|
||||||
} else if (cnt >= maxCount) {
|
|
||||||
Console.WriteLine("(stopped at maxCount)");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Console.WriteLine($"[DumpPositionChain-forward] Exception: {ex.Message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<string> ShowEntryLayout()
|
|
||||||
{
|
|
||||||
lock (_lock)
|
|
||||||
{
|
|
||||||
Stack<string> layout = [];
|
|
||||||
Entry* cur = _spareMemory;
|
|
||||||
while (cur != null)
|
|
||||||
{
|
|
||||||
layout.Push(EntryToString(cur));
|
|
||||||
cur = cur->PreviousNear;
|
|
||||||
}
|
|
||||||
return layout;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string EntryToString(
|
|
||||||
Entry* e
|
|
||||||
)
|
|
||||||
{
|
|
||||||
BucketNode* b = (BucketNode*)e->Position;
|
|
||||||
return $"{(ulong)e->PreviousNear}<-prev- Position:{(ulong)e} Bucket:{{{(ulong)e->Position},{e->Level},{e->Size}}} -next->{(ulong)e->NextNear}";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,166 +0,0 @@
|
|||||||
//-----------------------------------------------------------------------
|
|
||||||
// -----------------------------------------------------------------------
|
|
||||||
// file="HLSFAllocator.Free"
|
|
||||||
// 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 System;
|
|
||||||
using System.Buffers;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Drawing;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection.Emit;
|
|
||||||
using System.Reflection.Metadata;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
using hlsfHandle = StgSharp.HighPerformance.Memory.HlsfHandle;
|
|
||||||
|
|
||||||
namespace StgSharp.HighPerformance.Memory
|
|
||||||
{
|
|
||||||
public enum FreePolicy
|
|
||||||
{
|
|
||||||
|
|
||||||
FullCollect,
|
|
||||||
LazyCollect,
|
|
||||||
NoCollect
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
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 partial void Collect()
|
|
||||||
{
|
|
||||||
Entry* cur = _spareMemory->NextNear;
|
|
||||||
nuint size = 0;
|
|
||||||
while ((cur != null) && (cur != _spareMemory))
|
|
||||||
{
|
|
||||||
// cur is not empty, skip
|
|
||||||
if (cur->State != EntryState.Empty)
|
|
||||||
{
|
|
||||||
cur = cur->NextNear;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove cur from bucket
|
|
||||||
do
|
|
||||||
{
|
|
||||||
size += cur->Size;
|
|
||||||
Entry* next = cur->NextNear;
|
|
||||||
RemoveFromBucket(cur->Level, GetSegmentFromLevel(cur->Size, cur->Level), (BucketNode*)cur->Position);
|
|
||||||
RemoveFromPositionChain(cur);
|
|
||||||
cur = next;
|
|
||||||
} while ((cur != null) && (cur != _spareMemory) && (cur->State == EntryState.Empty));
|
|
||||||
if (size > 0)
|
|
||||||
{
|
|
||||||
OrganizeMemory(cur, size);
|
|
||||||
size = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Free allocated memory block and perform adjacent block merging
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="handle">
|
|
||||||
/// Handle to the allocated memory block
|
|
||||||
/// <param name="collectPolicy">
|
|
||||||
/// </param>
|
|
||||||
public void Free(
|
|
||||||
HlsfHandle handle,
|
|
||||||
FreePolicy collectPolicy = FreePolicy.NoCollect
|
|
||||||
)
|
|
||||||
{
|
|
||||||
Entry* e = handle.EntryHandle;
|
|
||||||
if (e == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (e->State != EntryState.Alloc) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mark as empty so it can be merged
|
|
||||||
e->State = EntryState.Empty;
|
|
||||||
|
|
||||||
// Try merge with adjacent free blocks and remove them from buckets
|
|
||||||
MergeAndRemoveFromBuckets(e);
|
|
||||||
|
|
||||||
// Calculate final level and segment for the merged block
|
|
||||||
nuint finalSize = e->Size;
|
|
||||||
(int finalLevel, int finalSegment) = GetIndexPairOnce(finalSize);
|
|
||||||
e->Level = finalLevel;
|
|
||||||
|
|
||||||
// Push the merged entry back to appropriate bucket
|
|
||||||
PushBucketToLevel(finalLevel, finalSegment, (BucketNode*)e->Position);
|
|
||||||
|
|
||||||
// Console.WriteLine("one block freed");
|
|
||||||
if (collectPolicy == FreePolicy.FullCollect)
|
|
||||||
{
|
|
||||||
Collect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void Free(
|
|
||||||
Entry* e,
|
|
||||||
FreePolicy collectPolicy = FreePolicy.NoCollect
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (e == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (e->State != EntryState.Alloc) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mark as empty so it can be merged
|
|
||||||
e->State = EntryState.Empty;
|
|
||||||
|
|
||||||
// Try merge with adjacent free blocks and remove them from buckets
|
|
||||||
/**/
|
|
||||||
MergeAndRemoveFromBuckets(e);
|
|
||||||
|
|
||||||
// Calculate final level and segment for the merged block
|
|
||||||
nuint finalSize = e->Size;
|
|
||||||
int finalLevel = GetLevelFromSize(finalSize);
|
|
||||||
e->Level = finalLevel;
|
|
||||||
int finalSegment = GetSegmentFromLevel(finalSize, finalLevel);
|
|
||||||
|
|
||||||
// Push the merged entry back to appropriate bucket
|
|
||||||
PushBucketToLevel(finalLevel, finalSegment, (BucketNode*)e->Position);
|
|
||||||
|
|
||||||
// Console.WriteLine("one block freed");
|
|
||||||
if (collectPolicy == FreePolicy.FullCollect)
|
|
||||||
{
|
|
||||||
Collect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,154 +0,0 @@
|
|||||||
//-----------------------------------------------------------------------
|
|
||||||
// -----------------------------------------------------------------------
|
|
||||||
// file="HLSFAllocator.Level"
|
|
||||||
// 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 System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Net.Security;
|
|
||||||
using System.Numerics;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace StgSharp.HighPerformance.Memory
|
|
||||||
{
|
|
||||||
public unsafe partial class HybridLayerSegregatedFitAllocator
|
|
||||||
{
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private nuint GetAllocSize(
|
|
||||||
nuint size
|
|
||||||
)
|
|
||||||
|
|
||||||
{
|
|
||||||
return (size + 63u) & (~63u);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private int GetBucketIndexOnce(
|
|
||||||
nuint size
|
|
||||||
)
|
|
||||||
{
|
|
||||||
int log2Size = 63 - BitOperations.LeadingZeroCount((ulong)size);
|
|
||||||
|
|
||||||
// Calculate level: level = max(0, (log2Size - 6) / 2)
|
|
||||||
int level = Math.Max(0, (log2Size - 6) / 2);
|
|
||||||
|
|
||||||
// caller is responsible for ensuring level is within bounds
|
|
||||||
level = Math.Min(level, _maxLevel);
|
|
||||||
uint levelSize = 1u << (log2Size & (~1));
|
|
||||||
int segment = ((int)(size / levelSize)) - 1;
|
|
||||||
return Math.Min((level * 3) + segment, _bucketHeads.Length - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private (int level, int segment) GetIndexPairOnce(
|
|
||||||
nuint size
|
|
||||||
)
|
|
||||||
{
|
|
||||||
int log2Size = 63 - BitOperations.LeadingZeroCount((ulong)size);
|
|
||||||
|
|
||||||
// Calculate level: level = max(0, (log2Size - 6) / 2)
|
|
||||||
int level = Math.Max(0, (log2Size - 6) / 2);
|
|
||||||
|
|
||||||
// caller is responsible for ensuring level is within bounds
|
|
||||||
level = Math.Min(level, _maxLevel);
|
|
||||||
uint levelSize = 1u << (log2Size & (~1));
|
|
||||||
int segment = ((int)(size / levelSize)) - 1;
|
|
||||||
return (level, segment);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// NOTICE: returned level may exceed maxLevel, caller is responsible for ensuring the
|
|
||||||
/// returned level is within bounds. This method is designed for performance and does not
|
|
||||||
/// perform bounds checking.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="size">
|
|
||||||
/// Size of the memory block to determine level for. This method does not assume size is
|
|
||||||
/// already aligned
|
|
||||||
/// </param>
|
|
||||||
/// <returns>
|
|
||||||
/// Level corresponding to the given size. Level is determined based on the formula: level
|
|
||||||
/// >= (log2(S) - 6) / 2
|
|
||||||
/// </returns>
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private int GetLevelFromSize(
|
|
||||||
nuint size
|
|
||||||
)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* 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)
|
|
||||||
*
|
|
||||||
* For size S, we need: 2^(6+2*level) >= S
|
|
||||||
* So: 6+2*level >= log2(S)
|
|
||||||
* Therefore: level >= (log2(S) - 6) / 2
|
|
||||||
*
|
|
||||||
* We use hardware LZCNT instruction via BitOperations.LeadingZeroCount
|
|
||||||
* log2(S) = 63 - LeadingZeroCount(S) for 64-bit values
|
|
||||||
*/
|
|
||||||
int log2Size = 63 - BitOperations.LeadingZeroCount((ulong)size);
|
|
||||||
|
|
||||||
// Calculate level: level = max(0, (log2Size - 6) / 2)
|
|
||||||
int level = Math.Max(0, (log2Size - 6) / 2);
|
|
||||||
|
|
||||||
// caller is responsible for ensuring level is within bounds
|
|
||||||
return level;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private static uint GetLevelSizeFromLevel(
|
|
||||||
int level
|
|
||||||
)
|
|
||||||
{
|
|
||||||
return 1u << (6 + (2 * level));
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private static uint GetLevelSizeFromSize(
|
|
||||||
nuint size
|
|
||||||
)
|
|
||||||
{
|
|
||||||
int log2Size = 63 - BitOperations.LeadingZeroCount((ulong)size);
|
|
||||||
return 1u << (log2Size & (~1));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Determine segment index based on size and originLevel. Input level value is regarded
|
|
||||||
/// safe.
|
|
||||||
/// </summary>
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private int GetSegmentFromLevel(
|
|
||||||
ulong size,
|
|
||||||
int level
|
|
||||||
)
|
|
||||||
{
|
|
||||||
return ((int)(size / GetLevelSizeFromLevel(level))) - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,219 +0,0 @@
|
|||||||
//-----------------------------------------------------------------------
|
|
||||||
// -----------------------------------------------------------------------
|
|
||||||
// file="HLSFAllocator.SegmentEntry"
|
|
||||||
// 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 System;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace StgSharp.HighPerformance.Memory
|
|
||||||
{
|
|
||||||
public unsafe partial class HybridLayerSegregatedFitAllocator
|
|
||||||
{
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Check if two Entries are adjacent in memory
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>
|
|
||||||
/// True if two mem blocks are adjacent.
|
|
||||||
/// </returns>
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private static bool AssertAdjacentTo(
|
|
||||||
Entry* front,
|
|
||||||
Entry* back
|
|
||||||
)
|
|
||||||
{
|
|
||||||
// Only a predicate; never throw here. Callers rely on false to skip merging.
|
|
||||||
return front->Position + front->Size == back->Position;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private static void InsertEntryAfter(
|
|
||||||
Entry* entry,
|
|
||||||
Entry* position
|
|
||||||
)
|
|
||||||
{
|
|
||||||
Entry* next = position->NextNear;
|
|
||||||
entry->NextNear = next;
|
|
||||||
entry->PreviousNear = position;
|
|
||||||
next->PreviousNear = entry;
|
|
||||||
position->NextNear = entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private static void InsertEntryBefore(
|
|
||||||
Entry* entry,
|
|
||||||
Entry* position
|
|
||||||
)
|
|
||||||
{
|
|
||||||
Entry* prev = position->PreviousNear;
|
|
||||||
entry->NextNear = position;
|
|
||||||
entry->PreviousNear = prev;
|
|
||||||
prev->NextNear = entry;
|
|
||||||
position->PreviousNear = entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Used in Free operations, efficiently remove from buckets and merge adjacent blocks.
|
|
||||||
/// Current Entry will serve as the final merged block. The Merge will be in lazy
|
|
||||||
/// strategy.
|
|
||||||
/// </summary>
|
|
||||||
private void MergeAndRemoveFromBuckets(
|
|
||||||
Entry* entry,
|
|
||||||
int maxScanRange = 4
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (entry == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
nuint size = entry->Size;
|
|
||||||
int originLevel = entry->Level;
|
|
||||||
int segment = GetSegmentFromLevel(size, originLevel);
|
|
||||||
|
|
||||||
for (int i = 0; i < maxScanRange; i++)
|
|
||||||
{
|
|
||||||
// previous direction merge
|
|
||||||
Entry* p = entry->PreviousNear;
|
|
||||||
if ((p == null) ||
|
|
||||||
(p == _spareMemory) ||
|
|
||||||
(p->State != EntryState.Empty) ||
|
|
||||||
(p->Level != entry->Level) ||
|
|
||||||
!AssertAdjacentTo(p, entry))
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Console.WriteLine($"{(ulong)p->Size} merged to {(ulong)entry->Size} previous");
|
|
||||||
entry->Size += p->Size;
|
|
||||||
entry->Position = p->Position;
|
|
||||||
RemoveFromBucket(originLevel, GetSegmentFromLevel(p->Size, originLevel), (BucketNode*)p->Position);
|
|
||||||
RemoveFromPositionChain(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < maxScanRange; i++)
|
|
||||||
{
|
|
||||||
// next direction merge
|
|
||||||
Entry* n = entry->NextNear;
|
|
||||||
if ((n == null) ||
|
|
||||||
(n == _spareMemory) ||
|
|
||||||
(n->State != EntryState.Empty) ||
|
|
||||||
(n->Level != entry->Level) ||
|
|
||||||
!AssertAdjacentTo(entry, n))
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Console.WriteLine($"{(ulong)n->Size} merged to {(ulong)entry->Size} next");
|
|
||||||
entry->Size += n->Size;
|
|
||||||
RemoveFromBucket(originLevel, GetSegmentFromLevel(n->Size, originLevel), (BucketNode*)n->Position);
|
|
||||||
RemoveFromPositionChain(n);
|
|
||||||
}
|
|
||||||
|
|
||||||
// originLevel = int.Min(9, originLevel);
|
|
||||||
entry->Level = GetLevelFromSize(entry->Size);
|
|
||||||
|
|
||||||
BucketNode* bucket = (BucketNode*)entry->Position;
|
|
||||||
bucket->EntryRef = entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Remove node from position linked list, this method does not recycle a BucketNode from
|
|
||||||
/// size bucket
|
|
||||||
/// </summary>
|
|
||||||
private void RemoveFromPositionChain(
|
|
||||||
Entry* entry
|
|
||||||
)
|
|
||||||
{
|
|
||||||
Entry* prev = entry->PreviousNear;
|
|
||||||
Entry* next = entry->NextNear;
|
|
||||||
|
|
||||||
prev->NextNear = next;
|
|
||||||
next->PreviousNear = prev;
|
|
||||||
|
|
||||||
// Clear node pointers
|
|
||||||
entry->NextNear = null;
|
|
||||||
entry->PreviousNear = null;
|
|
||||||
|
|
||||||
// Console.WriteLine("entry recycled.");
|
|
||||||
_entries.Free((nuint)entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
private void SetEntryLevel(
|
|
||||||
Entry* entry,
|
|
||||||
nuint position,
|
|
||||||
nuint size
|
|
||||||
)
|
|
||||||
{
|
|
||||||
entry ->Position = position;
|
|
||||||
entry->Size = size;
|
|
||||||
entry->Level = GetLevelFromSize(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Entry structure - streamlined version (only position and state information) Stored in
|
|
||||||
/// external SLAB
|
|
||||||
/// </summary>
|
|
||||||
[StructLayout(LayoutKind.Explicit, Size = 40)]
|
|
||||||
internal struct Entry
|
|
||||||
{
|
|
||||||
|
|
||||||
[FieldOffset(0)] internal Entry* NextNear; // Position linked list: next pointer
|
|
||||||
[FieldOffset(8)] internal Entry* PreviousNear; // Position linked list: previous pointer
|
|
||||||
|
|
||||||
[FieldOffset(32)] internal int Level; // Level of the block in the allocator
|
|
||||||
[FieldOffset(16)] internal nuint Size; // Block size information
|
|
||||||
[FieldOffset(36)] public EntryState State;
|
|
||||||
[FieldOffset(24)] public nuint Position; // Memory block position
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
[StructLayout(LayoutKind.Explicit, Size = 32)]
|
|
||||||
internal struct Metadata
|
|
||||||
{
|
|
||||||
|
|
||||||
[FieldOffset(0)] internal int NextLevel;
|
|
||||||
[FieldOffset(4)] internal int NextNear;
|
|
||||||
[FieldOffset(8)] internal int PreviousLevel;
|
|
||||||
[FieldOffset(12)] internal int PreviousNear;
|
|
||||||
[FieldOffset(16)] internal nuint Size;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
internal enum EntryState : uint
|
|
||||||
{
|
|
||||||
|
|
||||||
Spare,
|
|
||||||
Empty,
|
|
||||||
Alloc,
|
|
||||||
Large
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,163 +0,0 @@
|
|||||||
//-----------------------------------------------------------------------
|
|
||||||
// -----------------------------------------------------------------------
|
|
||||||
// file="HLSFAllocator"
|
|
||||||
// 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 System;
|
|
||||||
using System.Buffers;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace StgSharp.HighPerformance.Memory
|
|
||||||
{
|
|
||||||
public unsafe partial class HybridLayerSegregatedFitAllocator : IDisposable
|
|
||||||
{
|
|
||||||
|
|
||||||
// Bucket system field - directly in main allocator class
|
|
||||||
private readonly BucketNode*[] _bucketHeads;
|
|
||||||
private Metadata* _metadata;
|
|
||||||
|
|
||||||
// _spareMemory is also the beginning position of allocator
|
|
||||||
private Entry* _spareMemory;
|
|
||||||
private readonly byte* m_Buffer;
|
|
||||||
|
|
||||||
private readonly Action<nuint> _freeHandle;
|
|
||||||
private bool disposedValue;
|
|
||||||
private readonly int _align;
|
|
||||||
private readonly int _maxAllocCount;
|
|
||||||
private readonly int _maxLevel;
|
|
||||||
private int unusedMetaData;
|
|
||||||
private readonly nuint _maxAllocSize;
|
|
||||||
private readonly nuint _size;
|
|
||||||
private Ptr64 _basePtr;
|
|
||||||
private readonly SlabAllocator<Entry> _entries;
|
|
||||||
|
|
||||||
internal HybridLayerSegregatedFitAllocator(
|
|
||||||
nuint byteSize,
|
|
||||||
int align,
|
|
||||||
nuint maxBlockSize,
|
|
||||||
Func<nuint, int, nuint> allocator,
|
|
||||||
Action<nuint> freeHandle,
|
|
||||||
SlabAllocator<Entry>? entryManager
|
|
||||||
)
|
|
||||||
: this(byteSize, align, maxBlockSize, (void*)allocator(byteSize, align), freeHandle,
|
|
||||||
entryManager) { }
|
|
||||||
|
|
||||||
internal HybridLayerSegregatedFitAllocator(
|
|
||||||
nuint byteSize,
|
|
||||||
int align,
|
|
||||||
nuint maxBlockSize,
|
|
||||||
void* externalPointer,
|
|
||||||
Action<nuint> freeHandle,
|
|
||||||
SlabAllocator<Entry>? entryManager = null
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if ((align < 16) || ((align & (align - 1)) != 0)) {
|
|
||||||
throw new ArgumentException(
|
|
||||||
"Alignment must be a power of two and at least 16 bytes.");
|
|
||||||
}
|
|
||||||
_entries = entryManager ??
|
|
||||||
SlabAllocator.Create<Entry>(64, SlabBufferLayout.Chunked, false);
|
|
||||||
m_Buffer = (byte*)externalPointer;
|
|
||||||
_basePtr = (Ptr64)(nuint)externalPointer;
|
|
||||||
_metadata = (Metadata*)((nuint)externalPointer + byteSize);
|
|
||||||
_align = align;
|
|
||||||
_size = byteSize;
|
|
||||||
|
|
||||||
_spareMemory = (Entry*)_entries.Allocate();
|
|
||||||
_spareMemory->State = EntryState.Empty;
|
|
||||||
_spareMemory->PreviousNear = _spareMemory;
|
|
||||||
_spareMemory->NextNear = _spareMemory;
|
|
||||||
_spareMemory->Position = (nuint)m_Buffer;
|
|
||||||
_spareMemory->Size = byteSize;
|
|
||||||
|
|
||||||
_maxLevel = GetLevelFromSize(maxBlockSize) + 1;
|
|
||||||
_bucketHeads = new BucketNode*[(_maxLevel * 3) + 1];
|
|
||||||
_maxAllocSize = maxBlockSize;
|
|
||||||
_freeHandle = freeHandle ?? (static _ => { });
|
|
||||||
}
|
|
||||||
|
|
||||||
public HybridLayerSegregatedFitAllocator(
|
|
||||||
nuint byteSize
|
|
||||||
)
|
|
||||||
: this(byteSize, 16, 32U * 1024U * 1024U, NativeMemory.AlignedAlloc(byteSize, 16),
|
|
||||||
ptr => NativeMemory.AlignedFree((void*)ptr)) { }
|
|
||||||
|
|
||||||
public HybridLayerSegregatedFitAllocator(
|
|
||||||
nuint byteSize,
|
|
||||||
uint maxBlockSize
|
|
||||||
)
|
|
||||||
: this(byteSize, 16, maxBlockSize, NativeMemory.AlignedAlloc(byteSize, 16),
|
|
||||||
ptr => NativeMemory.AlignedFree((void*)ptr)) { }
|
|
||||||
|
|
||||||
public HybridLayerSegregatedFitAllocator(
|
|
||||||
nuint byteSize,
|
|
||||||
int align
|
|
||||||
)
|
|
||||||
: this(byteSize, align, 32U * 1024U * 1024U, NativeMemory.AlignedAlloc(byteSize, 16),
|
|
||||||
ptr => NativeMemory.AlignedFree((void*)ptr)) { }
|
|
||||||
|
|
||||||
public HybridLayerSegregatedFitAllocator(
|
|
||||||
nuint byteSize,
|
|
||||||
int align,
|
|
||||||
nuint maxBlockSize
|
|
||||||
)
|
|
||||||
: this(byteSize, align, maxBlockSize, NativeMemory.AlignedAlloc(byteSize, 16),
|
|
||||||
ptr => NativeMemory.AlignedFree((void*)ptr)) { }
|
|
||||||
|
|
||||||
public partial HlsfHandle Alloc(
|
|
||||||
nuint size
|
|
||||||
);
|
|
||||||
|
|
||||||
public partial void Collect();
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
Dispose(disposing:true);
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void Dispose(
|
|
||||||
bool disposing
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (!disposedValue)
|
|
||||||
{
|
|
||||||
if (disposing) {
|
|
||||||
_entries.Dispose();
|
|
||||||
}
|
|
||||||
_freeHandle((nuint)m_Buffer);
|
|
||||||
disposedValue = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
~HybridLayerSegregatedFitAllocator()
|
|
||||||
{
|
|
||||||
Dispose(disposing:false);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -34,6 +34,8 @@ using System.Runtime.CompilerServices;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
using Tlsf = global::StgSharp.HighPerformance.Memory.TwoLayerSegregatedFitAllocator;
|
||||||
|
|
||||||
namespace StgSharp.Mathematics.Numeric
|
namespace StgSharp.Mathematics.Numeric
|
||||||
{
|
{
|
||||||
public unsafe partial class Matrix<T>
|
public unsafe partial class Matrix<T>
|
||||||
@@ -45,7 +47,7 @@ namespace StgSharp.Mathematics.Numeric
|
|||||||
int columnLength,
|
int columnLength,
|
||||||
int rowLength,
|
int rowLength,
|
||||||
MatrixLayout layout,
|
MatrixLayout layout,
|
||||||
HybridLayerSegregatedFitAllocator allocator
|
TwoLayerSegregatedFitAllocator allocator
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(allocator);
|
ArgumentNullException.ThrowIfNull(allocator);
|
||||||
@@ -75,7 +77,7 @@ namespace StgSharp.Mathematics.Numeric
|
|||||||
long requiredBytes = kernelCount * MatrixKernel<T>.Size;
|
long requiredBytes = kernelCount * MatrixKernel<T>.Size;
|
||||||
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(requiredBytes, nameof(requiredBytes));
|
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(requiredBytes, nameof(requiredBytes));
|
||||||
|
|
||||||
HlsfHandle handle = allocator.Alloc((uint)requiredBytes);
|
Tlsf.Handle handle = allocator.Alloc((uint)requiredBytes);
|
||||||
MatrixStorageHLSF<T> storage = new MatrixStorageHLSF<T>(allocator, handle);
|
MatrixStorageHLSF<T> storage = new MatrixStorageHLSF<T>(allocator, handle);
|
||||||
|
|
||||||
Matrix<T> matrix = new Matrix<T>(MatrixKernelBoarder.Create(layout), new MatrixSize(columnLength,
|
Matrix<T> matrix = new Matrix<T>(MatrixKernelBoarder.Create(layout), new MatrixSize(columnLength,
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ using System.Runtime.CompilerServices;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
using HLSFAllocator = global::StgSharp.HighPerformance.Memory.HybridLayerSegregatedFitAllocator;
|
using Tlsf = global::StgSharp.HighPerformance.Memory.TwoLayerSegregatedFitAllocator;
|
||||||
|
|
||||||
namespace StgSharp.Mathematics.Numeric
|
namespace StgSharp.Mathematics.Numeric
|
||||||
{
|
{
|
||||||
@@ -53,13 +53,14 @@ namespace StgSharp.Mathematics.Numeric
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal class MatrixStorageHLSF<T>(
|
internal class MatrixStorageHLSF<T>(
|
||||||
HLSFAllocator allocator,
|
Tlsf allocator,
|
||||||
HlsfHandle handle
|
Tlsf.Handle handle
|
||||||
) : MatrixStorage<T> where T : unmanaged, INumber<T>
|
) : MatrixStorage<T> where T : unmanaged, INumber<T>
|
||||||
{
|
{
|
||||||
|
|
||||||
internal readonly HLSFAllocator _allocator = allocator;
|
internal readonly Tlsf.Handle _handle = handle;
|
||||||
internal readonly HlsfHandle _handle = handle;
|
|
||||||
|
internal readonly Tlsf _allocator = allocator;
|
||||||
|
|
||||||
protected internal override unsafe T* BufferPointer => (T*)_handle.Pointer;
|
protected internal override unsafe T* BufferPointer => (T*)_handle.Pointer;
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ namespace StgSharp
|
|||||||
{
|
{
|
||||||
// TestRender();
|
// TestRender();
|
||||||
/**/
|
/**/
|
||||||
HybridLayerSegregatedFitAllocator hlsf;
|
TwoLayerSegregatedFitAllocator hlsf;
|
||||||
unchecked {
|
unchecked {
|
||||||
hlsf = new(size, 32, size);
|
hlsf = new(size, 32, size);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
//-----------------------------------------------------------------------
|
//-----------------------------------------------------------------------
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
// file="HlsfStressTest"
|
// file="TlsfStressTest"
|
||||||
// Project: StgSharp
|
// Project: StgSharp
|
||||||
// AuthorGroup: Nitload
|
// AuthorGroup: Nitload
|
||||||
// Copyright (c) Nitload. All rights reserved.
|
// Copyright (c) Nitload. All rights reserved.
|
||||||
@@ -30,9 +30,11 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
using Tlsf = StgSharp.HighPerformance.Memory.TwoLayerSegregatedFitAllocator;
|
||||||
|
|
||||||
namespace StgSharp
|
namespace StgSharp
|
||||||
{
|
{
|
||||||
internal static class HlsfStressTest
|
internal static class TlsfStressTest
|
||||||
{
|
{
|
||||||
|
|
||||||
private const uint MaxAllocSize = 32 * 1024 * 1024; // 32MB documented cap
|
private const uint MaxAllocSize = 32 * 1024 * 1024; // 32MB documented cap
|
||||||
@@ -56,7 +58,7 @@ namespace StgSharp
|
|||||||
|
|
||||||
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))
|
using (TwoLayerSegregatedFitAllocator allocator = new TwoLayerSegregatedFitAllocator((nuint)ArenaBytes))
|
||||||
{
|
{
|
||||||
Warmup(allocator);
|
Warmup(allocator);
|
||||||
|
|
||||||
@@ -75,7 +77,7 @@ namespace StgSharp
|
|||||||
|
|
||||||
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))
|
using (TwoLayerSegregatedFitAllocator allocator = new TwoLayerSegregatedFitAllocator((nuint)ArenaBytes))
|
||||||
{
|
{
|
||||||
Warmup(allocator);
|
Warmup(allocator);
|
||||||
|
|
||||||
@@ -89,7 +91,7 @@ namespace StgSharp
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Console.WriteLine();
|
Console.WriteLine();
|
||||||
using (HybridLayerSegregatedFitAllocator allocator = new HybridLayerSegregatedFitAllocator((nuint)ArenaBytes))
|
using (TwoLayerSegregatedFitAllocator allocator = new TwoLayerSegregatedFitAllocator((nuint)ArenaBytes))
|
||||||
{
|
{
|
||||||
Warmup(allocator);
|
Warmup(allocator);
|
||||||
|
|
||||||
@@ -99,7 +101,7 @@ namespace StgSharp
|
|||||||
}
|
}
|
||||||
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)");
|
||||||
using (HybridLayerSegregatedFitAllocator allocator = new HybridLayerSegregatedFitAllocator((nuint)ArenaBytes))
|
using (TwoLayerSegregatedFitAllocator allocator = new TwoLayerSegregatedFitAllocator((nuint)ArenaBytes))
|
||||||
{
|
{
|
||||||
Warmup(allocator);
|
Warmup(allocator);
|
||||||
|
|
||||||
@@ -108,7 +110,7 @@ namespace StgSharp
|
|||||||
}
|
}
|
||||||
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)");
|
||||||
using (HybridLayerSegregatedFitAllocator allocator = new HybridLayerSegregatedFitAllocator((nuint)ArenaBytes))
|
using (TwoLayerSegregatedFitAllocator allocator = new TwoLayerSegregatedFitAllocator((nuint)ArenaBytes))
|
||||||
{
|
{
|
||||||
Warmup(allocator);
|
Warmup(allocator);
|
||||||
|
|
||||||
@@ -120,7 +122,7 @@ namespace StgSharp
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void BenchAllocFreePairs(
|
private static void BenchAllocFreePairs(
|
||||||
HybridLayerSegregatedFitAllocator allocator,
|
TwoLayerSegregatedFitAllocator allocator,
|
||||||
int pairs,
|
int pairs,
|
||||||
uint blockSize
|
uint blockSize
|
||||||
)
|
)
|
||||||
@@ -128,7 +130,7 @@ namespace StgSharp
|
|||||||
Stopwatch sw = Stopwatch.StartNew();
|
Stopwatch sw = Stopwatch.StartNew();
|
||||||
for (int i = 0; i < pairs; i++)
|
for (int i = 0; i < pairs; i++)
|
||||||
{
|
{
|
||||||
HlsfHandle h = allocator.Alloc(blockSize);
|
Tlsf.Handle h = allocator.Alloc(blockSize);
|
||||||
allocator.Free(h);
|
allocator.Free(h);
|
||||||
}
|
}
|
||||||
sw.Stop();
|
sw.Stop();
|
||||||
@@ -139,7 +141,7 @@ namespace StgSharp
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void BenchAllocThroughput(
|
private static void BenchAllocThroughput(
|
||||||
HybridLayerSegregatedFitAllocator allocator,
|
TwoLayerSegregatedFitAllocator allocator,
|
||||||
uint blockSize,
|
uint blockSize,
|
||||||
bool touch
|
bool touch
|
||||||
)
|
)
|
||||||
@@ -155,14 +157,14 @@ namespace StgSharp
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<HlsfHandle> list = new List<HlsfHandle>((int)ops);
|
List<Tlsf.Handle> list = new List<Tlsf.Handle>((int)ops);
|
||||||
Stopwatch sw = Stopwatch.StartNew();
|
Stopwatch sw = Stopwatch.StartNew();
|
||||||
for (long i = 0; i < ops; i++)
|
for (long i = 0; i < ops; i++)
|
||||||
{
|
{
|
||||||
HlsfHandle h = allocator.Alloc(blockSize);
|
Tlsf.Handle h = allocator.Alloc(blockSize);
|
||||||
if (touch)
|
if (touch)
|
||||||
{
|
{
|
||||||
Span<byte> span = h.BufferHandle;
|
Span<byte> span = h.AsSpan(10);
|
||||||
if (!span.IsEmpty) {
|
if (!span.IsEmpty) {
|
||||||
span[0] = (byte)(i & 0xFF);
|
span[0] = (byte)(i & 0xFF);
|
||||||
}
|
}
|
||||||
@@ -173,7 +175,7 @@ namespace StgSharp
|
|||||||
}
|
}
|
||||||
sw.Stop();
|
sw.Stop();
|
||||||
double allocOpsPerSec = ops / sw.Elapsed.TotalSeconds;
|
double allocOpsPerSec = ops / sw.Elapsed.TotalSeconds;
|
||||||
foreach (HlsfHandle h in list) {
|
foreach (Tlsf.Handle h in list) {
|
||||||
allocator.Free(h);
|
allocator.Free(h);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,19 +184,19 @@ namespace StgSharp
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void BenchFragmentationCycle(
|
private static void BenchFragmentationCycle(
|
||||||
HybridLayerSegregatedFitAllocator allocator,
|
TwoLayerSegregatedFitAllocator allocator,
|
||||||
int targetMiB
|
int targetMiB
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
int targetBytes = targetMiB * 1024 * 1024;
|
int targetBytes = targetMiB * 1024 * 1024;
|
||||||
Random rng = new Random(123);
|
Random rng = new Random(123);
|
||||||
List<HlsfHandle> live = new List<HlsfHandle>(128 * 1024);
|
List<Tlsf.Handle> live = new List<Tlsf.Handle>(128 * 1024);
|
||||||
long allocated = 0;
|
long allocated = 0;
|
||||||
Stopwatch swA = Stopwatch.StartNew();
|
Stopwatch swA = Stopwatch.StartNew();
|
||||||
while (allocated < targetBytes)
|
while (allocated < targetBytes)
|
||||||
{
|
{
|
||||||
uint sz = PickSize(rng);
|
uint sz = PickSize(rng);
|
||||||
HlsfHandle h = allocator.Alloc(sz);
|
Tlsf.Handle h = allocator.Alloc(sz);
|
||||||
live.Add(h);
|
live.Add(h);
|
||||||
allocated += sz;
|
allocated += sz;
|
||||||
}
|
}
|
||||||
@@ -218,7 +220,7 @@ namespace StgSharp
|
|||||||
while (allocated < targetBytes / 2)
|
while (allocated < targetBytes / 2)
|
||||||
{
|
{
|
||||||
uint sz = PickSize(rng);
|
uint sz = PickSize(rng);
|
||||||
HlsfHandle h = allocator.Alloc(sz);
|
Tlsf.Handle h = allocator.Alloc(sz);
|
||||||
live.Add(h);
|
live.Add(h);
|
||||||
allocated += sz;
|
allocated += sz;
|
||||||
reCount++;
|
reCount++;
|
||||||
@@ -227,7 +229,7 @@ namespace StgSharp
|
|||||||
Console.WriteLine($" phaseC re-alloc {reCount} in {swC.Elapsed.TotalSeconds:0.00}s ~{allocated / (1024.0 * 1024.0):0}MB");
|
Console.WriteLine($" phaseC re-alloc {reCount} in {swC.Elapsed.TotalSeconds:0.00}s ~{allocated / (1024.0 * 1024.0):0}MB");
|
||||||
|
|
||||||
Stopwatch swD = Stopwatch.StartNew();
|
Stopwatch swD = Stopwatch.StartNew();
|
||||||
foreach (HlsfHandle h in live) {
|
foreach (Tlsf.Handle h in live) {
|
||||||
allocator.Free(h);
|
allocator.Free(h);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,12 +238,12 @@ namespace StgSharp
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void BenchFreeThroughput(
|
private static void BenchFreeThroughput(
|
||||||
HybridLayerSegregatedFitAllocator allocator,
|
TwoLayerSegregatedFitAllocator allocator,
|
||||||
int count,
|
int count,
|
||||||
uint blockSize
|
uint blockSize
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
List<HlsfHandle> list = new List<HlsfHandle>(count);
|
List<Tlsf.Handle> list = new List<Tlsf.Handle>(count);
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
list.Add(allocator.Alloc(blockSize));
|
list.Add(allocator.Alloc(blockSize));
|
||||||
}
|
}
|
||||||
@@ -282,15 +284,15 @@ namespace StgSharp
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void Warmup(
|
private static void Warmup(
|
||||||
HybridLayerSegregatedFitAllocator allocator
|
TwoLayerSegregatedFitAllocator allocator
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
List<HlsfHandle> tmp = new List<HlsfHandle>(4096);
|
List<Tlsf.Handle> tmp = new List<Tlsf.Handle>(4096);
|
||||||
for (int i = 0; i < 4096; i++) {
|
for (int i = 0; i < 4096; i++) {
|
||||||
tmp.Add(allocator.Alloc(256));
|
tmp.Add(allocator.Alloc(256));
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (HlsfHandle h in tmp) {
|
foreach (Tlsf.Handle h in tmp) {
|
||||||
allocator.Free(h);
|
allocator.Free(h);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -33,6 +33,8 @@ using System.Linq;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
using Tlsf = StgSharp.HighPerformance.Memory.TwoLayerSegregatedFitAllocator;
|
||||||
|
|
||||||
namespace StgSharp
|
namespace StgSharp
|
||||||
{
|
{
|
||||||
internal partial class Program
|
internal partial class Program
|
||||||
@@ -47,7 +49,7 @@ namespace StgSharp
|
|||||||
// HLSF test arena
|
// HLSF test arena
|
||||||
Console.WriteLine(
|
Console.WriteLine(
|
||||||
"=== HLSF (Hybrid Layer Segregated Fit Allocator) Single-threaded Stress Test ===\n");
|
"=== HLSF (Hybrid Layer Segregated Fit Allocator) Single-threaded Stress Test ===\n");
|
||||||
HybridLayerSegregatedFitAllocator hlsfAllocator = new(size);
|
TwoLayerSegregatedFitAllocator hlsfAllocator = new(size);
|
||||||
|
|
||||||
// === 简洁交替测试:100次alloc与100次free交替进行,直到触发异常或达到最大循环 ===
|
// === 简洁交替测试:100次alloc与100次free交替进行,直到触发异常或达到最大循环 ===
|
||||||
Console.WriteLine("\n=== 简洁交替测试:100次alloc + 100次free 交替 ===");
|
Console.WriteLine("\n=== 简洁交替测试:100次alloc + 100次free 交替 ===");
|
||||||
@@ -80,7 +82,7 @@ namespace StgSharp
|
|||||||
Random rnd = new Random(42);
|
Random rnd = new Random(42);
|
||||||
for (int round = 1; round <= maxRounds; round++)
|
for (int round = 1; round <= maxRounds; round++)
|
||||||
{
|
{
|
||||||
List<HlsfHandle> batch = new(maxCount);
|
List<Tlsf.Handle> batch = new(maxCount);
|
||||||
for (int i = 0; i < maxCount; i++)
|
for (int i = 0; i < maxCount; i++)
|
||||||
{
|
{
|
||||||
uint sz = sizes[rnd.Next(sizes.Length)];
|
uint sz = sizes[rnd.Next(sizes.Length)];
|
||||||
@@ -90,7 +92,7 @@ namespace StgSharp
|
|||||||
}
|
}
|
||||||
for (int i = 5; i < maxCount; i += 10)
|
for (int i = 5; i < maxCount; i += 10)
|
||||||
{
|
{
|
||||||
HlsfHandle origin = batch[i];
|
Tlsf.Handle origin = batch[i];
|
||||||
int index = i + offset[rnd.Next(offset.Length)];
|
int index = i + offset[rnd.Next(offset.Length)];
|
||||||
batch[i] = batch[index];
|
batch[i] = batch[index];
|
||||||
batch[index] = origin;
|
batch[index] = origin;
|
||||||
@@ -104,9 +106,10 @@ namespace StgSharp
|
|||||||
if (round % 100 == 0)
|
if (round % 100 == 0)
|
||||||
{
|
{
|
||||||
Console.WriteLine($"[交替测试] 已完成 {round} 轮 {maxCount}alloc+{maxCount}free");
|
Console.WriteLine($"[交替测试] 已完成 {round} 轮 {maxCount}alloc+{maxCount}free");
|
||||||
hlsfAllocator.DumpPositionChain(false, 10);
|
|
||||||
hlsfAllocator.Collect();
|
// hlsfAllocator.DumpPositionChain(false, 10);
|
||||||
hlsfAllocator.DumpPositionChain(false, 10);
|
// hlsfAllocator.Collect();
|
||||||
|
// hlsfAllocator.DumpPositionChain(false, 10);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Console.WriteLine("[交替测试] 正常结束,无异常触发");
|
Console.WriteLine("[交替测试] 正常结束,无异常触发");
|
||||||
|
|||||||
Reference in New Issue
Block a user