diff --git a/StgSharp.Common/Collections/UnmanagedArray.cs b/StgSharp.Common/Collections/UnmanagedArray.cs
new file mode 100644
index 00000000..8df56eef
--- /dev/null
+++ b/StgSharp.Common/Collections/UnmanagedArray.cs
@@ -0,0 +1,176 @@
+//-----------------------------------------------------------------------
+// -----------------------------------------------------------------------
+// file="UnmanagedArray"
+// 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.Diagnostics;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace StgSharp.Common.Collections
+{
+ ///
+ /// Zero-overhead view over a contiguous region of unmanaged memory. Does not own the memory —
+ /// caller is responsible for lifetime management.
+ ///
+ ///
+ /// Element type. Must be unmanaged.
+ ///
+ public readonly unsafe struct UnmanagedArray where T : unmanaged
+ {
+
+ private static readonly nint ElementSize = sizeof(T);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public UnmanagedArray(
+ T* pointer,
+ int length
+ )
+ {
+ Address = pointer;
+ Length = length;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public UnmanagedArray(
+ nuint address,
+ int length
+ )
+ {
+ Address = (T*)address;
+ Length = length;
+ }
+
+ ///
+ /// Indexed access. No bounds check in Release builds.
+ ///
+ public ref T this[
+ int index
+ ]
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get
+ {
+ Debug.Assert((uint)index < (uint)Length,
+ "UnmanagedArray index out of range");
+ return ref Address[index];
+ }
+ }
+
+ ///
+ /// Raw pointer to the first element.
+ ///
+ public T* Address
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get;
+ init;
+ }
+
+ ///
+ /// Element count.
+ ///
+ public int Length
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get;
+ init;
+ }
+
+ ///
+ /// Total byte size of the array (for allocation calculations).
+ ///
+ public int ByteSize => Length * sizeof(T);
+
+ ///
+ /// Wraps the array as a .
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Span AsSpan()
+ {
+ return new Span(Address, Length);
+ }
+
+ ///
+ /// Wraps a sub-range as a .
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Span AsSpan(
+ int start,
+ int length
+ )
+ {
+ Debug.Assert((uint)start + (uint)length <= (uint)Length,
+ "UnmanagedArray.AsSpan range out of bounds");
+ return new Span(Address + start, length);
+ }
+
+ ///
+ /// Zero-fill all elements.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Clear()
+ {
+ Unsafe.InitBlock(Address, 0, (uint)(Length * sizeof(T)));
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public int IndexOf(
+ T* ptr
+ )
+ {
+ int offset = (int)(((nint)ptr - (nint)Address) / ElementSize);
+ ArgumentOutOfRangeException.ThrowIfNegative(offset, nameof(ptr));
+ ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(offset, Length, nameof(ptr));
+ return offset;
+ }
+
+ ///
+ /// Returns a pointer to element at . Useful for feeding SIMD
+ /// load instructions (e.g. Sse2.LoadVector128).
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public T* PtrAt(
+ int index
+ )
+ {
+ Debug.Assert((uint)index < (uint)Length,
+ "UnmanagedArray.PtrAt index out of range");
+ return Address + index;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public int UncheckedIndexOf(
+ T* ptr
+ )
+ {
+ return (int)(((nint)ptr - (nint)Address) / ElementSize);
+ }
+
+ }
+}
diff --git a/StgSharp.Common/HighPerformance/Memory/L4.InternalOp.cs b/StgSharp.Common/HighPerformance/Memory/L4.InternalOp.cs
new file mode 100644
index 00000000..55f703f5
--- /dev/null
+++ b/StgSharp.Common/HighPerformance/Memory/L4.InternalOp.cs
@@ -0,0 +1,81 @@
+//-----------------------------------------------------------------------
+// -----------------------------------------------------------------------
+// file="L4.InternalOp"
+// 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.Runtime.Intrinsics;
+using System.Runtime.Intrinsics.X86;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace StgSharp.HighPerformance.Memory
+{
+ public partial class L4
+ {
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private unsafe void AgeAll()
+ {
+ Vector128 one = Vector128.Create((byte)0x01);
+ Vector128 max = Vector128.Create((byte)0x03);
+ byte* p = (byte*)_eviction;
+ for (int i = 0; i < CacheLineCount; i += 16)
+ {
+ Vector128 v = Sse2.LoadVector128(p + i);
+ v = Sse2.AddSaturate(v, one);
+ v = Sse2.Min(v, max);
+ Sse2.Store(p + i, v);
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private unsafe void Evict(
+ int count = 1
+ )
+ {
+ int remain = count;
+ Vector128 one = Vector128.Create((byte)0x01);
+ Vector128 three = Vector128.Create((byte)0x03);
+ byte* p = (byte*)_eviction;
+ for (int i = 0; i < CacheLineCount && remain > 0; i += 16)
+ {
+ Vector128 v = Sse2.LoadVector128(p + i);
+ Vector128 mask = Sse2.CompareEqual(v, three);
+ int evictMask = Sse2.MoveMask(mask);
+ while (evictMask != 0 && remain > 0)
+ {
+ int index = BitOperations.TrailingZeroCount(evictMask);
+ evictMask &= (evictMask - 1);
+ EvictLine(i + index);
+ remain--;
+ }
+ }
+ }
+
+ }
+}
diff --git a/StgSharp.Common/HighPerformance/Memory/L4.Prefetch.cs b/StgSharp.Common/HighPerformance/Memory/L4.Prefetch.cs
index 31817265..bb1838e5 100644
--- a/StgSharp.Common/HighPerformance/Memory/L4.Prefetch.cs
+++ b/StgSharp.Common/HighPerformance/Memory/L4.Prefetch.cs
@@ -45,6 +45,7 @@ namespace StgSharp.HighPerformance.Memory
Span span = new(_prediction, PredictionCount);
int actualCount = PrefetchPredict.Predict(span);
int missIndex = 0;
+ AgeAll();
for (int i = 0; i < actualCount; i++)
{
ref CacheLinePrediction handle = ref span[i];
@@ -62,6 +63,9 @@ namespace StgSharp.HighPerformance.Memory
{
ref CacheLinePrediction handle = ref span[i];
nuint address = handle.Address;
+ nuint mapPtr = _cacheLineAllocator.Allocate();
+ CacheLineHead* head = (CacheLineHead*)_entryAllocator.Allocate();
+
// pre fetch them here
}
diff --git a/StgSharp.Common/HighPerformance/Memory/L4_design.md b/StgSharp.Common/HighPerformance/Memory/L4_design.md
index 66a26414..5d24baf9 100644
--- a/StgSharp.Common/HighPerformance/Memory/L4_design.md
+++ b/StgSharp.Common/HighPerformance/Memory/L4_design.md
@@ -171,15 +171,7 @@ SoA 布局 — 平行数组
**逐出标志位布局**(`_evictionFlags[i]`):
-```
- bit: 7 6 5 4 3 2 1 0
- ───────── ─ ───── ─
- 保留 C RRPV D
-
- D = bit 0 = dirty(0=只读副本, 1=有脏数据待写回)
- RRPV = bit 1-2 = Re-Reference Prediction Value(0~3)
- C = bit 3 = costClass(0=低成本, 1=高成本)
-```
+RRPV数组使用byte存储每一个cacheline的使用情况,其低二位表示如下逻辑
**RRPV 语义**:
@@ -203,7 +195,7 @@ SoA 布局 — 平行数组
| 对比项 | v0.1 环形链表 | v0.3 RRPV 掩码 + 回调 |
|---------------|------------------------|------------------------------|
| 数据结构 | 4 条双向链表 + next 指针 | byte 数组 + 函数指针数组 |
-| 升温操作 | 摘节点 + 插节点(4 指针)| `&= 0xF9`(1 字节写入) |
+| 升温操作 | 摘节点 + 插节点(4 指针)| `+= 1`(1 字节写入) |
| 老化操作 | 逐节点移链表 | SIMD 批量 +1(16 个/周期) |
| 逐出扫描 | 遍历链表头(指针追逐) | SIMD 模式匹配(16 个/周期) |
| 缓存友好性 | 差(指针跳跃) | 好(连续内存扫描) |
diff --git a/StgSharp.Common/Timing/StgSharpTime.cs b/StgSharp.Common/Timing/StgSharpTime.cs
index 61308443..440da94a 100644
--- a/StgSharp.Common/Timing/StgSharpTime.cs
+++ b/StgSharp.Common/Timing/StgSharpTime.cs
@@ -1,4 +1,4 @@
-//-----------------------------------------------------------------------
+//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="StgSharpTime"
// Project: StgSharp
@@ -54,7 +54,10 @@ namespace StgSharp
public static StgSharpTime OnlyInstance { get; } = new StgSharpTime();
- public override long GetCurrentTimeSpanTick() => mainProvider.ElapsedTicks;
+ public override long GetCurrentTimeSpanTick()
+ {
+ return mainProvider.ElapsedTicks;
+ }
public sealed override void StartProvidingTime()
{
@@ -85,12 +88,16 @@ namespace StgSharp
}
}
- protected sealed override void AddSubscriberUnsynced(TimeSpanProvider subscriber)
+ protected sealed override void AddSubscriberUnsynced(
+ TimeSpanProvider subscriber
+ )
{
_subscribers.Add(subscriber);
}
- protected sealed override void RemoveSubscriberUnsynced(TimeSpanProvider subscriber)
+ protected sealed override void RemoveSubscriberUnsynced(
+ TimeSpanProvider subscriber
+ )
{
_subscribers.Remove(subscriber);
}
diff --git a/StgSharp.Common/Timing/TimeSequence.cs b/StgSharp.Common/Timing/TimeSequence.cs
index 95b06554..970b4c6c 100644
--- a/StgSharp.Common/Timing/TimeSequence.cs
+++ b/StgSharp.Common/Timing/TimeSequence.cs
@@ -63,8 +63,11 @@ namespace StgSharp.Common.Timing
double totalSeconds,
double growthMultiplier,
double minSliceSeconds
- ) => new LogTimeSequence(initialSliceSeconds, totalSeconds, growthMultiplier,
- minSliceSeconds);
+ )
+ {
+ return new LogTimeSequence(initialSliceSeconds, totalSeconds, growthMultiplier,
+ minSliceSeconds);
+ }
public abstract bool EndsAt(
long currentTick
@@ -104,7 +107,10 @@ namespace StgSharp.Common.Timing
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool EndsAt(
long currentTick
- ) => currentTick >= EndingTick;
+ )
+ {
+ return currentTick >= EndingTick;
+ }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool IsNextFrameReady(
@@ -149,7 +155,7 @@ namespace StgSharp.Common.Timing
private double _nextBoundarySecondsAdjusted;
private readonly double _totalSeconds;
private int _sliceIndex;
- private long _freq;
+ private long _frequency;
private long _nextBoundaryTick;
private long _totalTicks;
@@ -181,7 +187,7 @@ namespace StgSharp.Common.Timing
long currentTick
)
{
- return currentTick >= EndingTick;
+ return currentTick >= EndingTick;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -239,7 +245,7 @@ namespace StgSharp.Common.Timing
TimeSourceProviderBase source
)
{
- _freq = source?.Frequency ?? throw new ArgumentNullException(nameof(source));
+ _frequency = source?.Frequency ?? throw new ArgumentNullException(nameof(source));
BeginningTick = source.GetCurrentTimeSpanTick();
_totalTicks = SecondsToTicks(_totalSeconds);
EndingTick = BeginningTick + _totalTicks;
@@ -254,7 +260,10 @@ namespace StgSharp.Common.Timing
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private long SecondsToTicks(
double seconds
- ) => (long)Math.Max(1L, Math.Round(seconds * _freq, MidpointRounding.AwayFromZero));
+ )
+ {
+ return (long)Math.Max(1L, Math.Round(seconds * _frequency, MidpointRounding.AwayFromZero));
+ }
}
@@ -278,7 +287,10 @@ namespace StgSharp.Common.Timing
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool EndsAt(
long currentTick
- ) => false; // never ends
+ )
+ {
+ return false; // never ends
+ }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public override bool IsNextFrameReady(
diff --git a/StgSharp.Common/Timing/TimeSpanProvider.cs b/StgSharp.Common/Timing/TimeSpanProvider.cs
index f847b1f5..33d390ef 100644
--- a/StgSharp.Common/Timing/TimeSpanProvider.cs
+++ b/StgSharp.Common/Timing/TimeSpanProvider.cs
@@ -1,4 +1,4 @@
-//-----------------------------------------------------------------------
+//-----------------------------------------------------------------------
// -----------------------------------------------------------------------
// file="TimeSpanProvider"
// Project: StgSharp
@@ -40,7 +40,7 @@ namespace StgSharp.Timing
{
// private bool _subscribed;
- private int _emittedSpanCount; // legacy counter
+ private readonly int _emittedSpanCount; // legacy counter
// internal ended flag (0 = running, 1 = ended)
private int _endedFlag;
@@ -50,7 +50,8 @@ namespace StgSharp.Timing
public TimeSpanProvider(
TimeSequence sequence,
TimeSourceProviderBase provider,
- int maxWaitCount = 1)
+ int maxWaitCount = 1
+ )
{
ArgumentNullException.ThrowIfNull(provider);
ArgumentNullException.ThrowIfNull(sequence);
@@ -78,17 +79,25 @@ namespace StgSharp.Timing
///
/// Current emitted time in seconds relative to the sequence beginning.
///
- public double CurrentSecond
- {
- get => (double)(_sequence.PreviousFrameTick - _sequence.BeginningTick) / _timeSource.Frequency;
- }
+ public double CurrentSecond => (double)(_sequence.PreviousFrameTick - _sequence.BeginningTick) / _timeSource.Frequency;
public int CurrentSpan => _emittedSpanCount;
public static TimeSourceProviderBase DefaultProvider => World.MainTimeProvider;
- public static explicit operator int(TimeSpanProvider provider) => provider?._emittedSpanCount ??
- 0;
+ public static int ToInt32(
+ TimeSpanProvider provider
+ )
+ {
+ return provider?._emittedSpanCount ?? 0;
+ }
+
+ public static explicit operator int(
+ TimeSpanProvider provider
+ )
+ {
+ return provider?._emittedSpanCount ?? 0;
+ }
}
-}
\ No newline at end of file
+}