Files
hakmem/docs/archive/PHASE_6.15_STEP2_COMPLETION.md
Moe Charm (CI) 52386401b3 Debug Counters Implementation - Clean History
Major Features:
- Debug counter infrastructure for Refill Stage tracking
- Free Pipeline counters (ss_local, ss_remote, tls_sll)
- Diagnostic counters for early return analysis
- Unified larson.sh benchmark runner with profiles
- Phase 6-3 regression analysis documentation

Bug Fixes:
- Fix SuperSlab disabled by default (HAKMEM_TINY_USE_SUPERSLAB)
- Fix profile variable naming consistency
- Add .gitignore patterns for large files

Performance:
- Phase 6-3: 4.79 M ops/s (has OOM risk)
- With SuperSlab: 3.13 M ops/s (+19% improvement)

This is a clean repository without large log files.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-05 12:31:14 +09:00

9.0 KiB
Raw Blame History

Phase 6.15 Step 2 完了報告: P0 Safety Lock

Date: 2025-10-22 Implementation Time: 135分 Status: ⚠️ 実装完了・性能問題あり


📊 実装結果

修正ファイル

  • apps/experiments/hakmem-poc/hakmem.c (+130 lines)

    • #include <pthread.h> 追加
    • pthread_mutex_t g_hakmem_lock グローバルロック追加
    • __thread int g_hakmem_lock_depth 再帰検出用カウンタ追加
    • malloc() wrapper 実装 (ロック + 再帰ガード)
    • free() wrapper 実装 (ロック + 再帰ガード)
    • calloc() wrapper 実装 (ロック + 再帰ガード)
    • realloc() wrapper 実装 (ロック + 再帰ガード)
  • apps/experiments/hakmem-poc/Makefile (+1 line)

    • LDFLAGS = -lm -lpthread に変更

ビルド

  • make clean && make shared 成功
  • libhakmem.so 生成成功
  • LD_PRELOAD テスト成功 (/bin/echo で確認)

🧪 テスト結果

Test 2.3.1: larson 1-thread

env LD_PRELOAD=./libhakmem.so /tmp/mimalloc-bench/bench/larson/larson 0 8 1024 10000 1 12345 1
  • 実測: 1,197,531 ops/sec (1.2M ops/sec)
  • 期待: 13-15M ops/sec
  • 結果: 92%遅い (期待の8%のみ達成)
  • ロックオーバーヘッド: -92% (予想0-15%を大幅に超過)

Test 2.3.2: larson 4-thread

env LD_PRELOAD=./libhakmem.so /tmp/mimalloc-bench/bench/larson/larson 0 8 1024 10000 1 12345 4
  • 実測: 717,791 ops/sec (0.7M ops/sec)
  • 期待: 13-15M ops/sec
  • 結果: 95%遅い (期待の5%のみ達成)
  • スケール: 0.60x (1T→4Tで逆に40%低下)

Test 2.3.3: Helgrind

  • Status: ⏸️ 時間制約によりスキップ
  • 理由: 性能問題の根本原因調査を優先

Test 2.3.4: 安定性10回実行

  • Status: ⏸️ 時間制約によりスキップ
  • 理由: 性能問題の根本原因調査を優先

成功基準チェック

  • [] ビルド成功
  • [] 1T性能: 1.2M ops/sec (期待13-15M、92%未達)
  • [] 4T性能: 0.7M ops/sec (期待13-15M、95%未達)
  • [⏸️] Helgrind: Data race 0件 (スキップ)
  • [⏸️] 安定性: 10/10 成功 (スキップ)

🚨 Critical Issue: 性能崩壊の原因分析

問題1: 想定外のロックオーバーヘッド

期待: 0-15%オーバーヘッド 実測: 92%性能低下 (15.3M → 1.2M ops/sec)

仮説:

  1. 過剰な再帰ガードフォールバック: g_hakmem_lock_depth チェックが不適切で、本来hakmemで処理すべきallocationが__libc_mallocにフォールバックしている可能性
  2. 初期化コストの問題: hakmem初期化時の大量のprintf出力が毎回実行されている可能性
  3. ロック粒度の問題: malloc/free wrapper レベルでロックを取ると、内部のhak_alloc_at/hak_free_atでも別のロックを取っている可能性二重ロック

問題2: 4-threadでさらに悪化

期待: 4T ≈ 1T (スケールなしだが同等性能) 実測: 4T = 0.60x 1T (逆に40%低下)

仮説:

  1. ロック競合: グローバルロックg_hakmem_lockでの競合
  2. False sharing: g_hakmem_lock_depth__threadだが、他のグローバル変数とキャッシュライン共有
  3. スレッド間の不均衡: 一部スレッドがロック待ちで停止

🔍 調査が必要な項目

Priority P0: 即座に調査

  1. 再帰ガードの動作確認

    // malloc() の最初で確認
    if (g_hakmem_lock_depth > 0) {
        // ← この分岐に入る頻度を計測
        extern void* __libc_malloc(size_t);
        return __libc_malloc(size);
    }
    
    • カウンタ追加: フォールバック率を測定
    • 期待: 初期化時のみ (<1%)
    • 実測: 不明(要調査)
  2. 二重ロックの有無確認

    • hak_alloc_at() / hak_free_at() 内でロックを取っているか?
    • wrapper でロックを取った後、内部でさらにロックを取ると deadlock or 性能劣化
  3. 初期化printf の影響

    • hak_init() が毎回15行のログを出力
    • LD_PRELOAD環境では初期化が複数回呼ばれる可能性

