Files
hakmem/docs/archive/PHASE4_IMPROVEMENT_ROADMAP.md

584 lines
15 KiB
Markdown
Raw Normal View History

# Phase 4 改善ロードマップ
## Overview
Phase 4 で 3.6% の性能退行が発生したため、段階的に改善します。
**現状**:
- Phase 3: 391 M ops/sec
- Phase 4: 373-380 M ops/sec
- **退行**: -3.6%
**目標**:
- Phase 4.1Quick Win: 385-390 M ops/sec+1-2%
- Phase 4.2Gating: 390-395 M ops/secPhase 3 レベル回復)
- Phase 4.3Batching: 395-400 M ops/secPhase 3 超え)
---
## Phase 4.1: Quick WinOption A+B
### 目標
- 実装時間: **5-10分**
- 期待効果: **+1-2%**385-390 M ops/sec
- リスク: **低**
### 実装内容
#### Option A: 重複メモリアクセスの削減
**Before**:
```c
// owner->class_idx を2回読む
int is_tls_active = (owner == g_tls_active_slab_a[owner->class_idx] ||
owner == g_tls_active_slab_b[owner->class_idx]);
if (is_tls_active && !mini_mag_is_full(&owner->mini_mag)) {
mini_mag_push(&owner->mini_mag, it.ptr);
stats_record_free(owner->class_idx);
continue;
}
```
**After**:
```c
// 1回だけ読んで再利用
uint8_t cidx = owner->class_idx;
TinySlab* tls_a = g_tls_active_slab_a[cidx];
TinySlab* tls_b = g_tls_active_slab_b[cidx];
if ((owner == tls_a || owner == tls_b) &&
!mini_mag_is_full(&owner->mini_mag)) {
mini_mag_push(&owner->mini_mag, it.ptr);
stats_record_free(cidx);
continue;
}
```
**改善点**:
- `owner->class_idx`: 3回読み → 1回読み
- `g_tls_active_slab_a/b[cidx]`: ループ外に hoist 可能
#### Option B: Branch prediction hint
```c
// TLS Magazine から spill する場合、TLS-active slab への戻りが likely
if (__builtin_expect((owner == tls_a || owner == tls_b) &&
!mini_mag_is_full(&owner->mini_mag), 1)) {
mini_mag_push(&owner->mini_mag, it.ptr);
stats_record_free(cidx);
continue;
}
```
**改善点**:
- Branch misprediction を削減
- CPU の分岐予測をヒント
### 修正箇所
**ファイル**: `hakmem_tiny.c`
**関数**: `hak_tiny_free_with_slab()`
**行番号**: 890-922
### 検証方法
```bash
# Phase 4.1 実装
make clean && make bench_tiny
# ベンチマーク実行3回
./bench_tiny 2>&1 | tail -5
./bench_tiny 2>&1 | tail -5
./bench_tiny 2>&1 | tail -5
# 期待結果: 385-390 M ops/sec
```
### 成功基準
- **最低**: 380 M ops/sec現状維持
- **目標**: 385-390 M ops/sec+1-2%
- **理想**: 390+ M ops/secPhase 3 レベル)
---
## Phase 4.2: High-water ゲートOption E-1
### 目標
- 実装時間: **10-20分**
- 期待効果: **+2-5%**390-395 M ops/sec
- リスク: **低〜中**
### 実装内容
#### High-water ゲートロジック
**コンセプト**:
> TLS Magazine が高水位≥75%のとき、Phase 4 を丸ごとスキップ
> 理由: 次回 alloc は TLS から出るので、mini-mag への投入は無駄
**実装**:
```c
// hak_tiny_free_with_slab() の先頭に追加
void hak_tiny_free_with_slab(...) {
// ... 既存の前処理 ...
// Phase 4.2: High-water ゲート
int tls_occ = mag->count; // TLS Magazine 現在の占有数
int tls_cap = TLS_MAG_CAPACITY; // 2048
if (tls_occ >= (tls_cap * 3 / 4)) {
// High-water: Phase 4 無効
// 全件 bitmap へ直書き(既存ロジック)
for (int i = 0; i < mag->count; i++) {
PoolItem it = mag->items[i];
TinySlab* owner = hak_tiny_owner_slab(it.ptr);
if (!owner) continue;
// Bitmap へ spill既存ロジックを流用
// ... bitmap operations ...
}
// 統計
g_tiny_pool.phase4_gate_skip[class_idx]++;
return;
}
// Low-water: Phase 4 実行(既存ロジック)
for (int i = 0; i < mag->count; i++) {
PoolItem it = mag->items[i];
TinySlab* owner = hak_tiny_owner_slab(it.ptr);
if (!owner) continue;
// Phase 4.1 で最適化したロジック
uint8_t cidx = owner->class_idx;
TinySlab* tls_a = g_tls_active_slab_a[cidx];
TinySlab* tls_b = g_tls_active_slab_b[cidx];
if (__builtin_expect((owner == tls_a || owner == tls_b) &&
!mini_mag_is_full(&owner->mini_mag), 1)) {
mini_mag_push(&owner->mini_mag, it.ptr);
stats_record_free(cidx);
g_tiny_pool.phase4_mini_push[cidx]++;
continue;
}
// Bitmap へ spill
// ... bitmap operations ...
g_tiny_pool.phase4_bitmap_spill[cidx]++;
}
g_tiny_pool.phase4_spill_count[class_idx]++;
}
```
#### 定数定義
```c
// hakmem_tiny.h または hakmem_tiny.c
#define TLS_MAG_CAPACITY 2048
#define TLS_MAG_HIGH_WATER (TLS_MAG_CAPACITY * 3 / 4) // 1536
#define TLS_MAG_LOW_WATER (TLS_MAG_CAPACITY / 4) // 512
```
#### 統計追加
```c
// hakmem_tiny.h: TinyPool 構造体に追加
typedef struct {
// 既存
uint64_t alloc_count[TINY_NUM_CLASSES];
uint64_t free_count[TINY_NUM_CLASSES];
uint64_t slab_count[TINY_NUM_CLASSES];
// Phase 4 測定用(新規)
uint64_t phase4_spill_count[TINY_NUM_CLASSES]; // Phase 4 判定回数
uint64_t phase4_mini_push[TINY_NUM_CLASSES]; // Mini-mag push 成功
uint64_t phase4_bitmap_spill[TINY_NUM_CLASSES]; // Bitmap spill
uint64_t phase4_gate_skip[TINY_NUM_CLASSES]; // High-water skip
} TinyPool;
```
```c
// hakmem_tiny.c: 初期化
void hak_tiny_init(void) {
// ... 既存の初期化 ...
for (int i = 0; i < TINY_NUM_CLASSES; i++) {
g_tiny_pool.phase4_spill_count[i] = 0;
g_tiny_pool.phase4_mini_push[i] = 0;
g_tiny_pool.phase4_bitmap_spill[i] = 0;
g_tiny_pool.phase4_gate_skip[i] = 0;
}
}
```
### 検証方法
```bash
# Phase 4.2 実装
make clean && make bench_tiny
# ベンチマーク実行3回
./bench_tiny 2>&1 | tail -5
./bench_tiny 2>&1 | tail -5
./bench_tiny 2>&1 | tail -5
# 統計確認(実装後)
# hak_tiny_print_stats() に phase4 統計を追加
./test_mf2 2>&1 | grep -A 10 "Phase 4"
# 期待結果: 390-395 M ops/secPhase 3 レベル)
```
### 成功基準
- **最低**: 385 M ops/secPhase 4.1 維持)
- **目標**: 390-395 M ops/secPhase 3 レベル回復)
- **理想**: 395+ M ops/secPhase 3 超え)
### Revert 判断
Phase 4.2 実装後も 385 M ops/sec を下回る場合:
- **Phase 4 全体を revert**
- Phase 3391 M ops/secに戻る
- Pull 型アプローチを検討
---
## Phase 4.3: Per-slab バッチOption E-2
### 目標
- 実装時間: **30-40分**
- 期待効果: **+2-5%**395-400 M ops/sec
- リスク: **中〜高**(実装複雑)
### 実装内容
#### Per-slab グルーピング
**コンセプト**:
> Spill 256 items を slab 単位でグルーピング
> is_tls_active 判定: 256回 → slab数回1-8回に激減
**データ構造**:
```c
#define SLAB_BUCKETS 32 // 線形プローブ用バケツ数
typedef struct {
TinySlab* owner;
void* ptrs[256];
int count;
} SlabBucket;
```
**実装**:
```c
void hak_tiny_free_with_slab_batched(...) {
// Phase 4.2: High-water ゲート
int tls_occ = mag->count;
if (tls_occ >= TLS_MAG_HIGH_WATER) {
fast_spill_all_to_bitmap(mag);
return;
}
// Phase 4.3: Per-slab バッチ
SlabBucket buckets[SLAB_BUCKETS] = {0};
// 1st pass: Slab 単位でグルーピング
for (int i = 0; i < mag->count; i++) {
PoolItem it = mag->items[i];
TinySlab* owner = hak_tiny_owner_slab(it.ptr);
if (!owner) continue;
// Linear probing hash
size_t hash = ((uintptr_t)owner >> 6) & (SLAB_BUCKETS - 1);
while (buckets[hash].owner && buckets[hash].owner != owner) {
hash = (hash + 1) & (SLAB_BUCKETS - 1);
}
if (!buckets[hash].owner) {
buckets[hash].owner = owner;
}
buckets[hash].ptrs[buckets[hash].count++] = it.ptr;
}
// 2nd pass: Slab ごとに処理(判定は slab ごとに 1 回)
for (int b = 0; b < SLAB_BUCKETS; b++) {
if (!buckets[b].owner) continue;
TinySlab* slab = buckets[b].owner;
uint8_t cidx = slab->class_idx;
TinySlab* tls_a = g_tls_active_slab_a[cidx];
TinySlab* tls_b = g_tls_active_slab_b[cidx];
int is_tls_active = (slab == tls_a || slab == tls_b);
int room = mini_capacity(&slab->mini_mag) - mini_count(&slab->mini_mag);
int take = is_tls_active ? min(room, buckets[b].count) : 0;
// Mini-mag へ一括 push
for (int i = 0; i < take; i++) {
mini_mag_push(&slab->mini_mag, buckets[b].ptrs[i]);
stats_record_free(cidx);
g_tiny_pool.phase4_mini_push[cidx]++;
}
// 余りは bitmap へ一括 spill
for (int i = take; i < buckets[b].count; i++) {
// ... bitmap operations ...
g_tiny_pool.phase4_bitmap_spill[cidx]++;
}
}
g_tiny_pool.phase4_spill_count[class_idx]++;
}
```
#### Helper 関数
```c
// Mini-magazine の容量と現在数
static inline int mini_capacity(PageMiniMag* mag) {
return mag->capacity;
}
static inline int mini_count(PageMiniMag* mag) {
return mag->count;
}
// min マクロ
#define min(a, b) ((a) < (b) ? (a) : (b))
```
### 検証方法
```bash
# Phase 4.3 実装
make clean && make bench_tiny
# ベンチマーク実行5回
for i in {1..5}; do
./bench_tiny 2>&1 | tail -5
done
# 統計確認
./test_mf2 2>&1 | grep -A 10 "Phase 4"
# 期待結果: 395-400 M ops/secPhase 3 超え)
```
### 成功基準
- **最低**: 390 M ops/secPhase 4.2 維持)
- **目標**: 395-400 M ops/secPhase 3 超え)
- **理想**: 400+ M ops/sec5% 改善)
---
## Phase 4.4: Pull 型反転(将来)
### 目標
- 実装時間: **1-2時間**
- 期待効果: **根本的解決**
- リスク: **高**(アーキテクチャ変更)
### コンセプト
**現状Push型**:
- Free 側spillで mini-mag に押し戻す
- すべての spill item に overhead
- 恩恵は allocation 側で発生(不確実)
**改善Pull型**:
- Allocation 側で必要時だけ mini-mag から引き上げる
- Free 側の overhead ゼロ
- Allocation latency は若干増加trade-off
### 実装箇所
**ファイル**: `hakmem_tiny.c`
**関数**: `hak_tiny_alloc()`
**タイミング**: Bitmap scan の直前
### 実装イメージ
```c
void* hak_tiny_alloc(size_t size) {
int class_idx = hak_tiny_size_to_class(size);
if (class_idx < 0) return NULL;
// 1. TLS Magazine (fast path)
if (!mini_mag_is_empty(&g_tls_mag[class_idx])) {
return mini_mag_pop(&g_tls_mag[class_idx]);
}
// 2. TLS Active Slabs (medium path)
TinySlab* tls = g_tls_active_slab_a[class_idx];
if (!(tls && tls->free_count > 0)) {
tls = g_tls_active_slab_b[class_idx];
}
if (tls && tls->free_count > 0) {
// Phase 4.4: Pull from page mini-mag
if (!mini_mag_is_empty(&tls->mini_mag)) {
void* p = mini_mag_pop(&tls->mini_mag);
if (p) {
stats_record_alloc(class_idx);
return p;
}
}
// Phase 4.4: Refill TLS Magazine from page mini-mag
if (!mini_mag_is_empty(&tls->mini_mag)) {
int pulled = mini_pull_batch(&tls->mini_mag, &g_tls_mag[class_idx], 16);
if (pulled > 0) {
void* p = mini_mag_pop(&g_tls_mag[class_idx]);
if (p) {
stats_record_alloc(class_idx);
return p;
}
}
}
// Fallback: Bitmap scan既存ロジック
// ...
}
// 3. Global pool既存ロジック
// ...
}
```
### Free 側の変更
```c
void hak_tiny_free_with_slab(...) {
// Phase 4.4: Push型ロジックを削除
// 全件 bitmap へ直書き(シンプル化)
for (int i = 0; i < mag->count; i++) {
PoolItem it = mag->items[i];
TinySlab* owner = hak_tiny_owner_slab(it.ptr);
if (!owner) continue;
// Bitmap へ spill既存ロジック
// ... bitmap operations ...
}
}
```
### Trade-off
**利点**:
- Free latency が安定overhead なし)
- Allocation 側で制御できる(必要時だけ pull
**欠点**:
- Allocation latency が若干増加mini-mag からの pull コスト)
- 実装が複雑
---
## 測定・診断
### 統計出力
```c
void hak_tiny_print_phase4_stats(void) {
printf("========================================\n");
printf("Phase 4 Statistics\n");
printf("========================================\n");
for (int i = 0; i < TINY_NUM_CLASSES; i++) {
uint64_t spill = g_tiny_pool.phase4_spill_count[i];
uint64_t mini = g_tiny_pool.phase4_mini_push[i];
uint64_t bitmap = g_tiny_pool.phase4_bitmap_spill[i];
uint64_t gate = g_tiny_pool.phase4_gate_skip[i];
if (spill == 0) continue;
double mini_ratio = (double)mini / (mini + bitmap) * 100;
double gate_ratio = (double)gate / spill * 100;
printf("Class %d (%zu B):\n", i, g_tiny_class_sizes[i]);
printf(" Spill count: %lu\n", spill);
printf(" Mini-mag push: %lu (%.1f%%)\n", mini, mini_ratio);
printf(" Bitmap spill: %lu (%.1f%%)\n", bitmap, 100 - mini_ratio);
printf(" Gate skip: %lu (%.1f%%)\n", gate, gate_ratio);
printf("\n");
}
}
```
### ベンチマーク比較
```bash
# Phase 3ベースライン
git checkout <phase3-commit>
make clean && make bench_tiny
./bench_tiny > results_phase3.txt
# Phase 4.1
git checkout <phase4.1-commit>
make clean && make bench_tiny
./bench_tiny > results_phase4_1.txt
# Phase 4.2
git checkout <phase4.2-commit>
make clean && make bench_tiny
./bench_tiny > results_phase4_2.txt
# Phase 4.3
git checkout <phase4.3-commit>
make clean && make bench_tiny
./bench_tiny > results_phase4_3.txt
# 比較
diff results_phase3.txt results_phase4_*.txt
```
---
## 成功基準まとめ
| Phase | 実装時間 | 期待性能 | リスク | 必須 |
|-------|---------|---------|-------|-----|
| 4.1 (A+B) | 5-10分 | 385-390 M ops/sec | 低 | ✅ Yes |
| 4.2 (ゲート) | 10-20分 | 390-395 M ops/sec | 低〜中 | ✅ Yes |
| 4.3 (バッチ) | 30-40分 | 395-400 M ops/sec | 中〜高 | ⚠️ Conditional |
| 4.4 (Pull) | 1-2時間 | 根本解決 | 高 | ❌ Future |
**Revert 条件**:
- Phase 4.2 実装後も < 385 M ops/sec Phase 4 全体を revert
**継続条件**:
- Phase 4.2 で >= 390 M ops/sec → Phase 4.3 に進む
---
## Timeline
**Day 1今日**:
1. ✅ ドキュメント整備(完了)
2. 🔄 Phase 4.1 実装5-10分
3. 🔄 Phase 4.1 検証5分
4. 🔄 Phase 4.2 実装10-20分
5. 🔄 Phase 4.2 検証5分
6. ✅ コミット
7. 📊 結果まとめ
**Day 2条件付き**:
- Phase 4.2 が成功した場合のみ
- Phase 4.3 実装・検証
**Future**:
- Phase 4.4Pull型は別途検討
---
## References
- PHASE4_REGRESSION_ANALYSIS.md詳細分析
- ChatGPT Pro アドバイス2025-10-26