Files
hakmem/PARTIAL_RELEASE_INVESTIGATION_REPORT_20251205.md
Moe Charm (CI) 2b2b607957 Add workload comparison and madvise investigation reports
Key findings from 2025-12-05 session:
1. HAKMEM vs mimalloc: 27x slower (4.5M vs 122M ops/s)
2. Root cause investigation: madvise 1081 calls vs mimalloc 0 calls
3. madvise disable test: -15% performance (worse, not better!)
4. Conclusion: MADV_POPULATE_WRITE is actually helping, not hurting
5. ChatGPT was right: time to move to user-space optimization phase

Reports added:
- WORKLOAD_COMPARISON_20251205.md
- PARTIAL_RELEASE_INVESTIGATION_REPORT_20251205.md

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 13:31:45 +09:00

11 KiB
Raw Blame History

Partial Release無効化テスト結果レポート

調査日: 2025年12月5日 目的: madvise()がCPU時間の58%を占めている問題を解決するため、Partial Release機能の影響を調査


📊 測定結果サマリー

ベンチマーク性能比較

条件 Run1 (ops/s) Run2 (ops/s) Run3 (ops/s) 平均 (ops/s) 改善率
Partial Release有効 (baseline) 4,784,048 4,709,849 4,739,815 4,744,571 -
Partial Release無効 (HAKMEM_TINY_SS_PARTIAL=0) 4,920,803 4,778,194 4,889,397 4,862,798 +2.5%

perf統計比較

指標 Partial Release有効 Partial Release無効
cycles 1,164,151,908 1,160,123,269 -4M (-0.3%)
page-faults 6,759 6,760 +1 (変化なし)
cache-misses 8,210,925 8,142,282 -69K (-0.8%)
L1-dcache-load-misses 17,780,041 (3.99%) 17,624,374 (3.96%) -156K (-0.9%)
user時間 0.030s 0.035s +0.005s
sys時間 0.247s 0.252s +0.005s
実行時間 0.278s 0.288s +0.010s

🔍 重大な発見Partial Release無効化してもmadvise()は減らない!

strace syscall統計

HAKMEM_TINY_SS_PARTIAL=0Partial Release無効

% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 91.21    0.173846         160      1082           madvise
  5.30    0.010094           9      1092           munmap
  3.20    0.006106           5      1113           mmap

デフォルトPartial Release有効

% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 91.86    0.159657         147      1081           madvise
  4.96    0.008628           7      1091           munmap
  2.90    0.005043           4      1113           mmap

結論: madvise呼び出し回数はほぼ同じ1081-1082回


🔬 詳細調査madvise()の発生源を特定

発見した環境変数

環境変数名 デフォルト値 用途 効果
HAKMEM_TINY_SS_PARTIAL 1 (有効) SuperSlabのPartial Release制御 効果なし (2.5%改善は誤差範囲)
HAKMEM_TINY_SS_PARTIAL_INTERVAL 4 Partial Release実行間隔 -
HAKMEM_DISABLE_BATCH 0 (有効) Batch madvise制御 効果なし
HAKMEM_SS_PREFAULT 0 (OFF) SuperSlab prefault制御 効果なし

全機能無効化テスト

HAKMEM_DISABLE_BATCH=1 HAKMEM_TINY_SS_PARTIAL=0 HAKMEM_SS_PREFAULT=0 strace -c ./bench_random_mixed_hakmem 1000000 400 1

結果: 依然として1081回のmadvise呼び出し (91.74% of syscall time)


💡 根本原因の特定

madvise()呼び出し箇所の完全マップ

straceで実際の呼び出しを確認:

strace -e madvise ./bench_random_mixed_hakmem 1000000 400 1

全てのmadvise呼び出しは MADV_POPULATE_WRITE であることが判明!

madvise(0x73f0e2680000, 524288, MADV_POPULATE_WRITE) = 0
madvise(0x73f0e2300000, 524288, MADV_POPULATE_WRITE) = 0
madvise(0x73f0e2200000, 524288, MADV_POPULATE_WRITE) = 0
...

発生源コード

1. 主犯: core/superslab_cache.c (Fallback Path)

