Table of Contents

KV 组合性能基线(RingOfLong × 两族索引)

数字实测于本机(i5-12400 / .NET 8.0.30 / x64 RyuJit AVX2 / BDN 0.13.12),文件系统=内存文件系统 (组合层开销口径,引擎 IO 噪声为零;本地文件系统另行对照)。

配置

  • Ring:RingOfLong(PageSize=8K / MemorySize=32MB / 全热区驱逐零干扰 / 无溢出 / meta Disabled)
  • 值 64B;点查预填 100k 条乱序命中;写=每迭代全新组合、每 invocation 批量 256 条摊平计时粒度 (OperationsPerInvoke);恢复=50k 条跨实例全量重建(W=Begin 拉流重放,无镜像)
  • 复现:
    dotnet run -c Release --project benchmarks/TC.Tier.Runtime.Benchmarks -- --filter "*KvComposition*"
    dotnet run -c Release --project benchmarks/TC.Tier.Runtime.Benchmarks -- --filter "*KvRecovery*"
    

点查(index.Find + Ring.GetValue 两段合口径,100k 条热区乱序命中)

索引族 Mean 当前机制
ProbingIndex (Hash) 161 ns 零分配;容量自适应(装载 >0.7 翻倍,函数式构建新代表 + 单引用原子发布——表缩 L3 的局部性红利)
SortedIndex (BTree) ~350 ns 零分配(节点缓存 LogicalAddressMap 扁平化 + 引擎读快路径 + 生长模式)
SortedIndex (SkipList) ~816-850 ns 零分配(NodeArena 变长驻留:工作集 6.5MB 全 L3 + 零拷贝跳链)

批量点查(终态形态,批量 256 查/invocation)index.EnterScope() 一批一进出 + IndexScope.Find + Ring.GetValueSpan(零拷贝切片;Ring 读为内容自愈读——SafeSnapshotTail 判据 + magic/CRC 校验内聚,调用方零保护零感知;溢出 record 回退 thread-static 拷贝; span 仅同步栈内合法)——94.67 ns/查(0.75× vs FASTER 2.6.5 同形热读 126.96ns,反超 1.34× 并破百)。逐次形态(每迭代进出双 scope)167.9ns,不划算。

写(Ring.Write + index.Insert 两步编排,全新组合冷启动口径)

索引族 Mean/op 当前机制
Hash 2.3-2.8 µs(批 2.05 µs,-28% 写口径含每迭代全新组合装配;稳态热写更低
BTree 3.3-4.3 µs(批 2.63 µs,-39%) 脏节点延迟写回:内容变更记脏标记、dump 时批量写引擎——插入/分裂路径零引擎写
SkipList 2.3-2.6 µs 363B/插(arena 零托管分配税);脏节点延迟写回同 BTree 模式

批量写(BeginWriteBatch:组合层大批量 key 写入的常用形态——批持锁 + epoch 独占写窗口, 批内 record 零锁零页检查。使用:using var batch = ring.BeginWriteBatch(); ... batch.Append(k, v); 地址语义与单条 Write 完全一致;单写者批语义(批期间其他写者阻塞)。

多写者无锁窗口BeginWriteBatch 批持锁改窗口领取——tail 预留整页段(批独占),批内零锁零 tail 推进。并发写吞吐(内存文件系统探针 --concurrent-write-probe):8 写者 batch 2.05M → 6.07M op/s(3.0×)、16 写者 5.90M(扩展饱和——内存带宽/CRC 并行度)、单写者 1.64M 无回归; single-write(逐条)仍 lock 串行(1.7-2.0M)——多写者收益走批窗口。索引侧:Hash 桶 CAS / SkipList 塔链 CAS 已就位——组合层多写者立得;BTree 单写者 = 当前短板(待结构层多写者协议)。

恢复(跨实例重开 Ring + 50k 条,整段墙钟)

索引族 全量重放 主存储帧/镜像载入(W=尾,零增量)* 探针分配
Hash 67-87 ms 64.7 ms 全量 2.7 MB(54B/条)(容量自适应)
BTree 106-130 ms 58.7 ms(2.2×)
SkipList 93-132 ms 49.0 ms(2.1×)
  • 持久化两形态:HashIndex 主存储(内置 TryDump() fuzzy 逐槽原子拷贝三段式帧,W = Ring 已落盘水位 GetFlushedWatermark(),后台策略自动 dump + 版本链 N 版轮替;恢复帧优先 + 只重放 (W,End]); SortedIndex 主存储(固定锚点帧 32B 几何——节点写时持久化在自持引擎、物化只设根+计数,族私有 codec 契约)。恢复核心帧优先——有效且 W∈[Begin,End] 载入 + 只重放 (W,End],无帧/损坏/越界 fail-safe 全量 同路。零增量基准只展示恢复共同底盘(ring 重开 + 引擎重开 + 载入 IO,~45-60ms/50k 量级);真收益在 W<尾的增量场景(重放量 ∝ 距尾距离,非数据总量)。三族载入零 key 回读/零结构重建(条目/塔链/ 子指针 = 地址一等公民)。

⚠️ 跨实例持久组合必须 DeleteOnClose=false——true 的引擎重开恒无帧走全量重放。

分解参考(Stopwatch 探针,100k 稳态,Hash 组合)

分解项 ns/op
Ring.Write 单独 ~906
HashIndex.Insert 单独 ~904
Ring.GetValue 单独 ~74

怎么选(现状口径)

  • 极省内存点查选 Hash:161ns 一跳命中+取值。FASTER 同形对照(2026-08-22,FasterHotReadBench, 同机同轮:FASTER 2.6.5 纯内存 hlog 100k×(8B key+64B struct value) 会话热随机 Read = 133.4ns, 本组合逐次 = 158.4ns——差距 1.19×/25ns)。25ns 差构成:两段组合的二次地址解码/就绪检查 + 桶 128B 双 cache line(FASTER 桶 64B 恰一行)+ 接口分派。批量形态反超(见上"批量点查")。 代价 = 构造期必注入 IKeyResolver(判等闭环)。
  • 地址一等公民(设计决策):16B LogicalAddress(seg/offset/ext)不可拆、不做 8B 紧凑编码—— 8B 永远无法正确解决无限地址空间(打包把几何烙进地址:段号占 N 位 ⇒ 段数 ≤2^N 且段大小 ≤2^(64−N));16B 是无限空间的正交表达,编码与几何解耦。上层自缓冲地址后直达取值永久跳过 hash——hash 路径只是发现地址的一次性成本,此后地址即句柄。
  • 有序遍历/range scan 选 Sorted:BTree ~350ns / SkipList ~816ns——BTree 点查已进亚µs; SkipList 写比 BTree 便宜(无分裂重排)。
  • 恢复速度:无镜像全量重建 50k 条 67-130ms;主存储载帧 + 增量重放 (W,End) 已落地—— 真收益在 W 距尾近的增量场景。

想深入?指路

想懂什么 去哪
结构层用法(Ring/索引装配/写读编排) ../structures.md
批量写/多写者窗口 API Ring 类型 XML 注释(Structures/Ring/
主存储帧格式/dump 协议 源码注释与设计稿(docs/design/