Files
hakmem/docs/archive/WHAT_IS_DYNAMIC.md

406 lines
8.8 KiB
Markdown
Raw Normal View History

# Phase 7.6: 何を「動的」にするのか?
**日付:** 2025-10-26
**質問:** "となると どこを動的にするんですかにゃ?"
---
## 🎯 結論SuperSlabの「生と死」を動的に
### Phase 7.6で動的にするもの
**1つだけです** SuperSlabのライフサイクル
```
❌ 現状(半分だけ動的):
割当: 動的(初回アクセス時に確保)✅
解放: 固定(一度確保したら永遠に保持)❌
✅ Phase 7.6(完全動的):
割当: 動的(初回アクセス時に確保)✅
解放: 動的空になったらOSに返却✅ ← これを実装!
```
---
## 📊 現状分析:何が固定で何が動的?
### 現在の実装Phase 6.24
**データ構造:**
```c
// TLS cacheスレッドごと
static __thread TinyTLSSlab g_tls_slabs[TINY_NUM_CLASSES];
typedef struct {
SuperSlab* ss; // SuperSlab pointer
TinySlabMeta* meta; // Slab metadata cache
uint8_t slab_idx; // Current slab index
} TinyTLSSlab;
```
**割当フロー:**
```c
malloc(16)
hak_tiny_alloc_superslab(class_idx=1) // 16B = class 1
TinyTLSSlab* tls = &g_tls_slabs[1];
if (tls->ss == NULL || slab満杯) {
superslab_refill(1); // ← 初回アクセス時に確保(動的!)
SuperSlab* ss = superslab_allocate(1); // mmap(2MB)
tls->ss = ss; // TLS cacheに保存
}
return block;
```
**解放フロー:**
```c
free(ptr)
hak_tiny_free(ptr)
Magazine に push // ← ここで止まる
SuperSlabへの通知なし
OSへの返却なし← ずっと保持!
```
---
## 🔍 何が「固定」で何が「動的」?
### 固定のものPhase 7.6でも変更なし)
| 項目 | 値 | 理由 |
|------|-----|------|
| SuperSlabサイズ | 2MB | mimalloc準拠、最適 |
| Slabサイズ | 64KB | 2MB / 32 = 64KB |
| Slabs per SuperSlab | 32個 | 固定bitmap効率 |
| サイズクラス | 8種8-64B | Tiny Pool範囲固定 |
| Magazine CAP | クラスごと固定 | 現状で十分高性能 |
**これらは変更しません!**
### 動的のもの(すでに実装済み)
| 項目 | 現状 | 実装 |
|------|------|------|
| SuperSlab割当 | ✅ 動的 | `superslab_refill()` で初回アクセス時 |
| Slab初期化 | ✅ 動的 | 必要になったslabのみ初期化 |
| TLS cache | ✅ 動的 | スレッドごとに自動管理 |
**これらはすでに動的です!**
### Phase 7.6で追加する動的要素
| 項目 | 現状 | Phase 7.6後 |
|------|------|-------------|
| **SuperSlab解放** | ❌ 固定(永遠保持) | ✅ 動的(空なら返却) |
**これだけです!** シンプルですね 🎯
---
## 🎨 図解:何が動的になるのか
### Before Phase 7.6(現状)
```
時間軸 →
起動時:
g_tls_slabs[0..7] = {NULL, NULL, ...} // 初期化のみ
SuperSlabs: 0個
初回 malloc(16):
superslab_refill(1) → mmap(2MB) → SuperSlab確保 ✅
g_tls_slabs[1].ss = SuperSlab#1
SuperSlabs: 1個
100K malloc(16):
必要に応じてSuperSlab確保 ✅
SuperSlabs: 3個
100K free(ptr):
Magazineにpush → SuperSlabは保持 ❌
SuperSlabs: 3個そのまま
500K malloc(16):
さらにSuperSlab確保 ✅
SuperSlabs: 7個
500K free(ptr):
Magazineにpush → SuperSlabは保持 ❌
SuperSlabs: 7個そのまま
1M malloc(16):
さらにSuperSlab確保 ✅
SuperSlabs: 13個
1M free(ptr):
Magazineにpush → SuperSlabは保持 ❌
SuperSlabs: 13個永遠に保持← 問題!
```
**結果:**
- 割当は動的 ✅(必要な時だけ確保)
- 解放は固定 ❌(一度確保したら永遠保持)
- **メモリが無駄!** 26MB常駐
---
### After Phase 7.6(目標)
```
時間軸 →
起動時:
g_tls_slabs[0..7] = {NULL, NULL, ...}
SuperSlabs: 0個 ✅
初回 malloc(16):
superslab_refill(1) → mmap(2MB) ✅
SuperSlabs: 1個
100K malloc(16):
SuperSlabs: 3個 ✅
100K free(ptr):
Magazine + SuperSlab追跡 ✅
total_active_blocks -= 100K
空SuperSlab検出 → munmap(2MB) × 3 ✅
SuperSlabs: 0個解放← 新機能!
500K malloc(16):
SuperSlabs: 7個 ✅
500K free(ptr):
空SuperSlab検出 → munmap × 7 ✅
SuperSlabs: 0個解放
1M malloc(16):
SuperSlabs: 13個 ✅
1M free(ptr):
空SuperSlab検出 → munmap × 13 ✅
SuperSlabs: 0個解放
終了時:
SuperSlabs: 0個 ✅
メモリ: ~2-3 MBMagazineのみ
```
**結果:**
- 割当は動的 ✅(必要な時だけ確保)
- 解放も動的 ✅(空になったら返却)← NEW!
- **メモリ効率MAX** 使用中のみ保持
---
## 🎯 具体的に「動的」とは?
### SuperSlabのライフサイクル
**Before半分動的**
```
Birth誕生: 動的 ✅
↓ malloc時、必要になったら
superslab_allocate() → mmap(2MB)
Life生存: 使用中
↓ malloc/freeを繰り返す
blocks割当・解放
Death: なし ❌
↓ プロセス終了まで
(永遠に保持)
```
**After Phase 7.6(完全動的):**
```
Birth誕生: 動的 ✅
↓ malloc時、必要になったら
superslab_allocate() → mmap(2MB)
Life生存: 使用中
↓ malloc/freeを繰り返す
total_active_blocks の増減を追跡 ← NEW!
Death: 動的 ✅ ← NEW!
↓ total_active_blocks == 0 になったら
superslab_free() → munmap(2MB) ← NEW!
OSへ返却、メモリ解放
```
---
## 📋 Phase 7.6の実装:具体的に何をする?
### Step 1-2: Magazine統合freeの追跡
**目的:** SuperSlabが「いつ空になったか」を検出
**実装:**
```c
// hakmem_tiny.c:908-912Magazine push
if (mag->top < cap) {
mag->items[mag->top].ptr = ptr;
mag->top++;
// Phase 7.6: 追跡追加 ← NEW!
SuperSlab* ss = ptr_to_superslab(ptr);
if (ss && ss->magic == SUPERSLAB_MAGIC) {
ss->total_active_blocks--; // カウンタ減算
}
return;
}
```
**これで:**
- ✅ freeを追跡できる
-`total_active_blocks` が正確になる
- ✅ 空検出が可能に
### Step 3: 空SuperSlab解放Deathの実装
**目的:** 空になったらOSに返却
**実装:**
```c
if (ss->total_active_blocks == 0) {
// 完全に空!
superslab_free(ss); // munmap(2MB) ← NEW!
g_tls_slabs[class_idx].ss = NULL; // TLS cacheクリア
}
```
**これで:**
- ✅ 空SuperSlabを解放
- ✅ OSにメモリ返却
- ✅ メモリ効率MAX
### Step 4: 遅延割当(すでに実装済み)
**現状:**
```c
// superslab_refill() (line 1027)
// 初回アクセス時のみ確保 ← すでに動的!
if (tls->ss == NULL) {
tls->ss = superslab_allocate(class_idx);
}
```
**Phase 7.6で追加:**
- 特になし!
- すでに遅延割当されている
**でも明示的に:**
```c
// グローバル配列も管理(解放追跡用)
static SuperSlab* g_active_superslabs[TINY_NUM_CLASSES] = {NULL};
```
---
## 🎓 他の「動的」との違い
### ACEの「動的」Mid/Large Pool
**何が動的?**
- CAP在庫量: ヒット率で調整
- W_MAX丸め度: UCB1学習
- しきい値: Canaryテスト
**手法:** 学習ベース
### Phase 7.6の「動的」Tiny Pool
**何が動的?**
- SuperSlabの解放: 空検出で返却
**手法:** 設計ベースBitmapの柔軟性
**共通点:**
- どちらも「必要な時だけ確保、不要になったら解放」
- でも実装手法が異なる
---
## 🚀 まとめ
### Phase 7.6で動的にするもの
**たった1つ**
```
SuperSlabの解放
```
**具体的には:**
```c
// Before
SuperSlab確保 → 永遠保持 ❌
// After
SuperSlab確保 → 使用 → 空検出 → OS返却 ✅
```
**これだけです!**
### なぜ「全部動的」と言ったのか?
**「全部動的」の意味:**
```
割当も動的 ✅(すでに実装済み)
解放も動的 ✅Phase 7.6で追加)
= SuperSlabのライフサイクル全体が動的
= 「全部動的」
```
**vs 中途半端(悪い例):**
```
割当は動的 ✅
解放は固定 ❌
= 半分だけ動的
= 中途半端 ❌
```
### 他に動的にしないもの
**これらは固定のまま:**
- ❌ SuperSlabサイズ2MB固定
- ❌ サイズクラス8種固定
- ❌ Magazine CAPクラスごと固定
**理由:**
- すでに最適
- 動的化の価値なし
- ACEと同じ「動的1個問題」を避ける
---
## 🎯 次のステップ
**やること:**
1. Magazine統合Step 1-2
2. 空SuperSlab解放Step 3
**やらないこと:**
- SuperSlabサイズの動的化不要
- Magazine CAPの学習不要
- ACE統合独立が美しい
**にゃーん!わかりましたか?** 🐱
---
**次:** Step 1実装を始めましょう