AWS 32コア物理環境における vmemkv と RocksDB/RocksDB-BlobDB/LMDB の性能比較ベンチマーク。08/10時点比、本ラウンドの主要変更は2件。 まずScanBaseSequentialの base 領域読み取りを、単一の固定 madvise 方針から、レコードごとの埋め込みサイズヒントで選ぶ3つの経路 (base_mmap_scan_seq: mmap + MADV_SEQUENTIAL、1ページ以下のレコード用 / base_mmap_scan: mmap + カーネルのデフォルトreadahead、それより大きいレコード用 / read_fd: 境界を絞ったpread()、Getの大レコード・非residentケース用)に置き換え、 1つのマッピングを全レコードサイズで共用していた旧設計が引き起こしていた1KB LTM Scanの退行(0.40〜0.78x)を解消(64KB LTMのGet/Hitも2.0〜3.3x、Scan(Zipf)も2.2x改善)。 次に、この3経路化の過程でget_impl()の小レコード(1ページ以下)向けbase領域読み取りが、誤ってScan専用の base_mmap_scan_seq(MADV_SEQUENTIAL)マッピングを再利用してしまっていたバグを発見・修正。 Getは本質的にランダムアクセスなので、実LTMスワップ圧下でMADV_SEQUENTIALの広いreadahead+drop-behindが1 major faultあたりのカーネル時間を約10倍に増幅させていた (perf stat実測: 117us vs 11.2us)。既にMADV_RANDOMを持つプライマリのbaseマッピングへ 直接読みに変更(seqlock無し、base-region不変性を利用)することで解消し、1KB LTMのGet/HitがZipf 5.2倍・Uniform 6.5倍改善、+Prefaultと同等かむしろ上回る水準まで回復。 併せてtry_scan_base_record()/try_get_base_record()を単一の try_read_base_record()エントリポイントへ統合し、マッピング選択方針をBaseReader switch文の1箇所に集約(今回のような取り違えの再発防止)。 In-Memoryや他バリアントへの退行は確認されなかった。詳細はvmemkv_impl.hpptry_read_base_record()のコメントを参照。 本レポートはこの修正を含む本番マトリクス全体(In-Memory/LTM × 8B/1KB/64KB × 全ストア)を、4台の独立したAWSスポットインスタンスに分割並列実行して取得した最新結果。

Stacking Variants

各最適化の詳細は Design Document を参照。

  • RocksDB: LSM-Tree のベースライン比較
  • LMDB: B+Tree / mmap ベースの比較対象
  • RocksDB-BlobDB: RocksDB の Blob 分離ストレージ変種(大きな Value 向け)
  • Baseline: vmemkv 最適化なし
  • +BF: + T1 Bloom Filter
  • +Inline: +BF + T1 Inline Value(<8B のValueをT1のみで処理)
  • +Prefault: +Inline + T2 Async Prefaulting
  • +ScanBaseSequential: +Prefault + base領域専用mmap(MADV_SEQUENTIAL)。本ラウンドから Get もこのmmapの高速パスを使う(全部盛り、= VMemKVStore)

Environment

  • Instance: AWS i4i.8xlarge (ap-northeast-1) x4 (4並列独立インスタンス、run_4parallel_bench.sh)
  • CPU: Intel Xeon Platinum 8375C @ 2.90GHz (32 cores)
  • RAM: 247.7 GiB
  • RAM (Larger-Than-Memory): cgroup で DRAM を 1GiB に制限、target_ratio=8.0でコーパスを約8倍の規模に構築後、cgroup内で計測(ストアごとに独立したcgroupスコープ + drop_caches)
  • Disk: Local NVMe SSD (Swap Media for LTM)
  • Kernel: 6.17.0-1019-aws
  • Git Rev: 7bf7d6a972ab (clean -- get_impl() base_mmap 高速パスを含む)

Workloads

  • Insert: 順次挿入(1/4/16/32 スレッド)
  • Get Hit/Miss (Zipf/Uniform): ポイントルックアップ
  • Update / Delete: Zipf 分布での更新・削除
  • Scan (Zipf/Uniform): レンジスキャン(旧レポートにあった reorganize モード分割は撤去済み)
  • YCSB-E: 95% Scan + 5% Insert 混合(32スレッド / 30秒タイムライン)

