diff --git a/StgSharp.Benchmark/SwissBenchmark.cs b/StgSharp.Benchmark/SwissBenchmark.cs
index b5437d64..187c86be 100644
--- a/StgSharp.Benchmark/SwissBenchmark.cs
+++ b/StgSharp.Benchmark/SwissBenchmark.cs
@@ -29,41 +29,127 @@ using BenchmarkDotNet.Attributes;
using StgSharp.Collections;
using System;
using System.Collections.Generic;
+using System.Runtime.CompilerServices;
namespace StgSharp.Benchmark
{
///
- /// Benchmark comparing SwissTable (SSE2 SIMD probing, zero-GC, native memory) against for the L4 cache address mapping workload: concentrated
- /// small-key insert/lookup/remove cycles. SwissTable.Create() performs a 1 MB memset
- /// (65536 × 16 control bytes), which is separated into IterationSetup to avoid polluting
- /// insert timings.
+ /// Benchmark comparing SwissTable against
+ /// under realistic L4 software cache address-mapping workloads.
+ ///
+ /// L4 scenario characteristics:
+ /// - Keys are HLSF/malloc pointer addresses (large, 1024-byte aligned)
+ /// - Access pattern is Zipfian-skewed (20% hot cache lines receive 80% of lookups)
+ /// - Steady state: high hit-rate lookups dominate, with rare evict+refill on miss
+ /// - Working set: 512–4096 active cache line mappings
+ ///
///
[MemoryDiagnoser]
public class SwissTableBench
{
- private const int LookupCount = 4096;
+ /// L4 cache line size in bytes (per L4_design.md).
+ private const int CacheLineSize = 1024;
- private nint[] _lookupKeys = null!;
+ /// Simulated heap base address for pointer-like keys.
+ private const long HeapBase = 0x7FFF_0000_0000L;
- // ── Pre-generated key arrays ──
+ /// Number of lookup operations per Lookup benchmark call.
+ private const int LookupIterations = 8192;
- private nint[] _preloadedKeys = null!;
+ ///
+ /// Number of steady-state operations per call.
+ /// Each op is either a lookup (hit) or an evict+insert+lookup (miss).
+ ///
+ private const int SteadyStateOps = 4096;
+
+ /// Fraction of steady-state ops that are cache misses.
+ private const double MissRate = 0.05;
+
+ // ── Pre-generated key arrays (pointer-like, 1024-byte aligned) ──
+
+ /// Keys for initial table fill (simulates cached addresses).
+ private nint[] _residentKeys = null!;
+
+ /// Additional keys not in the table, for triggering misses.
+ private nint[] _externalKeys = null!;
+
+ /// Zipfian-skewed lookup sequence (hot 20% accessed 80% of time).
+ private nint[] _zipfianLookups = null!;
+
+ /// Uniform random lookup sequence (baseline comparison).
+ private nint[] _uniformLookups = null!;
+
+ ///
+ /// Pre-built steady-state operation script.
+ /// Each entry: positive index → lookup _residentKeys[idx],
+ /// negative value → cache miss trigger (evict + insert from _externalKeys).
+ ///
+ private int[] _steadyStateScript = null!;
+
+ /// Indices into _residentKeys for eviction targets (cold entries, tail region).
+ private int[] _evictOrder = null!;
+
+ // ── Pre-filled lookup targets ──
+
+ private SwissTable _swissLookup = null!;
private Dictionary _dictLookup = null!;
- private Dictionary _freshDict = null!;
// ── Fresh instances for insert/mixed (rebuilt each iteration) ──
private SwissTable _freshSwiss = null!;
+ private Dictionary _freshDict = null!;
- // ── Lookup targets (pre-filled in GlobalSetup) ──
-
- private SwissTable _swissLookup = null!;
-
- [Params(500, 2000, 5000)]
+ ///
+ /// Active cache line count. Matches L4 capacity range:
+ /// 5 MB / 1024B ≈ 5120, 10 MB / 1024B ≈ 10240.
+ /// 512 and 1024 test low-occupancy; 4096 tests near-capacity.
+ ///
+ [Params(512, 1024, 4096)]
public int KeyCount;
+ // ════════════════════════════════════════════════════════════
+ // Setup / Teardown
+ // ════════════════════════════════════════════════════════════
+
+ [GlobalSetup]
+ public void Setup()
+ {
+ Random rng = new Random(42);
+
+ // ── Generate pointer-like keys (HeapBase + random_offset * 1024) ──
+ _residentKeys = GeneratePointerKeys(KeyCount, rng);
+ _externalKeys = GeneratePointerKeys(KeyCount, rng); // disjoint set for miss simulation
+
+ // ── Build lookup sequences ──
+ _zipfianLookups = GenerateZipfianLookups(_residentKeys, LookupIterations, rng);
+ _uniformLookups = GenerateUniformLookups(_residentKeys, LookupIterations, rng);
+
+ // ── Build steady-state operation script ──
+ _steadyStateScript = GenerateSteadyStateScript(KeyCount, SteadyStateOps, MissRate, rng);
+
+ // ── Build eviction order (cold tail entries first) ──
+ _evictOrder = new int[KeyCount];
+ for (int i = 0; i < KeyCount; i++)
+ {
+ _evictOrder[i] = i;
+ }
+ // Shuffle so eviction isn't just sequential
+ Shuffle(_evictOrder, rng);
+
+ // ── Pre-fill lookup tables ──
+ _swissLookup = SwissTable.Create();
+ _dictLookup = new Dictionary(KeyCount);
+
+ for (int i = 0; i < KeyCount; i++)
+ {
+ nint key = _residentKeys[i];
+ nint val = key ^ unchecked((nint)0xDEAD_BEEF); // simulated cache pointer
+ _swissLookup.TryAddOrSet(key, val, out _);
+ _dictLookup[key] = val;
+ }
+ }
+
[GlobalCleanup]
public void Cleanup()
{
@@ -71,119 +157,114 @@ namespace StgSharp.Benchmark
_dictLookup.TrimExcess();
}
- // ══════════════════════════════════════
- // Insert: pure write throughput
- // Empty table → fill N keys
- // Create() cost excluded via IterationSetup
- // ══════════════════════════════════════
-
- [Benchmark]
- public void Dictionary_Insert()
- {
- Dictionary dict = _freshDict;
- nint[] keys = _preloadedKeys;
- for (int i = 0; i < keys.Length; i++) {
- dict[keys[i]] = keys[i] * 2;
- }
- }
-
- // ══════════════════════════════════════
- // Lookup: pure read throughput
- // Pre-filled table, random key access
- // ══════════════════════════════════════
-
- [Benchmark(Baseline = true)]
- public nint Dictionary_Lookup()
- {
- nint sum = 0;
- nint[] keys = _lookupKeys;
- Dictionary dict = _dictLookup;
- for (int i = 0; i < keys.Length; i++)
- {
- if (dict.TryGetValue(keys[i], out nint value)) {
- sum += value;
- }
- }
- return sum;
- }
-
- // ══════════════════════════════════════
- // Mixed: insert all → lookup half → remove half → lookup all
- // Simulates L4 cache lifecycle:
- // load working set → compute → evict → recompute
- // ══════════════════════════════════════
-
- [Benchmark]
- public nint Dictionary_Mixed()
- {
- Dictionary dict = _freshDict;
- nint[] keys = _preloadedKeys;
- nint sum = 0;
-
- // insert all
- for (int i = 0; i < keys.Length; i++)
- {
- dict[keys[i]] = keys[i] * 2;
- }
-
- // lookup first half
- for (int i = 0; i < keys.Length / 2; i++)
- {
- if (dict.TryGetValue(keys[i], out nint value)) {
- sum += value;
- }
- }
-
- // remove first half (eviction)
- for (int i = 0; i < keys.Length / 2; i++)
- {
- dict.Remove(keys[i]);
- }
-
- // lookup all (half hit, half miss)
- for (int i = 0; i < keys.Length; i++)
- {
- if (dict.TryGetValue(keys[i], out nint value)) {
- sum += value;
- }
- }
-
- return sum;
- }
-
[IterationSetup(Targets = [
nameof(SwissTable_Insert),
- nameof(SwissTable_Mixed),
+ nameof(SwissTable_SteadyState),
nameof(Dictionary_Insert),
- nameof(Dictionary_Mixed)
+ nameof(Dictionary_SteadyState)
])]
public void IterationSetup()
{
_freshSwiss = SwissTable.Create();
_freshDict = new Dictionary(KeyCount);
- }
-
- // ── Setup / Teardown ──
-
- [GlobalSetup]
- public void Setup()
- {
- _preloadedKeys = new nint[KeyCount];
- _lookupKeys = new nint[LookupCount];
-
- _swissLookup = SwissTable.Create();
- _dictLookup = new Dictionary(KeyCount);
+ // Pre-fill for steady-state benchmarks
for (int i = 0; i < KeyCount; i++)
{
- _preloadedKeys[i] = (nint)i;
- _swissLookup.TryAddOrSet((nint)i, (nint)(i * 2), out _);
- _dictLookup[(nint)i] = (nint)(i * 2);
+ nint key = _residentKeys[i];
+ nint val = key ^ unchecked((nint)0xDEAD_BEEF);
+ _freshSwiss.TryAddOrSet(key, val, out _);
+ _freshDict[key] = val;
}
+ }
- Random rng = new Random(42);
- for (int i = 0; i < LookupCount; i++) {
- _lookupKeys[i] = (nint)rng.Next(0, KeyCount);
+ // ════════════════════════════════════════════════════════════
+ // Lookup — Zipfian (L4 hot path, skewed access)
+ // 20% of cache lines receive 80% of accesses.
+ // This is the most representative L4 benchmark.
+ // ════════════════════════════════════════════════════════════
+
+ [Benchmark(Baseline = true)]
+ public nint Dictionary_Lookup_Zipfian()
+ {
+ nint sum = 0;
+ nint[] keys = _zipfianLookups;
+ Dictionary dict = _dictLookup;
+ for (int i = 0; i < keys.Length; i++)
+ {
+ if (dict.TryGetValue(keys[i], out nint value))
+ {
+ sum += value;
+ }
+ }
+ return sum;
+ }
+
+ [Benchmark]
+ public nint SwissTable_Lookup_Zipfian()
+ {
+ nint sum = 0;
+ nint[] keys = _zipfianLookups;
+ SwissTable swiss = _swissLookup;
+ for (int i = 0; i < keys.Length; i++)
+ {
+ if (swiss.TryGet(keys[i], out nint value))
+ {
+ sum += value;
+ }
+ }
+ return sum;
+ }
+
+ // ════════════════════════════════════════════════════════════
+ // Lookup — Uniform (baseline comparison)
+ // ════════════════════════════════════════════════════════════
+
+ [Benchmark]
+ public nint Dictionary_Lookup_Uniform()
+ {
+ nint sum = 0;
+ nint[] keys = _uniformLookups;
+ Dictionary dict = _dictLookup;
+ for (int i = 0; i < keys.Length; i++)
+ {
+ if (dict.TryGetValue(keys[i], out nint value))
+ {
+ sum += value;
+ }
+ }
+ return sum;
+ }
+
+ [Benchmark]
+ public nint SwissTable_Lookup_Uniform()
+ {
+ nint sum = 0;
+ nint[] keys = _uniformLookups;
+ SwissTable swiss = _swissLookup;
+ for (int i = 0; i < keys.Length; i++)
+ {
+ if (swiss.TryGet(keys[i], out nint value))
+ {
+ sum += value;
+ }
+ }
+ return sum;
+ }
+
+ // ════════════════════════════════════════════════════════════
+ // Insert — bulk fill with scattered pointer addresses
+ // Simulates L4 initial cache warm-up phase.
+ // ════════════════════════════════════════════════════════════
+
+ [Benchmark]
+ public void Dictionary_Insert()
+ {
+ Dictionary dict = _freshDict;
+ nint[] keys = _residentKeys;
+ for (int i = 0; i < keys.Length; i++)
+ {
+ dict[keys[i]] = keys[i] ^ unchecked((nint)0xDEAD_BEEF);
}
}
@@ -191,64 +272,235 @@ namespace StgSharp.Benchmark
public void SwissTable_Insert()
{
SwissTable swiss = _freshSwiss;
- nint[] keys = _preloadedKeys;
- for (int i = 0; i < keys.Length; i++) {
- swiss.TryAddOrSet(keys[i], keys[i] * 2, out _);
+ nint[] keys = _residentKeys;
+ for (int i = 0; i < keys.Length; i++)
+ {
+ swiss.TryAddOrSet(keys[i], keys[i] ^ unchecked((nint)0xDEAD_BEEF), out _);
}
}
+ // ════════════════════════════════════════════════════════════
+ // SteadyState — the KEY L4 benchmark
+ // Pre-filled table at capacity, then 4096 operations:
+ // ~95% are lookups (cache hits)
+ // ~5% are miss → evict cold entry → insert new entry → lookup new
+ // This directly simulates L4's main runtime loop.
+ // ════════════════════════════════════════════════════════════
+
[Benchmark]
- public nint SwissTable_Lookup()
+ public nint Dictionary_SteadyState()
{
nint sum = 0;
- nint[] keys = _lookupKeys;
- SwissTable swiss = _swissLookup;
- for (int i = 0; i < keys.Length; i++)
+ Dictionary dict = _freshDict;
+ nint[] resident = _residentKeys;
+ nint[] external = _externalKeys;
+ int[] script = _steadyStateScript;
+ int[] evict = _evictOrder;
+ int evictCursor = 0;
+ int externalCursor = 0;
+
+ for (int i = 0; i < script.Length; i++)
{
- if (swiss.TryGet(keys[i], out nint value)) {
- sum += value;
+ int op = script[i];
+ if (op >= 0)
+ {
+ // Cache hit: lookup existing entry
+ if (dict.TryGetValue(resident[op], out nint value))
+ {
+ sum += value;
+ }
+ }
+ else
+ {
+ // Cache miss: evict a cold entry, insert a new one, then lookup
+ int evictIdx = evict[evictCursor % evict.Length];
+ evictCursor++;
+ dict.Remove(resident[evictIdx]);
+
+ int extIdx = externalCursor % external.Length;
+ externalCursor++;
+ nint newKey = external[extIdx];
+ nint newVal = newKey ^ unchecked((nint)0xDEAD_BEEF);
+ dict[newKey] = newVal;
+
+ if (dict.TryGetValue(newKey, out nint v))
+ {
+ sum += v;
+ }
}
}
return sum;
}
[Benchmark]
- public nint SwissTable_Mixed()
+ public nint SwissTable_SteadyState()
{
+ nint sum = 0;
SwissTable swiss = _freshSwiss;
- nint[] keys = _preloadedKeys;
- nint sum = 0;
+ nint[] resident = _residentKeys;
+ nint[] external = _externalKeys;
+ int[] script = _steadyStateScript;
+ int[] evict = _evictOrder;
+ int evictCursor = 0;
+ int externalCursor = 0;
- // insert all
- for (int i = 0; i < keys.Length; i++)
+ for (int i = 0; i < script.Length; i++)
{
- swiss.TryAddOrSet(keys[i], keys[i] * 2, out _);
- }
+ int op = script[i];
+ if (op >= 0)
+ {
+ // Cache hit: lookup existing entry
+ if (swiss.TryGet(resident[op], out nint value))
+ {
+ sum += value;
+ }
+ }
+ else
+ {
+ // Cache miss: evict a cold entry, insert a new one, then lookup
+ int evictIdx = evict[evictCursor % evict.Length];
+ evictCursor++;
+ swiss.TryRemove(resident[evictIdx], out _);
- // lookup first half
- for (int i = 0; i < keys.Length / 2; i++)
- {
- if (swiss.TryGet(keys[i], out nint value)) {
- sum += value;
+ int extIdx = externalCursor % external.Length;
+ externalCursor++;
+ nint newKey = external[extIdx];
+ nint newVal = newKey ^ unchecked((nint)0xDEAD_BEEF);
+ swiss.TryAddOrSet(newKey, newVal, out _);
+
+ if (swiss.TryGet(newKey, out nint v))
+ {
+ sum += v;
+ }
}
}
-
- // remove first half (eviction)
- for (int i = 0; i < keys.Length / 2; i++)
- {
- swiss.TryRemove(keys[i], out _);
- }
-
- // lookup all (half hit, half miss)
- for (int i = 0; i < keys.Length; i++)
- {
- if (swiss.TryGet(keys[i], out nint value)) {
- sum += value;
- }
- }
-
return sum;
}
+ // ════════════════════════════════════════════════════════════
+ // Data generation helpers
+ // ════════════════════════════════════════════════════════════
+
+ ///
+ /// Generate unique pointer-like keys.
+ /// Each key = HeapBase + randomOffset * CacheLineSize,
+ /// simulating HLSF-allocated 1024-byte aligned addresses
+ /// scattered across a ~1 GB virtual address range.
+ ///
+ private static nint[] GeneratePointerKeys(
+ int count,
+ Random rng
+ )
+ {
+ nint[] keys = new nint[count];
+ HashSet used = new HashSet(count);
+ for (int i = 0; i < count; i++)
+ {
+ long offset;
+ do
+ {
+ // Random offset within ~1M cache lines (1 GB range)
+ offset = (long)rng.Next(0, 1 << 20) * CacheLineSize;
+ } while (!used.Add(offset));
+ keys[i] = (nint)(HeapBase + offset);
+ }
+ return keys;
+ }
+
+ ///
+ /// Generate Zipfian-skewed lookup sequence:
+ /// 80% of accesses hit the hottest 20% of keys (sorted by index as "hotness proxy"),
+ /// 20% of accesses hit the remaining 80%.
+ /// This models matrix computation where a small panel working set is reused heavily.
+ ///
+ private static nint[] GenerateZipfianLookups(
+ nint[] sourceKeys,
+ int count,
+ Random rng
+ )
+ {
+ int hotSetSize = Math.Max(1, sourceKeys.Length / 5);
+ nint[] lookups = new nint[count];
+ for (int i = 0; i < count; i++)
+ {
+ if (rng.NextDouble() < 0.8)
+ {
+ lookups[i] = sourceKeys[rng.Next(0, hotSetSize)];
+ }
+ else
+ {
+ lookups[i] = sourceKeys[rng.Next(hotSetSize, sourceKeys.Length)];
+ }
+ }
+ return lookups;
+ }
+
+ ///
+ /// Generate uniform random lookup sequence (baseline for comparison with Zipfian).
+ ///
+ private static nint[] GenerateUniformLookups(
+ nint[] sourceKeys,
+ int count,
+ Random rng
+ )
+ {
+ nint[] lookups = new nint[count];
+ for (int i = 0; i < count; i++)
+ {
+ lookups[i] = sourceKeys[rng.Next(0, sourceKeys.Length)];
+ }
+ return lookups;
+ }
+
+ ///
+ /// Generate a steady-state operation script.
+ /// Each entry: non-negative value = index into _residentKeys (lookup/hit),
+ /// negative value (-1) = cache miss trigger (evict → insert → lookup).
+ /// Lookup indices are Zipfian-skewed.
+ ///
+ private static int[] GenerateSteadyStateScript(
+ int keyCount,
+ int opCount,
+ double missRate,
+ Random rng
+ )
+ {
+ int hotSetSize = Math.Max(1, keyCount / 5);
+ int[] script = new int[opCount];
+ for (int i = 0; i < opCount; i++)
+ {
+ if (rng.NextDouble() < missRate)
+ {
+ script[i] = -1; // miss
+ }
+ else
+ {
+ // Zipfian hit
+ if (rng.NextDouble() < 0.8)
+ {
+ script[i] = rng.Next(0, hotSetSize);
+ }
+ else
+ {
+ script[i] = rng.Next(hotSetSize, keyCount);
+ }
+ }
+ }
+ return script;
+ }
+
+ /// Fisher-Yates shuffle.
+ private static void Shuffle(
+ int[] array,
+ Random rng
+ )
+ {
+ for (int i = array.Length - 1; i > 0; i--)
+ {
+ int j = rng.Next(0, i + 1);
+ (array[i], array[j]) = (array[j], array[i]);
+ }
+ }
+
}
}
\ No newline at end of file
diff --git a/StgSharp.Common/Collections/SwissTable.cs b/StgSharp.Common/Collections/SwissTable.cs
index 844839dd..96e41d46 100644
--- a/StgSharp.Common/Collections/SwissTable.cs
+++ b/StgSharp.Common/Collections/SwissTable.cs
@@ -2,8 +2,8 @@
// -----------------------------------------------------------------------
// file="SwissTable"
// Project: StgSharp
-// AuthorGroup: Nitload
-// Copyright (c) Nitload. All rights reserved.
+// AuthorGroup: Nitload Space
+// Copyright (c) Nitload Space. 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
@@ -43,14 +43,12 @@ namespace StgSharp.Collections
private const int BucketCount = 1 << EntryMaskWidth;
private const int BucketSize = 16;
private const uint CrcMagic = 0xffff9961; // Magic number for CRC32C, used in hash computation
- private const int EntryMaskWidth = 16; // 65536 entries total, 16 per bucket
+ private const int EntryMaskWidth = 10; // 1024 entries total, 16 per bucket
public const int MaxCapacity = BucketCount * BucketSize; // 256 entries per bucket, 1024 buckets
private readonly Entry* _entries;
- private SwissTable(
- Entry* buffer
- )
+ private SwissTable(Entry* buffer)
{
_entries = buffer;
}
@@ -66,11 +64,7 @@ namespace StgSharp.Collections
return new SwissTable(buffer);
}
- public bool TryAddOrSet(
- nint key,
- nint value,
- out bool isNewKey
- )
+ public bool TryAddOrSet(nint key, nint value, out bool isNewKey)
{
uint hash = BitOperations.Crc32C(CrcMagic, (ulong)key);
ushort entry = (ushort)(hash & (BucketCount - 1));
@@ -78,10 +72,7 @@ namespace StgSharp.Collections
return IBucketScanner.Current.TrySet(key, hash, bucket, value, out isNewKey);
}
- public bool TryGet(
- nint key,
- out nint value
- )
+ public bool TryGet(nint key, out nint value)
{
uint hash = BitOperations.Crc32C(CrcMagic, (ulong)key);
ushort entry = (ushort)(hash & (BucketCount - 1));
@@ -96,10 +87,7 @@ namespace StgSharp.Collections
*/
}
- public bool TryRemove(
- nint key,
- out nint value
- )
+ public bool TryRemove(nint key, out nint value)
{
uint hash = BitOperations.Crc32C(CrcMagic, (ulong)key);
ushort entry = (ushort)(hash & (BucketCount - 1));
@@ -134,27 +122,11 @@ namespace StgSharp.Collections
static IBucketScanner Current { get; } = Create();
- bool TryFind(
- nint key,
- uint Hash,
- Bucket* bucket,
- out nint value
- );
+ bool TryFind(nint key, uint Hash, Bucket* bucket, out nint value);
- unsafe bool TryRemove(
- nint key,
- uint hash,
- Bucket* bucket,
- out nint value
- );
+ unsafe bool TryRemove(nint key, uint hash, Bucket* bucket, out nint value);
- bool TrySet(
- nint key,
- uint hash,
- Bucket* bucket,
- nint value,
- out bool isNewKey
- );
+ bool TrySet(nint key, uint hash, Bucket* bucket, nint value, out bool isNewKey);
private static IBucketScanner Create()
{
@@ -197,4 +169,4 @@ namespace StgSharp.Collections
}
}
-}
\ No newline at end of file
+}
diff --git a/StgSharp.Common/HighPerformance/Memory/L4.cs b/StgSharp.Common/HighPerformance/Memory/L4.cs
new file mode 100644
index 00000000..7d951ea9
--- /dev/null
+++ b/StgSharp.Common/HighPerformance/Memory/L4.cs
@@ -0,0 +1,59 @@
+//-----------------------------------------------------------------------
+// -----------------------------------------------------------------------
+// file="L4"
+// Project: StgSharp
+// AuthorGroup: Nitload Space
+// Copyright (c) Nitload Space. 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 StgSharp.Collections;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace StgSharp.HighPerformance.Memory
+{
+ public unsafe class L4
+ {
+
+ private readonly byte* _buffer;
+ private readonly SwissTable _map;
+
+ public nint MapMemory(nint address)
+ {
+ if (_map.TryGet(address, out nint ptr))
+ {
+ return ptr;
+ } else
+ {
+ /*
+ * register a request for memory mapping,
+ * and return a pointer to the mapped memory.
+ */
+ return 0;
+ }
+ }
+
+ }
+}
diff --git a/StgSharp.Common/HighPerformance/Memory/L4_design.md b/StgSharp.Common/HighPerformance/Memory/L4_design.md
index 103c87e4..9464b8ee 100644
--- a/StgSharp.Common/HighPerformance/Memory/L4_design.md
+++ b/StgSharp.Common/HighPerformance/Memory/L4_design.md
@@ -77,8 +77,8 @@ L4 缓存器是一个纯软件实现的**线程本地数据缓存层**,
| 参数 | 值 | 说明 |
|-------------------|-----------------------|-----------------------------------|
| 缓存区总容量 | 5-10 MB | 线程本地连续内存 |
-| 缓存行大小 | 1024 Bytes | = 8 个 `double MAT_KERNEL` 的大小 |
-| 映射表类型 | Swiss Table | 1024 桶 × 256 槽,SIMD 加速 |
+| 缓存行大小 | 8192 Bytes | = 64 个 `double MAT_KERNEL` 的大小 |
+| 映射表类型 | Swiss Table | 65536 桶 × 16 槽,SIMD 加速 |
| 缓存行存储池 | Slab Allocator | 非并发、固定容量、不自动扩容 |
| 逐出策略 | 4 级热度环形链表 | 基于访问频率的近似 LFU |
| 写回策略 | 无(只读缓存) | 逐出时直接丢弃,不写回远端 |