Priority P1: 次の調査

  1. ロック競合の可視化

    • pthread_mutex_lock() の待ち時間を計測
    • 期待: <1% の時間
    • 実測: 不明
  2. hakmem vs __libc_malloc 比率

    • 実際にhakmemで処理された allocation の割合
    • 期待: >99%
    • 実測: 不明

📋 次のステップ

Option A: P0調査 → 根本原因修正 (推奨)

Task A.1: Fallback率測定 (30分)

  • malloc()/free() にカウンタ追加
  • hakmem経路 vs __libc_fallback経路 の比率を出力

Task A.2: 二重ロック確認 (15分)

  • hak_alloc_at() / hak_free_at() のロック状況を確認
  • wrapper側でロック→内部でロックなら、内部ロック削除

Task A.3: 初期化最適化 (15分)

  • g_initialized check を malloc wrapper の最初に移動
  • printf出力を HAKMEM_SILENT 環境変数でsuppress

Option B: Step 3 (P1 TLS) に進む (非推奨)

理由: 現状の性能では、TLSを追加しても92%の劣化を解消できない可能性が高い


💡 技術的発見

再帰ガードの実装

// 正しい順序 (Phase 6.15 Step 2 実装)
pthread_mutex_lock(&g_hakmem_lock);     // 1. ロック取得
g_hakmem_lock_depth++;                   // 2. 深さ記録

void* ptr = hak_alloc_at(size, ...);     // 3. 内部処理

g_hakmem_lock_depth--;                   // 4. 深さ復元
pthread_mutex_unlock(&g_hakmem_lock);    // 5. ロック解放

Key Point: ロック取得g_hakmem_lock_depth をインクリメント

理由:

  • ロック前にインクリメントすると、全呼び出しがfallbackになる初回実装のバグ
  • ロック後にインクリメントすると、初期化時のnestedのみfallbackになる修正版

__libc_malloc へのfallback

extern void* __libc_malloc(size_t);  // glibc の実際の malloc

用途: 初期化時にhak_init()内のprintf()が内部でmallocを呼ぶとデッドロック 解決: g_hakmem_lock_depth > 0 で検出してfallback


📝 実装コード抜粋

Global Lock宣言 (hakmem.c:61-72)

// ============================================================================
// Phase 6.15 P0: Thread Safety - Global Lock
// ============================================================================

// Global lock for all allocator operations
// Purpose: Ensure correctness in multi-threaded environment
// Performance: 4T ≈ 1T (no scalability, safety first)
// Will be replaced by TLS in P1-P3 (95%+ lock avoidance)
static pthread_mutex_t g_hakmem_lock = PTHREAD_MUTEX_INITIALIZER;

// Recursive lock count (to prevent deadlock during initialization)
static __thread int g_hakmem_lock_depth = 0;

malloc() Wrapper (hakmem.c:740-759)

// malloc wrapper - intercepts system malloc() calls
void* malloc(size_t size) {
    // Phase 6.15 P0: Global lock (with recursion guard)
    // If we're already holding the lock (depth > 0), use fallback to prevent deadlock
    if (g_hakmem_lock_depth > 0) {
        // Nested call detected - fallback to system malloc
        extern void* __libc_malloc(size_t);
        return __libc_malloc(size);
    }

    // First-level call - acquire lock
    pthread_mutex_lock(&g_hakmem_lock);
    g_hakmem_lock_depth++;

    void* ptr = hak_alloc_at(size, HAK_CALLSITE());

    g_hakmem_lock_depth--;
    pthread_mutex_unlock(&g_hakmem_lock);
    return ptr;
}

🎯 結論

実装完了項目

  1. pthread.h インクルード
  2. グローバルロック宣言
  3. malloc/free/calloc/realloc wrapper 実装
  4. 再帰ガード実装
  5. ビルド成功
  6. LD_PRELOAD動作確認

未達成項目

  1. 1T性能: 1.2M ops/sec (期待13-15M、92%未達)
  2. 4T性能: 0.7M ops/sec (期待13-15M、95%未達)
  3. Helgrind検証 (時間制約によりスキップ)
  4. 安定性テスト (時間制約によりスキップ)

Critical Decision Point

Step 2は「実装完了」だが「性能目標未達」

推奨アクション:

  1. Option A (推奨): P0調査を実施1時間

    • Fallback率測定
    • 二重ロック確認
    • 初期化最適化
    • 期待: 1.2M → 12-15M ops/sec に改善可能
  2. Option B (代替): 現状を "Baseline with Lock" として受け入れ、Step 3 TLS実装へ進む

    • リスク: TLSでも根本問題が残る可能性

📁 関連ドキュメント


Implementation Time: 135分 (予定120分、+15分オーバー) Next Action: P0調査 (Option A) または Step 3開始判断 (Option B)