hlsf error is solved. HLSF is now full functional available.
This commit is contained in:
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Root Type="DevExpress.CodeRush.VisualizePlugins.StructuralHighlighting.Options.StructuralHighlightingOptions">
|
||||||
|
<Options Language="Neutral">
|
||||||
|
<Enabled>False</Enabled>
|
||||||
|
</Options>
|
||||||
|
</Root>
|
||||||
+1
-19
@@ -114,22 +114,4 @@ dotnet_naming_style.以_i_开始.word_separator =
|
|||||||
dotnet_naming_style.以_i_开始.capitalization = pascal_case
|
dotnet_naming_style.以_i_开始.capitalization = pascal_case
|
||||||
|
|
||||||
dotnet_naming_style.帕斯卡拼写法.required_prefix =
|
dotnet_naming_style.帕斯卡拼写法.required_prefix =
|
||||||
dotnet_naming_style.帕斯卡拼写法.required_suffix =
|
dotenv
|
||||||
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
|
|
||||||
@@ -60,9 +60,12 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
s = 0;
|
s = 0;
|
||||||
i++;
|
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;
|
break;
|
||||||
}
|
}
|
||||||
@@ -106,7 +109,7 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
long offset = (long)e->Position - sizeToSlice - (long)m_Buffer; // offset of buffer to slice
|
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
|
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);
|
i = int.Min(10, i);
|
||||||
for (; i < levelSizeArray.Length; i++)
|
for (; i < levelSizeArray.Length; i++)
|
||||||
{
|
{
|
||||||
@@ -116,7 +119,7 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
int count = (int)((long.Min(edge, end) - offset) / size);
|
int count = (int)((long.Min(edge, end) - offset) / size);
|
||||||
if (count > 0)
|
if (count > 0)
|
||||||
{
|
{
|
||||||
int newSize = count * size;
|
long newSize = count * size;
|
||||||
|
|
||||||
Entry* newEntry = (Entry*)_entries.Allocate();
|
Entry* newEntry = (Entry*)_entries.Allocate();
|
||||||
InsertEntryBefore(newEntry, next);
|
InsertEntryBefore(newEntry, next);
|
||||||
@@ -142,7 +145,7 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
int count = (int)((long.Min(edge, end) - offset) / upper);
|
int count = (int)((long.Min(edge, end) - offset) / upper);
|
||||||
if (count > 0)
|
if (count > 0)
|
||||||
{
|
{
|
||||||
int newSize = count * upper;
|
long newSize = (long)count * (long)upper;
|
||||||
|
|
||||||
Entry* newEntry = (Entry*)_entries.Allocate();
|
Entry* newEntry = (Entry*)_entries.Allocate();
|
||||||
InsertEntryBefore(newEntry, next);
|
InsertEntryBefore(newEntry, next);
|
||||||
@@ -153,7 +156,7 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
b->EntryRef = newEntry;
|
b->EntryRef = newEntry;
|
||||||
PushBucketToLevel(i, count - 1, b);
|
PushBucketToLevel(i, count - 1, b);
|
||||||
offset += newSize;
|
offset += newSize;
|
||||||
remain -= (long)newSize;
|
remain -= newSize;
|
||||||
if (remain <= 0) {
|
if (remain <= 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -167,7 +170,7 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
int count = (int)((long.Min(edge, end) - offset) / size);
|
int count = (int)((long.Min(edge, end) - offset) / size);
|
||||||
if (count > 0)
|
if (count > 0)
|
||||||
{
|
{
|
||||||
int newSize = count * size;
|
long newSize = count * size;
|
||||||
|
|
||||||
Entry* newEntry = (Entry*)_entries.Allocate();
|
Entry* newEntry = (Entry*)_entries.Allocate();
|
||||||
InsertEntryBefore(newEntry, next);
|
InsertEntryBefore(newEntry, next);
|
||||||
@@ -180,7 +183,7 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
b->PreviousLevel = null;
|
b->PreviousLevel = null;
|
||||||
PushBucketToLevel(i, count - 1, b);
|
PushBucketToLevel(i, count - 1, b);
|
||||||
offset += newSize;
|
offset += newSize;
|
||||||
remain -= (long)newSize;
|
remain -= newSize;
|
||||||
}
|
}
|
||||||
if (remain <= 0) {
|
if (remain <= 0) {
|
||||||
return;
|
return;
|
||||||
@@ -197,7 +200,7 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
/// <returns>
|
/// <returns>
|
||||||
/// True if allocation succeeded
|
/// True if allocation succeeded
|
||||||
/// </returns>
|
/// </returns>
|
||||||
private bool TryAllocateNew16MBBlock(out Entry* handle)
|
private bool TryAllocateNew32MBBlock(out Entry* handle)
|
||||||
{
|
{
|
||||||
nuint pos = _spareMemory->Position;
|
nuint pos = _spareMemory->Position;
|
||||||
long remain = _spareMemory->Size;
|
long remain = _spareMemory->Size;
|
||||||
@@ -206,7 +209,7 @@ namespace StgSharp.HighPerformance.Memory
|
|||||||
handle = null;
|
handle = null;
|
||||||
return false;
|
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
|
// build new entry
|
||||||
|
|||||||
@@ -1,271 +1,109 @@
|
|||||||
# HLSF (Hybrid Layer Segregated Fit) Allocator
|
# 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
|
## Key Points
|
||||||
|
- O(1) allocate/free with 10 levels: 64B ★ 16MB; each level split into 1~/2~/3~ segments
|
||||||
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.
|
- External metadata (`Entry`, `BucketNode`) in dedicated SLABs; user blocks have no headers
|
||||||
|
- Configurable alignment (-16B); large block cap per allocation: 32MB
|
||||||
## Key Features
|
- Single-threaded implementation (external sync required for multi-threaded use)
|
||||||
|
|
||||||
### 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
|
|
||||||
|
|
||||||
|
## Quick Use
|
||||||
```csharp
|
```csharp
|
||||||
// Create allocator with 64MB pool
|
using var hlsf = new HybridLayerSegregatedFitAllocator(64 * 1024 * 1024);
|
||||||
using var allocator = new HybridLayerSegregatedFitAllocator(64 * 1024 * 1024);
|
var h = hlsf.Alloc(1024);
|
||||||
|
Span<byte> s = h.BufferHandle; s[0] = 42;
|
||||||
// Allocate 1KB block
|
hlsf.Free(h);
|
||||||
var handle = allocator.Alloc(1024);
|
|
||||||
|
|
||||||
// Use the allocated memory
|
|
||||||
Span<byte> memory = handle.BufferHandle;
|
|
||||||
memory[0] = 42;
|
|
||||||
|
|
||||||
// Free the block
|
|
||||||
allocator.Free(handle);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Custom Alignment
|
## Benchmarks (this run)
|
||||||
|
Environment: .NET 8 (Release), x64, single-thread, arena=3072 MiB. Results from `StgSharpDebug/HlsfStressTest`.
|
||||||
|
|
||||||
```csharp
|
- Free throughput: 1KB ~ 262,144 ★ 27,062,260.6 free/s
|
||||||
// Create allocator with 32-byte alignment
|
- Alloc+free pairs: 256B ~ 5,000,000 ★ 19,256,409.9 ops/s
|
||||||
using var allocator = new HybridLayerSegregatedFitAllocator(
|
- Fragmentation cycle (「512MB): fast recovery in short cycle (see test for details)
|
||||||
byteSize: 32 * 1024 * 1024,
|
|
||||||
align: 32
|
|
||||||
);
|
|
||||||
|
|
||||||
var handle = allocator.Alloc(256);
|
Representative allocation throughput (higher is better):
|
||||||
// Memory is guaranteed to be 32-byte aligned
|
|
||||||
```
|
|
||||||
|
|
||||||
### 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
|
Notes:
|
||||||
var handles = new List<HybridLayerSegregatedFitAllocationHandle>();
|
- 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
|
## Detailed Design
|
||||||
for (int i = 0; i < 1000; i++)
|
|
||||||
{
|
|
||||||
handles.Add(allocator.Alloc((uint)(64 + i % 960))); // 64-1023 bytes
|
|
||||||
}
|
|
||||||
|
|
||||||
// Free all blocks
|
### Size Classes and Segments
|
||||||
foreach (var handle in handles)
|
- Level sizes: `levelSizeArray = [64, 256, 1024, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216]` (64B , 4^L)
|
||||||
{
|
- Level selection:
|
||||||
allocator.Free(handle);
|
- `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
|
### Buckets
|
||||||
public HybridLayerSegregatedFitAllocator(nuint byteSize)
|
- `PushBucketToLevel(level, segment, node)`: push to bucket head, set `IsInBucket`, maintain prev/next
|
||||||
public HybridLayerSegregatedFitAllocator(nuint byteSize, int align)
|
- `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
|
### Alignment
|
||||||
public HybridLayerSegregatedFitAllocationHandle Alloc(uint size)
|
- Default `_align = 16`; `AlignOfLevel(level)` returns `min(levelSizeArray[level], _align)`
|
||||||
public void Free(HybridLayerSegregatedFitAllocationHandle handle)
|
- Internal blocks align to level boundaries by design of slicing/edge calculations
|
||||||
public void Dispose()
|
|
||||||
```
|
|
||||||
|
|
||||||
### 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
|
Suggested focus: reduce per-slice math and bucket ops; add fast-paths for fully aligned regions; batch/lazy removals after merges.
|
||||||
|
|
||||||
```csharp
|
|
||||||
public readonly byte* Pointer { get; }
|
|
||||||
public readonly Span<byte> 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.
|
|
||||||
@@ -65,7 +65,6 @@ namespace StgSharp
|
|||||||
uint size = sz > MaxAllocSize ? MaxAllocSize : sz;
|
uint size = sz > MaxAllocSize ? MaxAllocSize : sz;
|
||||||
BenchAllocThroughput(allocator, size, touch:false);
|
BenchAllocThroughput(allocator, size, touch:false);
|
||||||
allocator.Collect();
|
allocator.Collect();
|
||||||
allocator.DumpPositionChain();
|
|
||||||
GC.Collect();
|
GC.Collect();
|
||||||
GC.WaitForPendingFinalizers();
|
GC.WaitForPendingFinalizers();
|
||||||
}
|
}
|
||||||
@@ -130,7 +129,9 @@ namespace StgSharp
|
|||||||
}
|
}
|
||||||
sw.Stop();
|
sw.Stop();
|
||||||
double opsPerSec = pairs / sw.Elapsed.TotalSeconds;
|
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(
|
private static void BenchAllocThroughput(
|
||||||
@@ -171,7 +172,8 @@ namespace StgSharp
|
|||||||
allocator.Free(h);
|
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(
|
private static void BenchFragmentationCycle(
|
||||||
@@ -244,7 +246,9 @@ namespace StgSharp
|
|||||||
|
|
||||||
sw.Stop();
|
sw.Stop();
|
||||||
double freesPerSec = count / sw.Elapsed.TotalSeconds;
|
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)
|
private static string Fmt(uint bytes)
|
||||||
|
|||||||
Reference in New Issue
Block a user