diff --git a/.cr/personal/Editor/Painting/Structural Highlighting.xml b/.cr/personal/Editor/Painting/Structural Highlighting.xml
new file mode 100644
index 00000000..347228e8
--- /dev/null
+++ b/.cr/personal/Editor/Painting/Structural Highlighting.xml
@@ -0,0 +1,6 @@
+
+
+
+ False
+
+
\ No newline at end of file
diff --git a/.editorconfig b/.editorconfig
index a8a7e26e..00843515 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -114,22 +114,4 @@ dotnet_naming_style.以_i_开始.word_separator =
dotnet_naming_style.以_i_开始.capitalization = pascal_case
dotnet_naming_style.帕斯�拼写法.required_prefix =
-dotnet_naming_style.帕斯�拼写法.required_suffix =
-dotnet_naming_style.帕斯�拼写法.word_separator =
-dotnet_naming_style.帕斯�拼写法.capitalization = pascal_case
-
-dotnet_naming_style.帕斯�拼写法.required_prefix =
-dotnet_naming_style.帕斯�拼写法.required_suffix =
-dotnet_naming_style.帕斯�拼写法.word_separator =
-dotnet_naming_style.帕斯�拼写法.capitalization = pascal_case
-
-[*.{cs,vb}]
-dotnet_style_qualification_for_field = false:silent
-dotnet_style_qualification_for_property = false:silent
-dotnet_style_qualification_for_method = false:silent
-dotnet_style_qualification_for_event = false:silent
-dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
-end_of_line = crlf
-tab_width = 4
-indent_size = 4
-dotnet_style_operator_placement_when_wrapping = beginning_of_line
\ No newline at end of file
+dotenv
\ No newline at end of file
diff --git a/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.Alloc.cs b/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.Alloc.cs
index 4b0ad74d..a3e7c05a 100644
--- a/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.Alloc.cs
+++ b/StgSharp.Common/HighPerformance/Memory/HLSFAllocator.Alloc.cs
@@ -60,9 +60,12 @@ namespace StgSharp.HighPerformance.Memory
s = 0;
i++;
}
- if (i >= levelSizeArray.Length)
+ if (i > MaxLevel)
{
- if (TryAllocateNew16MBBlock(out handle))
+ if (TryPopLevelForAllocation(10, 1, out handle))
+ {
+ break;
+ } else if (TryAllocateNew32MBBlock(out handle))
{
break;
}
@@ -106,7 +109,7 @@ namespace StgSharp.HighPerformance.Memory
long offset = (long)e->Position - sizeToSlice - (long)m_Buffer; // offset of buffer to slice
long end = (long)e->Position - (long)m_Buffer; // end offset of buffer to slice
- int i = (BitOperations.TrailingZeroCount(remain) - 6) / 2;
+ int i = (BitOperations.TrailingZeroCount(offset) - 6) / 2;
i = int.Min(10, i);
for (; i < levelSizeArray.Length; i++)
{
@@ -116,7 +119,7 @@ namespace StgSharp.HighPerformance.Memory
int count = (int)((long.Min(edge, end) - offset) / size);
if (count > 0)
{
- int newSize = count * size;
+ long newSize = count * size;
Entry* newEntry = (Entry*)_entries.Allocate();
InsertEntryBefore(newEntry, next);
@@ -142,7 +145,7 @@ namespace StgSharp.HighPerformance.Memory
int count = (int)((long.Min(edge, end) - offset) / upper);
if (count > 0)
{
- int newSize = count * upper;
+ long newSize = (long)count * (long)upper;
Entry* newEntry = (Entry*)_entries.Allocate();
InsertEntryBefore(newEntry, next);
@@ -153,7 +156,7 @@ namespace StgSharp.HighPerformance.Memory
b->EntryRef = newEntry;
PushBucketToLevel(i, count - 1, b);
offset += newSize;
- remain -= (long)newSize;
+ remain -= newSize;
if (remain <= 0) {
return;
}
@@ -167,7 +170,7 @@ namespace StgSharp.HighPerformance.Memory
int count = (int)((long.Min(edge, end) - offset) / size);
if (count > 0)
{
- int newSize = count * size;
+ long newSize = count * size;
Entry* newEntry = (Entry*)_entries.Allocate();
InsertEntryBefore(newEntry, next);
@@ -180,7 +183,7 @@ namespace StgSharp.HighPerformance.Memory
b->PreviousLevel = null;
PushBucketToLevel(i, count - 1, b);
offset += newSize;
- remain -= (long)newSize;
+ remain -= newSize;
}
if (remain <= 0) {
return;
@@ -197,7 +200,7 @@ namespace StgSharp.HighPerformance.Memory
///
/// True if allocation succeeded
///
- private bool TryAllocateNew16MBBlock(out Entry* handle)
+ private bool TryAllocateNew32MBBlock(out Entry* handle)
{
nuint pos = _spareMemory->Position;
long remain = _spareMemory->Size;
@@ -206,7 +209,7 @@ namespace StgSharp.HighPerformance.Memory
handle = null;
return false;
}
- long size = remain > 16 * 1024 * 1024L ? 16 * 1024 * 1024L : remain;
+ long size = remain > 32 * 1024 * 1024L ? 32 * 1024 * 1024L : remain;
// build new entry
diff --git a/StgSharp.Common/HighPerformance/Memory/InroductionToHLSF.md b/StgSharp.Common/HighPerformance/Memory/InroductionToHLSF.md
index 68c417f7..92c2da3c 100644
--- a/StgSharp.Common/HighPerformance/Memory/InroductionToHLSF.md
+++ b/StgSharp.Common/HighPerformance/Memory/InroductionToHLSF.md
@@ -1,271 +1,109 @@
# HLSF (Hybrid Layer Segregated Fit) Allocator
-A high-performance, O(1) memory allocator designed for real-time applications that demand predictable allocation and deallocation performance.
+A constant-time (O(1)) allocator for real-time, high-throughput scenarios. It uses layered size classes with segmented buckets and stores all metadata externally for clean user memory and better cache locality.
-## Overview
-
-The Hybrid Layer Segregated Fit (HLSF) allocator is a sophisticated memory management system that combines the benefits of segregated free lists with layered size classification. It provides constant-time allocation and deallocation operations while maintaining excellent memory utilization and low fragmentation.
-
-## Key Features
-
-### Performance Characteristics
-- **O(1) Time Complexity**: Both allocation and deallocation operations execute in constant time
-- **High Throughput**: Capable of handling millions of operations per second
-- **Low Latency**: Predictable performance suitable for real-time applications
-- **Excellent Deallocation Speed**: Superior free performance compared to traditional allocators
-
-### Memory Management
-- **External Metadata Storage**: All allocation metadata is stored in external SLAB allocators, keeping the main allocation area clean
-- **Native Byte Alignment Control**: Configurable alignment (minimum 16 bytes) with hardware-optimized defaults
-- **Large Block Support**: Single allocation limit of 32MB per request
-- **Efficient Memory Utilization**: Low overhead with intelligent block merging
-
-### Architecture
-- **10-Level Hierarchy**: Size classes from 64 bytes to 16MB (64, 256, 1KB, 4KB, 16KB, 64KB, 256KB, 1MB, 4MB, 16MB)
-- **3-Segment Segregation**: Each level is divided into 3 segments for better size distribution
-- **Hybrid Approach**: Combines best aspects of segregated fit and layered allocation strategies
-
-## Architecture Details
-
-### Size Classification System
-
-The allocator uses a logarithmic size classification system with 10 levels:
-
-```
-Level 0: 64 bytes (2^6)
-Level 1: 256 bytes (2^8)
-Level 2: 1KB (2^10)
-Level 3: 4KB (2^12)
-Level 4: 16KB (2^14)
-Level 5: 64KB (2^16)
-Level 6: 256KB (2^18)
-Level 7: 1MB (2^20)
-Level 8: 4MB (2^22)
-Level 9: 16MB (2^24)
-```
-
-Each level follows the pattern: `size = 64 * 4^level`
-
-### Bucket Organization
-
-Each size level is further divided into 3 segments based on exact size requirements:
-- **Segment 0**: 1x level size
-- **Segment 1**: 2x level size
-- **Segment 2**: 3x level size
-
-This provides fine-grained size matching while maintaining O(1) lookup performance.
-
-### External Metadata Design
-
-Unlike traditional allocators that embed metadata within allocated blocks, HLSF stores all metadata externally:
-
-- **Entry Structure (36 bytes)**: Contains position, size, level, and linkage information
-- **BucketNode Structure (32 bytes)**: Manages free block organization with integrity verification
-- **SLAB Storage**: Both structures are allocated from separate SLAB allocators for optimal cache performance
-
-This design provides several advantages:
-- Clean allocation areas free from metadata corruption
-- Better cache locality for metadata operations
-- Simplified debugging and memory inspection
-- No metadata overhead in allocated blocks
-
-## Performance Benchmarks
-
-*Note: The following results are from informal testing and should be considered preliminary.*
-
-### Single-threaded Performance (65,536 operations)
-
-| Operation | Average Time | Throughput | Performance Rating |
-|-----------|--------------|------------|-------------------|
-| Allocation | 31-40ms | ~2M allocs/sec | Good |
-| Free | 2-3ms | ~25M frees/sec | Excellent |
-
-**Test Configuration:**
-- Pool Size: 16MB
-- Block Sizes: 64-1024 bytes (mixed)
-- Platform: x64, .NET 8
-- Iterations: 5 rounds
-
-### Memory Efficiency
-
-- **Metadata Overhead**: ~2.8% of total allocation
-- **Alignment**: 16-byte default (configurable)
-- **Fragmentation**: Low due to intelligent merging algorithms
-
-## Usage Examples
-
-### Basic Allocation
+## Key Points
+- O(1) allocate/free with 10 levels: 64B ¡ú 16MB; each level split into 1¡Á/2¡Á/3¡Á segments
+- External metadata (`Entry`, `BucketNode`) in dedicated SLABs; user blocks have no headers
+- Configurable alignment (¡Ý16B); large block cap per allocation: 32MB
+- Single-threaded implementation (external sync required for multi-threaded use)
+## Quick Use
```csharp
-// Create allocator with 64MB pool
-using var allocator = new HybridLayerSegregatedFitAllocator(64 * 1024 * 1024);
-
-// Allocate 1KB block
-var handle = allocator.Alloc(1024);
-
-// Use the allocated memory
-Span memory = handle.BufferHandle;
-memory[0] = 42;
-
-// Free the block
-allocator.Free(handle);
+using var hlsf = new HybridLayerSegregatedFitAllocator(64 * 1024 * 1024);
+var h = hlsf.Alloc(1024);
+Span s = h.BufferHandle; s[0] = 42;
+hlsf.Free(h);
```
-### Custom Alignment
+## Benchmarks (this run)
+Environment: .NET 8 (Release), x64, single-thread, arena=3072 MiB. Results from `StgSharpDebug/HlsfStressTest`.
-```csharp
-// Create allocator with 32-byte alignment
-using var allocator = new HybridLayerSegregatedFitAllocator(
- byteSize: 32 * 1024 * 1024,
- align: 32
-);
+- Free throughput: 1KB ¡Á 262,144 ¡ú 27,062,260.6 free/s
+- Alloc+free pairs: 256B ¡Á 5,000,000 ¡ú 19,256,409.9 ops/s
+- Fragmentation cycle (¡Ö512MB): fast recovery in short cycle (see test for details)
-var handle = allocator.Alloc(256);
-// Memory is guaranteed to be 32-byte aligned
-```
+Representative allocation throughput (higher is better):
-### Bulk Operations
+| Size | Ops | alloc/s (no touch) | alloc/s (touch) |
+|------|----:|--------------------:|----------------:|
+| 64B | 2,000,000 | 7,902,555.2 | 14,176,544.8 |
+| 1KB | 1,572,864 | 5,794,161.6 | 7,293,856.5 |
+| 4KB | 393,216 | 5,210,132.6 | 6,646,372.3 |
+| 32KB | 49,152 | 5,931,432.3 | 7,719,199.1 |
+| 256KB | 6,144 | 14,663,484.5 | 10,425,929.1 |
+| 512KB | 3,072 | 11,054,336.1 | 16,245,372.8 |
+| 1MB | 1,536 | 13,391,456.0 | 9,159,212.9 |
+| 2MB | 768 | 17,028,824.8 | 11,962,616.8 |
+| 8MB | 192 | 13,061,224.5 | 9,142,857.1 |
+| 32MB | 48 | 11,707,317.1 | 9,411,764.7 |
-```csharp
-var handles = new List();
+Notes:
+- Peak no-touch at 2MB (¡Ö17.0M alloc/s). Peak touch at 512KB (¡Ö16.25M alloc/s).
+- Non-aligned sizes (e.g., 768B, 3KB) typically lower due to slicing/boundary handling.
-// Allocate multiple blocks
-for (int i = 0; i < 1000; i++)
-{
- handles.Add(allocator.Alloc((uint)(64 + i % 960))); // 64-1023 bytes
-}
+## Detailed Design
-// Free all blocks
-foreach (var handle in handles)
-{
- allocator.Free(handle);
-}
-```
+### Size Classes and Segments
+- Level sizes: `levelSizeArray = [64, 256, 1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216]` (64B ¡¤ 4^L)
+- Level selection:
+ - `GetLevelFromSize(uint S)`: `level = max(0, (log2(S) - 6) / 2)` using `BitOperations.LeadingZeroCount`
+- Segment selection (within a level):
+ - `DetermineSegmentIndex(size, level) = (int)((size - 1) / levelSizeArray[level])` ¡ú 0/1/2
+- Bucket index: `index = 3*level + segment` (for L ¡Ü 9); overflow bucket at `^1` for level > 9
-## API Reference
+### Metadata Structures
+- `Entry` (external SLAB): linkage in position chain, `Position`, `Size`, `Level`, `State`
+ - Invariants: near neighbors must form a consistent double-linked ring; `Level` setter validates size vs. level window
+- `BucketNode` (in user region): `EntryRef`, `NextLevel`, `PreviousLevel`, `IsInBucket`
+- Position chain: circular doubly linked list anchored by sentinel `_spareMemory` (`State.Spare`)
-### HybridLayerSegregatedFitAllocator
+### Allocation Flow
+1) Compute `level` and `segment` from requested `size`
+2) Try pop bucket (`TryPopLevelForAllocation(level, segment, out entry)`) and escalate:
+ - Advance `segment` 0¡ú1¡ú2, then `level++`, until `MaxLevel`
+ - If still empty: try pop from overflow bucket (L=10, seg=1) or `TryAllocateNew32MBBlock`
+3) On success: bound-check the slice offset; set `entry` to `Alloc` using `SetEntryLevel`
+4) If remainder exists in the source region, call `SliceMemory(entry->NextNear, remainder)` to produce free chunks into buckets
-Main allocator class providing memory management functionality.
+### Slicing Logic (`SliceMemory`)
+- Inputs: `next` entry position, `sizeToSlice`
+- Compute `offset` and `end` relative to base buffer; pick starting level by trailing-zero count of `offset`:
+ - `i = (BitOperations.TrailingZeroCount(offset) - 6) / 2`, clamped to `¡Ü MaxLevel`
+- Three phases to cover [offset, end):
+ 1) Forward levels i..N-1: for each level size `size` and `upper=size*4`, align to `edge=((offset+upper-1)/upper)*upper`, pack `count = ((min(edge,end) - offset) / size)` blocks
+ 2) If `i > MaxLevel`: pack in largest upper window on tail
+ 3) Backward i-1..0: fill remaining tail with smaller sizes
+- For each packed region: allocate new `Entry`, insert before `next`, mark `Empty`, and push corresponding `BucketNode` into the `(level, segment)` bucket (`PushBucketToLevel(i, count-1, b)`)
-#### Constructors
+### Free and Merge (`MergeAndRemoveFromBuckets`)
+- Given an `Entry` to free (turned `Empty` by caller):
+ - Merge left while neighbor `p` is `Empty`, same `Level`, and within boundary (`AssertBoundary`)
+ - Remove `p` from bucket (`RemoveFromBucket`), expand current, fix `Position`, unlink `p`
+ - Merge right with neighbor `n` by the same rule
+ - Optionally promote `Level` if `Size ¡Ý levelSizeArray[originLevel] * 4`
+ - Ensure `BucketNode.EntryRef` points to the merged entry; caller re-enqueues into the proper bucket
-```csharp
-public HybridLayerSegregatedFitAllocator(nuint byteSize)
-public HybridLayerSegregatedFitAllocator(nuint byteSize, int align)
-```
+### Buckets
+- `PushBucketToLevel(level, segment, node)`: push to bucket head, set `IsInBucket`, maintain prev/next
+- `TryPopLevel(level, segment, out node)`: pop head; if empty, return false
+- `RemoveFromBucket(level, segment, node)`: unlink from middle or use `TryPopLevel` if it is the head
-#### Methods
+### Large Allocations and Spare Memory
+- Requests > 32MB use `NativeMemory.AlignedAlloc(size, align)` and mark `State.Large`
+- When buckets are empty, `TryAllocateNew32MBBlock` carves a 32MB region from `_spareMemory`, builds an `Empty` entry, and advances the spare pointer
-```csharp
-public HybridLayerSegregatedFitAllocationHandle Alloc(uint size)
-public void Free(HybridLayerSegregatedFitAllocationHandle handle)
-public void Dispose()
-```
+### Alignment
+- Default `_align = 16`; `AlignOfLevel(level)` returns `min(levelSizeArray[level], _align)`
+- Internal blocks align to level boundaries by design of slicing/edge calculations
-### HybridLayerSegregatedFitAllocationHandle
+### Complexity and Invariants
+- Allocate/free are O(1) on average with bounded levels (¡Ü10) and constant-time bucket ops
+- Invariants guarded by exceptions (e.g., `HLSFPositionChainBreakException`) and pointer consistency checks
-Handle representing an allocated memory block.
+## Profiling (hotspots, current workloads)
+- `SliceMemory` ¡Ö22% (self ¡Ö17%) ¡ª slicing and bucket enqueue dominate allocation path
+- `MergeAndRemoveFromBuckets` ¡Ö13% (self ¡Ö12%) ¡ª merge + removal dominate free path
+- `Collect` ¡Ö7.6% (self ¡Ö7.5%) ¡ª low overall cost
-#### Properties
-
-```csharp
-public readonly byte* Pointer { get; }
-public readonly Span BufferHandle { get; }
-public readonly uint AllocSize { get; }
-public readonly ref byte this[int index] { get; }
-```
-
-## Thread Safety
-
-The current implementation is **single-threaded**. For multi-threaded applications, external synchronization is required. A concurrent version is planned for future releases.
-
-## Comparison with Other Allocators
-
-### vs TLSF (Two-Level Segregated Fit)
-
-| Aspect | HLSF | TLSF | Notes |
-|--------|------|------|-------|
-| Allocation Speed | ~2M ops/sec | ~3-5M ops/sec | HLSF has optimization potential |
-| Deallocation Speed | ~25M ops/sec | ~10-15M ops/sec | **HLSF advantage** |
-| Memory Overhead | ~2.8% | ~1-2% | HLSF uses external metadata |
-| Time Complexity | O(1) | O(1) | Equal |
-| Code Complexity | Moderate | Low | HLSF more sophisticated |
-
-### Strengths
-- Superior deallocation performance
-- Clean separation of data and metadata
-- Configurable alignment support
-- Excellent for allocation-heavy workloads
-
-### Areas for Improvement
-- Allocation speed optimization
-- Memory overhead reduction
-- Concurrent implementation
-
-## Implementation Notes
-
-### Alignment Guarantees
-
-- Minimum alignment: 16 bytes
-- All allocations respect specified alignment
-- Internal blocks aligned to 64-byte boundaries for optimal cache performance
-
-### Merging Strategy
-
-The allocator employs an aggressive merging strategy:
-
-1. **Immediate Merging**: Adjacent free blocks are merged during deallocation
-2. **Boundary Checking**: Ensures merging respects level boundaries
-3. **Bidirectional Merging**: Checks both forward and backward neighbors
-4. **Level Promotion**: Merged blocks may be promoted to higher levels
-
-## Future Roadmap
-
-### Planned Optimizations
-
-1. **Performance Enhancements**
- - Lookup table optimization for size classification
- - Bucket operation improvements
- - SIMD utilization for bulk operations
-
-2. **Concurrent Support**
- - Thread-local caches
- - Lock-free bucket operations
- - NUMA-aware allocation
-
-3. **Advanced Features**
- - Adaptive size level adjustment
- - Memory pressure handling
- - Statistics and profiling support
-
-### Target Performance Goals
-
-- Allocation Speed: 4-6M ops/sec (single-threaded)
-- Memory Overhead: <1.5%
-- Fragmentation Rate: <3%
-- Concurrent Scaling: Linear up to 16 threads
-
-## Building and Testing
-
-The HLSF allocator is part of the StgSharp.Common library and requires:
-
-- .NET 8 or later
-- x64 architecture (no x86 support planned)
-- Windows/Linux
-
-### Running Benchmarks
-
-```csharp
-// See StgSharpDebug/Program.cs for comprehensive benchmark suite
-var allocator = new HybridLayerSegregatedFitAllocator(16 * 1024 * 1024);
-// Run allocation/deallocation cycles...
-```
-
-## License
-
-This implementation is part of the StgSharp project and follows the project's licensing terms.
-
-## Contributing
-
-Performance improvements, bug fixes, and documentation enhancements are welcome. Please ensure all changes maintain the O(1) performance guarantees and include appropriate benchmarks.
\ No newline at end of file
+Suggested focus: reduce per-slice math and bucket ops; add fast-paths for fully aligned regions; batch/lazy removals after merges.
\ No newline at end of file
diff --git a/StgSharpDebug/HlsfStressTest.cs b/StgSharpDebug/HlsfStressTest.cs
index 6e1450fd..ffaa4031 100644
--- a/StgSharpDebug/HlsfStressTest.cs
+++ b/StgSharpDebug/HlsfStressTest.cs
@@ -65,7 +65,6 @@ namespace StgSharp
uint size = sz > MaxAllocSize ? MaxAllocSize : sz;
BenchAllocThroughput(allocator, size, touch:false);
allocator.Collect();
- allocator.DumpPositionChain();
GC.Collect();
GC.WaitForPendingFinalizers();
}
@@ -130,7 +129,9 @@ namespace StgSharp
}
sw.Stop();
double opsPerSec = pairs / sw.Elapsed.TotalSeconds;
- Console.WriteLine($" size={Fmt(blockSize),7} pairs={pairs,9} alloc+free/s={opsPerSec,12:0.0}");
+
+ // Left align and widen columns
+ Console.WriteLine($" size={Fmt(blockSize),-12} pairs={pairs,-12} alloc+free/s={opsPerSec,-16:0.0}");
}
private static void BenchAllocThroughput(
@@ -171,7 +172,8 @@ namespace StgSharp
allocator.Free(h);
}
- Console.WriteLine($" size={Fmt(blockSize),7} ops={ops,9} alloc/s={allocOpsPerSec,12:0.0} touch={touch}");
+ // Left align and widen columns
+ Console.WriteLine($" size={Fmt(blockSize),-12} ops={ops,-12} alloc/s={allocOpsPerSec,-16:0.0} touch={touch,-5}");
}
private static void BenchFragmentationCycle(
@@ -244,7 +246,9 @@ namespace StgSharp
sw.Stop();
double freesPerSec = count / sw.Elapsed.TotalSeconds;
- Console.WriteLine($" size={Fmt(blockSize),7} count={count,9} free/s={freesPerSec,12:0.0}");
+
+ // Left align and widen columns
+ Console.WriteLine($" size={Fmt(blockSize),-12} count={count,-12} free/s={freesPerSec,-16:0.0}");
}
private static string Fmt(uint bytes)