277 lines
9.0 KiB
Markdown
277 lines
9.0 KiB
Markdown
|
|
# 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**
|
|||
|
|
```bash
|
|||
|
|
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**
|
|||
|
|
```bash
|
|||
|
|
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. **再帰ガードの動作確認**
|
|||
|
|
```c
|
|||
|
|
// 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: 次の調査**
|
|||
|
|
|
|||
|
|
4. **ロック競合の可視化**
|
|||
|
|
- `pthread_mutex_lock()` の待ち時間を計測
|
|||
|
|
- 期待: <1% の時間
|
|||
|
|
- 実測: 不明
|
|||
|
|
|
|||
|
|
5. **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%の劣化を解消できない可能性が高い
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 💡 **技術的発見**
|
|||
|
|
|
|||
|
|
### **再帰ガードの実装**
|
|||
|
|
|
|||
|
|
```c
|
|||
|
|
// 正しい順序 (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**
|
|||
|
|
|
|||
|
|
```c
|
|||
|
|
extern void* __libc_malloc(size_t); // glibc の実際の malloc
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**用途**: 初期化時に`hak_init()`内の`printf()`が内部でmallocを呼ぶとデッドロック
|
|||
|
|
**解決**: `g_hakmem_lock_depth > 0` で検出してfallback
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 📝 **実装コード抜粋**
|
|||
|
|
|
|||
|
|
### **Global Lock宣言** (hakmem.c:61-72)
|
|||
|
|
|
|||
|
|
```c
|
|||
|
|
// ============================================================================
|
|||
|
|
// 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)
|
|||
|
|
|
|||
|
|
```c
|
|||
|
|
// 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でも根本問題が残る可能性
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 📁 **関連ドキュメント**
|
|||
|
|
- **計画**: [PHASE_6.15_PLAN.md](PHASE_6.15_PLAN.md) - 全体計画
|
|||
|
|
- **前フェーズ**: [PHASE_6.14_COMPLETION_REPORT.md](PHASE_6.14_COMPLETION_REPORT.md) - Baseline (15.3M ops/sec)
|
|||
|
|
- **Thread Safety分析**: [THREAD_SAFETY_SOLUTION.md](THREAD_SAFETY_SOLUTION.md) - 完全分析
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
**Implementation Time**: 135分 (予定120分、+15分オーバー)
|
|||
|
|
**Next Action**: P0調査 (Option A) または Step 3開始判断 (Option B)
|