Overall Notes & Annotations

Workload Winners & Relative Speedup

Best VMemKV variant vs. whichever rival (RocksDB, LMDB, RocksDB-BlobDB) is FASTEST at that cell, at 32 threads (max concurrency tested) -- not the best-of-any-thread-count-or-rival figure, since both axes are known to shrink (sometimes invert) the ratio if picked favorably. The rival actually being compared against is named in small text under each badge.

Workload 8B In-Memory 1KB In-Memory 1KB LTM 64KB LTM
Insert
✅ WIN (1.50x) vmemkv (+Prefault) vs RocksDB-BlobDB
✅ WIN (1.61x) vmemkv (All opts) vs RocksDB-BlobDB
✅ WIN (1.61x) vmemkv (All opts) vs RocksDB-BlobDB
✅ WIN (2.64x) vmemkv (+Inline) vs RocksDB
Update (Zipf)
✅ WIN (1.46x) vmemkv (All opts) vs RocksDB
✅ WIN (1.57x) vmemkv (+Inline) vs RocksDB-BlobDB
✅ WIN (2.14x) vmemkv (All opts) vs RocksDB-BlobDB
✅ WIN (3.02x) vmemkv (+Inline) vs LMDB
Delete
✅ WIN (1.57x) vmemkv (All opts) vs RocksDB-BlobDB
✅ WIN (1.68x) vmemkv (+BF) vs RocksDB
✅ WIN (1.74x) vmemkv (+BF) vs RocksDB
✅ WIN (3.61x) vmemkv (+Inline) vs RocksDB-BlobDB
Get (Miss)
✅ WIN (2.76x) vmemkv (+Prefault) vs RocksDB
✅ WIN (3.08x) vmemkv (+Prefault) vs LMDB
✅ WIN (2.83x) vmemkv (+Prefault) vs LMDB
✅ WIN (2.59x) vmemkv (+Prefault) vs LMDB
Get (Hit, Zipf)
✅ WIN (1.35x) vmemkv (+Inline) vs LMDB
✅ WIN (1.18x) vmemkv (All opts) vs LMDB
❌ LOSE (0.62x) vmemkv (+Inline) vs RocksDB-BlobDB
✅ WIN (1.26x) vmemkv (All opts) vs RocksDB
Get (Hit, Uniform)
WIN (1.08x) vmemkv (+Inline) vs LMDB
WIN (1.11x) vmemkv (All opts) vs LMDB
LOSE (0.88x) vmemkv (All opts) vs RocksDB-BlobDB
≈ EVEN (0.99x) vmemkv (All opts) vs RocksDB-BlobDB
Scan (Zipf)
❌ LOSE (0.83x) vmemkv (+Prefault) vs LMDB
≈ EVEN (1.04x) vmemkv (All opts) vs LMDB
❌ LOSE (0.77x) vmemkv (All opts) vs RocksDB
WIN (1.08x) vmemkv (All opts) vs LMDB
Scan (Uniform)
❌ LOSE (0.85x) vmemkv (+Inline) vs LMDB
WIN (1.11x) vmemkv (All opts) vs LMDB
✅ WIN (1.32x) vmemkv (All opts) vs RocksDB
≈ EVEN (0.96x) vmemkv (All opts) vs RocksDB-BlobDB
YCSB-E (Timeline Workload)
✅ WIN (2.80x) vmemkv (All opts) vs RocksDB
✅ WIN (1.62x) vmemkv (All opts) vs RocksDB
≈ EVEN (0.98x) vmemkv (All opts) vs RocksDB
WIN (1.06x) vmemkv (All opts) vs RocksDB

Tier 1 Reorganize (T1only)

Average and worst-case times to reorganize the active buffer.

Scenario 8B 1KB 64KB
In-MemoryAvg: 1.36s
Max: 2.29s
Avg: 0.66s
Max: 0.73s
-
LTM-Avg: 1.29s
Max: 1.48s
Avg: 0.10s
Max: 0.16s

Tier 1+2 Reorganize (Full)

Average and worst-case times for a full T1+T2 reorganize / checkpoint.

