L4 brief ok

This commit is contained in:
Youmu Liu
2026-03-13 18:04:36 +08:00
parent 5da448548b
commit ccf95ec270
2 changed files with 29 additions and 31 deletions
@@ -63,6 +63,10 @@ namespace StgSharp.HighPerformance.Memory
{ {
ref CacheLinePrediction handle = ref span[i]; ref CacheLinePrediction handle = ref span[i];
nuint address = handle.Address; nuint address = handle.Address;
if (TestHit(address))
{
continue;
}
if (!TryGetEmptyLine(out int index)) { if (!TryGetEmptyLine(out int index)) {
throw new OverflowException("L4 has no empty cache line."); throw new OverflowException("L4 has no empty cache line.");
} }
@@ -187,7 +187,7 @@ RRPV数组使用byte存储每一个cacheline的使用情况,其低二位表示
| 时机 | 操作 | 说明 | | 时机 | 操作 | 说明 |
|---------------|-----------------------------------|-----------------------------| |---------------|-----------------------------------|-----------------------------|
| 加载时(插入) | RRPV = 0 | 刚加载的数据马上要用 | | 加载时(插入) | RRPV = 0 | 刚加载的数据马上要用 |
| 命中时 | `_evictionFlags[i] &= 0xF9` | bit 1-2 清零,RRPV 归零 | | 命中时 | `_evictionFlags[i] = 0` | bit 1-2 清零,RRPV 归零 |
| 逐出扫描时 | 找 RRPV=3 的行逐出 | 找不到则全体 RRPV+1(老化) | | 逐出扫描时 | 找 RRPV=3 的行逐出 | 找不到则全体 RRPV+1(老化) |
**相比 v0.1 的 4 级环形链表的优势**: **相比 v0.1 的 4 级环形链表的优势**:
@@ -207,39 +207,31 @@ RRPV数组使用byte存储每一个cacheline的使用情况,其低二位表示
**职责**:为可写缓存行提供灵活的写回策略,通过本机函数指针避免托管开销。 **职责**:为可写缓存行提供灵活的写回策略,通过本机函数指针避免托管开销。
**设计原则**:所有可写缓存行默认视为脏数据。逐出或显式刷新时, **设计原则**:所有可写缓存行默认视为脏数据。逐出或显式刷新时,
L4 通过注册的函数指针调用写回逻辑,调用者决定如何预处理和写回。 L4 本身不负责管理写回策略,写回策略由绑定的预测器负责实现。
预测器通过如下的接口进行定义
``` ```
写回流程: public interface IPrefetchPredict
{
逐出脏缓存行 slot[i]: int Predict(
1. 检查 dirty 位:(_evictionFlags[i] & 0x01) != 0 Span<CacheLinePrediction> node
2. 调用预处理:_preprocess[i](cacheline_addr, _callbackCtx[i]) );
→ 例如:将 Pack 格式反向重排为原始矩阵布局
3. 调用写回: _writeback[i](origin_addr, cacheline_addr, _callbackCtx[i])
→ 例如:累加 partial sum 到目标矩阵
4. 标准逐出:注销映射,归还 Slab
```
**回调签名**(本机函数指针): bool Prefetch(
nuint origin,
ulong policy,
nuint cache
);
```csharp void WriteBack(
/// <summary> nuint origin,
/// 写回预处理回调。在写回前对缓存行数据进行变换。 ulong policy,
/// 例如:将 Pack 重排的数据恢复为原始布局。 nuint cache
/// </summary> );
/// <param name="cacheLineAddr">缓存行本地地址</param>
/// <param name="context">调用者提供的上下文</param>
delegate* unmanaged<nuint, nuint, void> Preprocess;
/// <summary> }
/// 写回回调。将缓存行数据写回到目标地址。
/// 例如:将 partial sum 累加到目标矩阵。
/// </summary>
/// <param name="originAddr">原始目标地址</param>
/// <param name="cacheLineAddr">缓存行本地地址</param>
/// <param name="context">调用者提供的上下文</param>
delegate* unmanaged<nuint, nuint, nuint, void> Writeback;
``` ```
**典型回调实现**: **典型回调实现**:
@@ -396,7 +388,7 @@ private void TryRefreshPrefetch()
### 4.3 缓存未命中(只读) ### 4.3 缓存未命中(只读)
1. Swiss Table 查找未命中 1. Swiss Table 查找未命中
2. 计算线程进入 SpinWait 等待循环 2. 计算线程进入 Thread.Sleep(0) 等待循环
3. 等待 Predictor 驱动的预取刷新将该行加载进 L4 3. 等待 Predictor 驱动的预取刷新将该行加载进 L4
4. Swiss Table 出现命中后退出等待 4. Swiss Table 出现命中后退出等待
5. 构造 CacheLine 句柄并返回 5. 构造 CacheLine 句柄并返回
@@ -653,6 +645,7 @@ L4 的 Predictor 是**确定性的 Oracle**——它不预测,它知道。
### 6.3 回调驱动的写回策略 ### 6.3 回调驱动的写回策略
通过本机函数指针实现写回,避免了: 通过本机函数指针实现写回,避免了:
- 虚方法调用开销 - 虚方法调用开销
- 接口分发开销 - 接口分发开销
- 委托的 GC 压力 - 委托的 GC 压力
@@ -671,6 +664,7 @@ L4 在逐出或 Flush 时直接通过函数指针调用,
### 6.5 缓存行大小与矩阵 Kernel 对齐 ### 6.5 缓存行大小与矩阵 Kernel 对齐
8192B 的缓存行大小不是任意选取的: 8192B 的缓存行大小不是任意选取的:
- 1 个 `double MAT_KERNEL`(4×4 double 矩阵)= 128 Bytes - 1 个 `double MAT_KERNEL`(4×4 double 矩阵)= 128 Bytes
- 1 个缓存行 = 64 个 `MAT_KERNEL` - 1 个缓存行 = 64 个 `MAT_KERNEL`
- 矩阵乘法的微内核每次处理的数据量与缓存行完全对齐 - 矩阵乘法的微内核每次处理的数据量与缓存行完全对齐
@@ -719,7 +713,7 @@ L4 在逐出或 Flush 时直接通过函数指针调用,
|-----------------|-------------------------------|---------------------------------------| |-----------------|-------------------------------|---------------------------------------|
| 逐出数据结构 | 4 级环形双向链表 | RRPV 2-bit 掩码 + 8KB SoA 数组 | | 逐出数据结构 | 4 级环形双向链表 | RRPV 2-bit 掩码 + 8KB SoA 数组 |
| 逐出扫描方式 | 链表头指针遍历 | SSE2 批量模式匹配(16 行/周期) | | 逐出扫描方式 | 链表头指针遍历 | SSE2 批量模式匹配(16 行/周期) |
| 热���更新 | 链表节点摘插(4 指针写) | 单字节位运算 `&= 0xF9` | | 热���更新 | 链表节点摘插(4 指针写) | 单字节位运算 `&= 0x03` |
| 老化操作 | 逐节点移链表 | SIMD 批量 +1 | | 老化操作 | 逐节点移链表 | SIMD 批量 +1 |
| 引用保护 | 无 | refCount > 0 时 SIMD 扫描跳过 | | 引用保护 | 无 | refCount > 0 时 SIMD 扫描跳过 |
| 预取策略 | 无 | Predictor 窗口 OPT(2048 条缓冲) | | 预取策略 | 无 | Predictor 窗口 OPT(2048 条缓冲) |