# 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実装を始めましょう!