Files
hakmem/docs/archive/WHAT_IS_DYNAMIC.md
Moe Charm (CI) 52386401b3 Debug Counters Implementation - Clean History
Major Features:
- Debug counter infrastructure for Refill Stage tracking
- Free Pipeline counters (ss_local, ss_remote, tls_sll)
- Diagnostic counters for early return analysis
- Unified larson.sh benchmark runner with profiles
- Phase 6-3 regression analysis documentation

Bug Fixes:
- Fix SuperSlab disabled by default (HAKMEM_TINY_USE_SUPERSLAB)
- Fix profile variable naming consistency
- Add .gitignore patterns for large files

Performance:
- Phase 6-3: 4.79 M ops/s (has OOM risk)
- With SuperSlab: 3.13 M ops/s (+19% improvement)

This is a clean repository without large log files.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-05 12:31:14 +09:00

8.8 KiB
Raw Blame History

Phase 7.6: 何を「動的」にするのか?

日付: 2025-10-26 質問: "となると どこを動的にするんですかにゃ?"


🎯 結論SuperSlabの「生と死」を動的に

Phase 7.6で動的にするもの

1つだけです SuperSlabのライフサイクル

❌ 現状(半分だけ動的):
  割当: 動的(初回アクセス時に確保)✅
  解放: 固定(一度確保したら永遠に保持)❌

✅ Phase 7.6(完全動的):
  割当: 動的(初回アクセス時に確保)✅
  解放: 動的空になったらOSに返却✅ ← これを実装!

📊 現状分析:何が固定で何が動的?

現在の実装Phase 6.24

データ構造:

// 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;

割当フロー:

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;

解放フロー:

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が「いつ空になったか」を検出

実装:

// 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に返却

実装:

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: 遅延割当(すでに実装済み)

現状:

// superslab_refill() (line 1027)
// 初回アクセス時のみ確保 ← すでに動的!
if (tls->ss == NULL) {
    tls->ss = superslab_allocate(class_idx);
}

Phase 7.6で追加:

  • 特になし!
  • すでに遅延割当されている

でも明示的に:

// グローバル配列も管理(解放追跡用)
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の解放

具体的には:

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