Scenario 8B 1KB 64KB
In-MemoryAvg: 24.48s
Max: 40.40s
Avg: 13.53s
Max: 21.74s
-
LTM-Avg: 39.66s
Max: 60.00s
*(1/4 points timed out)
Avg: 32.08s
Max: 58.06s

Insert Workload

実験概要: ユニークキーを空のストアに順次挿入する際の書き込みスループット。

Update (Zipf) Workload

実験概要: Zipf 分布での既存キーに対する更新スループット(ステートフルな上書き)。

Delete (Zipf) Workload

実験概要: T1 Tombstone 付与による並行削除スループット。

Get Miss (Zipf) Workload

実験概要: 存在しないキーへの読み取り。Bloom Filter の偽陽性回避効果を評価。

Get Hit (Zipf) Workload

実験概要: Zipf 分布(ホットキー偏重)での既存キーに対するポイントルックアップ。get_impl() の base_mmap 高速パス(本ラウンドの主要変更)が最も効くケース。

Get Hit (Uniform) Workload

実験概要: 一様分布での既存キーに対するポイントルックアップ。同上。

Scan (Zipf) Workload

実験概要: Zipf 分布で選ばれたレンジのスキャン。現行 bench_kv は reorganize 状態を単一の Scan ベンチマークとして測定する形に統合済み(旧レポートの Scan_WithReorg / Scan_T1T2Reorg モード分割は撤去済み)。

Scan (Uniform) Workload

実験概要: 一様分布で選ばれたレンジのスキャン(同上)。

YCSB-E — Scan + Insert 混合スループット(30秒タイムライン)

YCSB-E ワークロード: 95% Scan(100件レンジスキャン)+ 5% Insert を 32スレッドで 30秒間実行。 藍色の点線: 自然発生のT1 Reorganize(Adaptive Soft Limitによる自動トリガー)。 橘色の点線: t=15秒時点で強制的に1回実行されるT1 Reorganize。

Scan QPS タイムライン

32 threads / 30 seconds

Reorganize Duration vs. Corpus Size (T1-only vs T1+T2)

単発のreorganize()呼び出し1回の所要時間を、コーパスサイズ(件数)を横軸にスイープ計測。 藍色 = T1-only、赤色 = T1+T2。 ▲マーカーはタイムアウトを示す。

Reorganize Duration

single-shot, threads:1

Insert Workload

実験概要: ユニークキーを空のストアに順次挿入する際の書き込みスループット。

Update (Zipf) Workload

実験概要: Zipf 分布での既存キーに対する更新スループット(ステートフルな上書き)。

Delete (Zipf) Workload

実験概要: T1 Tombstone 付与による並行削除スループット。

Get Miss (Zipf) Workload

実験概要: 存在しないキーへの読み取り。Bloom Filter の偽陽性回避効果を評価。

Get Hit (Zipf) Workload

実験概要: Zipf 分布(ホットキー偏重)での既存キーに対するポイントルックアップ。get_impl() の base_mmap 高速パス(本ラウンドの主要変更)が最も効くケース。

Get Hit (Uniform) Workload

実験概要: 一様分布での既存キーに対するポイントルックアップ。同上。

Scan (Zipf) Workload

実験概要: Zipf 分布で選ばれたレンジのスキャン。現行 bench_kv は reorganize 状態を単一の Scan ベンチマークとして測定する形に統合済み(旧レポートの Scan_WithReorg / Scan_T1T2Reorg モード分割は撤去済み)。

Scan (Uniform) Workload

実験概要: 一様分布で選ばれたレンジのスキャン(同上)。

YCSB-E — Scan + Insert 混合スループット(30秒タイムライン)

YCSB-E ワークロード: 95% Scan(100件レンジスキャン)+ 5% Insert を 32スレッドで 30秒間実行。 藍色の点線: 自然発生のT1 Reorganize(Adaptive Soft Limitによる自動トリガー)。 橘色の点線: t=15秒時点で強制的に1回実行されるT1 Reorganize。

Scan QPS タイムライン

32 threads / 30 seconds

Reorganize Duration vs. Corpus Size (T1-only vs T1+T2)

単発のreorganize()呼び出し1回の所要時間を、コーパスサイズ(件数)を横軸にスイープ計測。 藍色 = T1-only、赤色 = T1+T2。 ▲マーカーはタイムアウトを示す。