/mnt/workdisk/public_share/hakmem/core/superslab_cache.c:113

// Pre-fault pages in fallback path (only after trim to actual SuperSlab size)
// This is critical: we MUST touch the pages after munmap() to establish valid mappings
// CRITICAL FIX (2025-12-05): Use MADV_POPULATE_WRITE for efficiency
#ifdef MADV_POPULATE_WRITE
    int ret = madvise(ptr, ss_size, MADV_POPULATE_WRITE);  // ← ここが主犯!
    if (ret != 0) {
        // Fallback: explicit memset
        memset(ptr, 0, ss_size);
    }
#else
    // Fallback for kernels < 5.14
    memset(ptr, 0, ss_size);
#endif

問題点:

  • このmadvise()環境変数で無効化できない
  • Fallback pathが常に使われているMAP_ALIGNED_SUPERが失敗
  • munmap trim後に必ずMADV_POPULATE_WRITEが実行される

2. core/box/ss_os_acquire_box.c:179 (populate=1の場合)

#ifdef MADV_POPULATE_WRITE
    if (populate) {
        int ret = madvise(ptr, ss_size, MADV_POPULATE_WRITE);
        ...
    }
#endif

3. core/hakmem_tiny_intel.inc:692 (Partial Release - 今回無効化を試した箇所)

static inline void superslab_partial_release(SuperSlab* ss, uint32_t epoch) {
#if defined(MADV_DONTNEED)
    if (!g_ss_partial_enable) return;  // ← HAKMEM_TINY_SS_PARTIAL=0で無効化
    ...
    if (madvise(ss, len, MADV_DONTNEED) == 0) {
        ss->partial_epoch = epoch;
    }
#endif
}

重要: この関数は今回のベンチマークではほとんど呼ばれていない

4. core/hakmem_batch.c:120,123 (Batch madvise)

int ret = madvise(ptr, size, MADV_FREE);
if (ret != 0) {
    ret = madvise(ptr, size, MADV_DONTNEED);
}

📈 アーキテクチャ分析

madvise()の4つの用途

# 場所 種類 目的 頻度 環境変数制御
1 superslab_cache.c:113 MADV_POPULATE_WRITE Fallback path でのページ確保 超高頻度 (~1000回) 不可
2 ss_os_acquire_box.c:179 MADV_POPULATE_WRITE populate=1時のprefault populate時のみ HAKMEM_SS_PREFAULT
3 hakmem_tiny_intel.inc:692 MADV_DONTNEED Partial Release (空SuperSlabのメモリ返却) 低頻度 HAKMEM_TINY_SS_PARTIAL
4 hakmem_batch.c:120 MADV_FREE/DONTNEED Batch madvise (大きいalloc解放時) 中頻度 HAKMEM_DISABLE_BATCH

今回のベンチマークでの実態

  • 1081回のmadvise呼び出し全てが MADV_POPULATE_WRITE
  • これは**#1のFallback Path**から発生している
  • #3 Partial Release (MADV_DONTNEED) は今回のベンチマークではほとんど実行されていない

🎯 結論

主要な発見

  1. HAKMEM_TINY_SS_PARTIAL=0 は効果がほぼない(+2.5%程度)

    • 理由: このベンチマークではPartial Releaseがほとんど発動していない
    • 1081回のmadviseのうち、MADV_DONTNEEDはほぼゼロ
  2. 真の問題は superslab_cache.c のFallback Path

    • 全てのSuperSlab割り当てで MADV_POPULATE_WRITE が実行される
    • 環境変数では制御不可能
    • コメントによると「munmap trim後の再確保に必須」
  3. 前回のperf分析での58%のmadvise時間は別の要因だった可能性

    • 前回のベンチマーク: bench_random_mixed.c (16-1024バイト)
    • 今回のベンチマーク: bench_random_mixed_hakmem 1000000 400 1 (16-1040バイト)
    • ワークロードが異なるため、Partial Releaseの発動頻度も異なる
  4. 性能への影響は限定的

    • MADV_POPULATE_WRITEは割り当て時の1回のみホットパスではない
    • ウォームアップ後はSuperSlabの再利用で新規madviseは減る
    • syscall時間の91%でも、全体の実行時間は0.25s程度

