Skip to content

perf: 多 shard 锁数组按 64B 对齐,修复 >8 线程吞吐断崖(10–20 线程 1.6–3.7×) - #15

Merged
carsontung666 merged 1 commit into
array2d:mainfrom
carsontung666:perf/shard-lock-cacheline
Oct 31, 2026
Merged

carsontung666 merged 1 commit into
array2d:mainfrom
carsontung666:perf/shard-lock-cacheline

Conversation

@carsontung666

Copy link
Copy Markdown
Collaborator

问题

多 shard(数据区 ≥ 128MB,64 个 shard)时,v0.1.7 的锁数组从 meta 偏移 56 + 64×48 = 3128 开始(mod 64 = 56)。sbo_lock_t 虽然填充到 64 字节,但每把锁的锁字和上一个 shard 的 hot_id1 / hot_base 落在同一条 cache line 上,而后者每次分配都会写。

home shard 按 计数 × 9 & 63 分配,8 个线程以内恰好互不相邻(0, 9, 18, …, 63)。从第 9 个线程起开始出现相邻 shard,吞吐断崖下跌:alloc+free 从 8 线程的 157 Mops/s 掉到 10 线程的 63 Mops/s。

改动

  • 多 shard 时锁数组起点按 64 字节对齐(相对 meta;mmap 得到的 meta 天然满足),头部多 8 字节,sbo_meta_size / sbo_init 同步。
  • sbo_meta_t 用原 _pad 中的 1 个字节记 layout,sizeof 仍是 56。v0.1.7 创建的多 shard meta(layout 0)由 sbo_meta_ok 拒绝(alloc 失败、free 不动作),不会被按新布局误读。
  • 单 shard 布局逐字节不变。 kvspace-c 只用单 shard,并且硬编码了 SBO_HEAD_FIXED,所以不受影响。

验证

正确性

  • ctest(Debug ASan+UBSan,不含 03_stress)10/10;18_model_check 16 50000 PASS。
  • 布局检查:单 shard 的 pool 起点仍是 168(等于 kvspace-c 的 SBO_HEAD_FIXED);64 shard 时 lock[i] mod 64 = 0,最后一个 pool 不越过 head。
  • kvspace-c(perf/big-opt 分支,out-of-tree 构建,KVSPACE_BUILD_TESTS=ON)用 main 头和本分支头各跑一遍 ctest,都是 11/11。
  • 跨版本打开同一个 meta 文件(用 v0.1.7 创建 → 用本分支打开,先查旧对象,再分配 2000 次并检查是否与旧对象重叠):
数据区 v0.1.7 → v0.1.7 v0.1.7 → 本分支 本分支 → 本分支
2MB(单 shard) 正常 正常:旧对象全部可读,新分配 118 次、0 重叠 正常
128MB(64 shard) 正常 拒绝:旧对象不可读,新分配 0 次,没有误读 正常

性能

Xeon w7-3455(24 核 48 线程,机器同时有其他负载),gcc 13.2 -O3 -DNDEBUG。数据区 128MB / 64 shard,meta 16MB,每个线程绑一个物理核。main、本分支、main 副本(A/A)交替运行 15 次取中位数。

负载 线程 main Mops/s 本分支 Mops/s 本分支/main A/A
alloc+free {8,64,512,4096}B 1 21.8 21.0 0.96 0.97
4 80.0 80.6 1.01 1.01
8 156.5 139.7 0.89 1.01
12 70.8 214.2 3.03 0.99
16 85.2 266.6 3.13 0.96
20 90.6 331.1 3.65 1.01
churn(1024 个存活对象) 1 13.7 13.5 0.98 0.98
8 109.6 106.6 0.97 0.97
12 91.4 155.7 1.70 0.97
16 106.6 202.2 1.90 1.00
20 133.6 234.8 1.76 1.02

8 线程 alloc+free 那一格的 0.89 没能复现。换一组更空闲的核、每线程 400 万次操作再测,6/8 线程的本分支/main 是 0.95–1.17,而同一组的 A/A 是 0.98–1.30,四分位区间完全重叠。≤8 线程两边的布局没有伪共享差异,这里看到的是机器噪声。≥10 线程的提升远大于噪声,四分位区间不重叠(例如 10 线程 main [59, 69],本分支 [152, 204])。

v0.1.7 的锁数组起点是 56 + 64*48 = 3128(mod 64 = 56),每把锁的锁字和
上一个 shard 的 hot_id1/hot_base 在同一条 cache line 上。home shard 按
步长 9 分配,8 线程以内恰好不相邻;从第 9 个线程起相邻 shard 同时有人
用,吞吐从 8 线程的 157 Mops/s 掉到 10 线程的 63 Mops/s。

- 多 shard 时锁数组起点对齐到 64B(相对 meta;mmap 得到的 meta 天然满足),
  头部多 8 字节,sbo_meta_size / sbo_init 同步。
- sbo_meta_t 用原 _pad 的 1 字节记 layout;v0.1.7 创建的多 shard meta
  (layout 0)由 sbo_meta_ok 拒绝,不会被误读。
- 单 shard 布局逐字节不变(kvspace-c 依赖这一点)。
@carsontung666
carsontung666 merged commit 8f96ee7 into array2d:main Oct 31, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant