406 lines
8.8 KiB
Markdown
406 lines
8.8 KiB
Markdown
|
|
# 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 MB(Magazineのみ)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**結果:**
|
|||
|
|
- 割当は動的 ✅(必要な時だけ確保)
|
|||
|
|
- 解放も動的 ✅(空になったら返却)← 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-912(Magazine 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実装を始めましょう!
|