🔧 推奨される次のステップ

Option 1: Fallback Path の最適化(根本対策)

問題: MAP_ALIGNED_SUPERが失敗し、常にFallback Pathが使われている

調査項目:

  1. なぜMAP_ALIGNED_SUPERが失敗するのか
  2. FreeBSDでは成功するが、Linuxでは失敗する
  3. Fallback PathでのMADV_POPULATE_WRITEは本当に必須か

改善案:

// Option A: 環境変数で制御可能にする
static int prefault_disabled = -1;
if (prefault_disabled == -1) {
    const char* env = getenv("HAKMEM_NO_FALLBACK_PREFAULT");
    prefault_disabled = (env && *env && *env != '0') ? 1 : 0;
}
if (!prefault_disabled) {
    madvise(ptr, ss_size, MADV_POPULATE_WRITE);
}

// Option B: memsetフォールバックを常に使うsyscall削減
memset(ptr, 0, ss_size);  // syscallなし、ただしCPU使用量増加

Option 2: ワークロード別の最適化

異なるベンチマークで再測定:

# より長時間のベンチマークPartial Releaseが発動しやすい
./bench_random_mixed_hakmem 10000000 400 1

# より大きいワーキングセットSuperSlabの入れ替えが多い
./bench_random_mixed_hakmem 1000000 4000 1

Option 3: perf record で詳細プロファイリング

前回報告されていた58%のmadviseを再現:

# 前回と同じベンチマーク条件で測定
perf record -g ./bench_random_mixed.c の該当コマンド
perf report --stdio

📋 コード参照

関連ファイル

  • /mnt/workdisk/public_share/hakmem/core/superslab_cache.c:113 - MADV_POPULATE_WRITE (主犯)
  • /mnt/workdisk/public_share/hakmem/core/box/ss_os_acquire_box.c:179 - MADV_POPULATE_WRITE (populate時)
  • /mnt/workdisk/public_share/hakmem/core/hakmem_tiny_intel.inc:685-698 - superslab_partial_release (MADV_DONTNEED)
  • /mnt/workdisk/public_share/hakmem/core/hakmem_batch.c:120 - Batch madvise (MADV_FREE/DONTNEED)
  • /mnt/workdisk/public_share/hakmem/core/hakmem_config.c:231-234 - HAKMEM_DISABLE_BATCH の処理
  • /mnt/workdisk/public_share/hakmem/core/hakmem_tiny_lifecycle.inc:78-90 - HAKMEM_TINY_SS_PARTIAL の処理
  • /mnt/workdisk/public_share/hakmem/core/box/ss_prefault_box.h:35-54 - HAKMEM_SS_PREFAULT の処理

🧪 再現手順

環境変数一覧

# Partial Release無効化効果: +2.5%程度)
export HAKMEM_TINY_SS_PARTIAL=0

# Batch madvise無効化効果: なし)
export HAKMEM_DISABLE_BATCH=1

# SuperSlab prefault無効化効果: なし)
export HAKMEM_SS_PREFAULT=0

ベンチマーク実行

# ベースライン
./bench_random_mixed_hakmem 1000000 400 1

# Partial Release無効
HAKMEM_TINY_SS_PARTIAL=0 ./bench_random_mixed_hakmem 1000000 400 1

# 全無効化
HAKMEM_DISABLE_BATCH=1 HAKMEM_TINY_SS_PARTIAL=0 HAKMEM_SS_PREFAULT=0 ./bench_random_mixed_hakmem 1000000 400 1

# syscall詳細
strace -c ./bench_random_mixed_hakmem 1000000 400 1
strace -e madvise ./bench_random_mixed_hakmem 1000000 400 1 2>&1 | head -30

📚 参考資料

  • PERF_ANALYSIS_16_1024B_20251205.md - 前回のperf分析madvise 58%問題)
  • LAZY_ZEROING_IMPLEMENTATION_RESULTS_20251204.md - MADV_DONTNEED最適化の失敗例
  • EXPLICIT_PREFAULT_IMPLEMENTATION_REPORT_20251205.md - MADV_POPULATE_WRITE導入の経緯

報告者: Claude Code 最終更新: 2025年12月5日