Reorganize Duration

single-shot, threads:1

Insert Workload

実験概要: ユニークキーを空のストアに順次挿入する際の書き込みスループット。

Update (Zipf) Workload

実験概要: Zipf 分布での既存キーに対する更新スループット(ステートフルな上書き)。

Delete (Zipf) Workload

実験概要: T1 Tombstone 付与による並行削除スループット。

Get Miss (Zipf) Workload

実験概要: 存在しないキーへの読み取り。Bloom Filter の偽陽性回避効果を評価。

Get Hit (Zipf) Workload

実験概要: Zipf 分布(ホットキー偏重)での既存キーに対するポイントルックアップ。get_impl() の base_mmap 高速パス(本ラウンドの主要変更)が最も効くケース。

Get Hit (Uniform) Workload

実験概要: 一様分布での既存キーに対するポイントルックアップ。同上。

Scan (Zipf) Workload

実験概要: Zipf 分布で選ばれたレンジのスキャン。現行 bench_kv は reorganize 状態を単一の Scan ベンチマークとして測定する形に統合済み(旧レポートの Scan_WithReorg / Scan_T1T2Reorg モード分割は撤去済み)。

Scan (Uniform) Workload

実験概要: 一様分布で選ばれたレンジのスキャン(同上)。

YCSB-E — Scan + Insert 混合スループット(30秒タイムライン)

YCSB-E ワークロード: 95% Scan(100件レンジスキャン)+ 5% Insert を 32スレッドで 30秒間実行。 藍色の点線: 自然発生のT1 Reorganize(Adaptive Soft Limitによる自動トリガー)。 橘色の点線: t=15秒時点で強制的に1回実行されるT1 Reorganize。

Scan QPS タイムライン

32 threads / 30 seconds

Reorganize Duration vs. Corpus Size (T1-only vs T1+T2)

単発のreorganize()呼び出し1回の所要時間を、コーパスサイズ(件数)を横軸にスイープ計測。 藍色 = T1-only、赤色 = T1+T2。 ▲マーカーはタイムアウトを示す。

Reorganize Duration

single-shot, threads:1

Insert Workload

実験概要: ユニークキーを空のストアに順次挿入する際の書き込みスループット。

Update (Zipf) Workload

実験概要: Zipf 分布での既存キーに対する更新スループット(ステートフルな上書き)。

Delete (Zipf) Workload

実験概要: T1 Tombstone 付与による並行削除スループット。

Get Miss (Zipf) Workload

実験概要: 存在しないキーへの読み取り。Bloom Filter の偽陽性回避効果を評価。

Get Hit (Zipf) Workload

実験概要: Zipf 分布(ホットキー偏重)での既存キーに対するポイントルックアップ。get_impl() の base_mmap 高速パス(本ラウンドの主要変更)が最も効くケース。

Get Hit (Uniform) Workload

実験概要: 一様分布での既存キーに対するポイントルックアップ。同上。

Scan (Zipf) Workload

実験概要: Zipf 分布で選ばれたレンジのスキャン。現行 bench_kv は reorganize 状態を単一の Scan ベンチマークとして測定する形に統合済み(旧レポートの Scan_WithReorg / Scan_T1T2Reorg モード分割は撤去済み)。

Scan (Uniform) Workload

実験概要: 一様分布で選ばれたレンジのスキャン(同上)。

YCSB-E — Scan + Insert 混合スループット(30秒タイムライン)

YCSB-E ワークロード: 95% Scan(100件レンジスキャン)+ 5% Insert を 32スレッドで 30秒間実行。 藍色の点線: 自然発生のT1 Reorganize(Adaptive Soft Limitによる自動トリガー)。 橘色の点線: t=15秒時点で強制的に1回実行されるT1 Reorganize。

Scan QPS タイムライン

32 threads / 30 seconds

Reorganize Duration vs. Corpus Size (T1-only vs T1+T2)

単発のreorganize()呼び出し1回の所要時間を、コーパスサイズ(件数)を横軸にスイープ計測。 藍色 = T1-only、赤色 = T1+T2。 ▲マーカーはタイムアウトを示す。

Reorganize Duration

single-shot, threads:1