# Phase 6.12: Tiny Pool - 超小サイズ専用アロケータ **Date**: 2025-10-21 **Status**: 実装中 **優先度**: P1(最初に実装) --- ## 🎯 目標 **≤1KB allocations 専用の固定サイズスラブアロケータ** **期待効果**: - mimalloc比 -10-20% (tiny allocations) - L1キャッシュヒット率向上 - メタデータオーバーヘッド削減 --- ## 📊 サイズクラス設計 ### 8つの固定サイズクラス | Class | Size | Blocks/Slab (64KB) | Bitmap (uint64_t) | |-------|------|-------------------|-------------------| | 0 | 8B | 8192 | 128 | | 1 | 16B | 4096 | 64 | | 2 | 32B | 2048 | 32 | | 3 | 64B | 1024 | 16 | | 4 | 128B | 512 | 8 | | 5 | 256B | 256 | 4 | | 6 | 512B | 128 | 2 | | 7 | 1KB | 64 | 1 | **Bitmap計算**: blocks/slab ÷ 64 = bitmap配列サイズ --- ## 🏗️ データ構造 ### TinySlab (スラブ単位) ```c typedef struct TinySlab { void* base; // スラブのベースアドレス (64KB aligned) uint64_t* bitmap; // 空きブロックのビットマップ(動的サイズ) uint16_t free_count; // 空きブロック数 uint16_t total_count; // 総ブロック数 uint8_t class_idx; // サイズクラスインデックス (0-7) uint8_t _padding[5]; struct TinySlab* next; // 次のスラブ } TinySlab; ``` ### TinyPool (グローバル状態) ```c typedef struct { TinySlab* slabs[8]; // 各クラスのスラブリスト(空きがあるスラブのみ) TinySlab* full_slabs[8]; // 満杯スラブリスト(将来のfree用) uint64_t alloc_count[8]; // 各クラスのallocation統計 uint64_t free_count[8]; // 各クラスのfree統計 uint64_t slab_count[8]; // 各クラスのスラブ数 } TinyPool; ``` --- ## ⚡ アルゴリズム ### Allocation (O(1) 高速パス) ```c void* hak_tiny_alloc(size_t size) { // 1. サイズ → クラスインデックス(branchless LUT) int class_idx = tiny_size_to_class(size); if (class_idx < 0) return NULL; // >1KB // 2. 空きスラブを取得 TinySlab* slab = g_tiny_pool.slabs[class_idx]; if (!slab) { // 新しいスラブを確保 slab = allocate_new_slab(class_idx); if (!slab) return NULL; } // 3. ビットマップから空きブロックを検索 (__builtin_ctzll) int block_idx = find_free_block(slab); if (block_idx < 0) { // スラブが満杯 → 次のスラブへ move_to_full_list(class_idx, slab); return hak_tiny_alloc(size); // リトライ } // 4. ビットマップをセット、ポインタを返す set_block_used(slab, block_idx); slab->free_count--; size_t block_size = g_tiny_class_sizes[class_idx]; void* ptr = (char*)slab->base + (block_idx * block_size); g_tiny_pool.alloc_count[class_idx]++; return ptr; } ``` ### Free (O(1) 高速パス) ```c void hak_tiny_free(void* ptr) { // 1. ポインタ → スラブ(64KB境界でアライン) uintptr_t addr = (uintptr_t)ptr; uintptr_t slab_base = addr & ~(SLAB_SIZE - 1); // 64KB境界 TinySlab* slab = find_slab_by_base(slab_base); if (!slab) return; // Tiny Pool外 // 2. ブロックインデックスを計算 size_t block_size = g_tiny_class_sizes[slab->class_idx]; int block_idx = (addr - slab_base) / block_size; // 3. ビットマップをクリア clear_block_used(slab, block_idx); slab->free_count++; // 4. スラブが満杯→空きリストへ移動 if (slab->free_count == 1 && slab->total_count > 1) { move_to_free_list(slab->class_idx, slab); } // 5. スラブが完全に空 → 解放(またはキャッシュ) if (slab->free_count == slab->total_count) { release_slab(slab); } g_tiny_pool.free_count[slab->class_idx]++; } ``` --- ## 🔍 ビットマップ操作(高速化の肝) ### 空きブロック検索 ```c static inline int find_free_block(TinySlab* slab) { int bitmap_size = (slab->total_count + 63) / 64; for (int i = 0; i < bitmap_size; i++) { uint64_t bits = ~slab->bitmap[i]; // 0=空き, 1=使用中 if (bits != 0) { // 最下位の0ビット(空きブロック)を検索 int bit_idx = __builtin_ctzll(bits); return i * 64 + bit_idx; } } return -1; // 空きなし } ``` ### ビット設定/クリア ```c static inline void set_block_used(TinySlab* slab, int block_idx) { int word_idx = block_idx / 64; int bit_idx = block_idx % 64; slab->bitmap[word_idx] |= (1ULL << bit_idx); } static inline void clear_block_used(TinySlab* slab, int block_idx) { int word_idx = block_idx / 64; int bit_idx = block_idx % 64; slab->bitmap[word_idx] &= ~(1ULL << bit_idx); } ``` --- ## 🚀 統合方法 ### hakmem.c への統合 ```c void* hak_alloc_at(size_t size, uintptr_t site) { // Phase 6.12: Tiny Pool 優先パス if (size <= 1024) { void* ptr = hak_tiny_alloc(size); if (ptr) return ptr; // fallback to L2 Pool } // 既存のL2 Pool / BigCache / malloc/mmap パス // ... } void hak_free(void* ptr) { if (!ptr) return; // Phase 6.12: Tiny Pool チェック if (hak_tiny_is_managed(ptr)) { hak_tiny_free(ptr); return; } // 既存のL2 Pool / BigCache / malloc/mmap パス // ... } ``` --- ## 📈 期待される効果 ### ベンチマーク見込み | Scenario | Before | After | Improvement | |----------|--------|-------|-------------| | **tiny** (8-64B) | +30% vs mimalloc | **-5%** | -35% | | **small** (128-512B) | +15% vs mimalloc | **-10%** | -25% | | **json** (64KB) | +0.3% vs mimalloc | **-2%** | -2.3% | **総合効果**: mimalloc比 -10-20% (tiny allocations中心のワークロード) --- ## 🔧 実装ファイル ### 新規作成 - `hakmem_tiny.h` (120 lines) - API定義 - `hakmem_tiny.c` (300 lines) - スラブアロケータ実装 ### 修正 - `hakmem.c` - Tiny Pool統合(hak_alloc_at, hak_free) - `hakmem.h` - Tiny Pool API export - `test_hakmem.c` - Tiny Pool テスト追加 - `Makefile` - hakmem_tiny.o 追加 --- ## 🧪 テスト計画 ### 単体テスト 1. **基本動作**: 8クラス × alloc/free 2. **スラブ遷移**: 空き → 満杯 → 空き 3. **ビットマップ正確性**: 全ブロック exhaustive test 4. **境界条件**: 0B, 1B, 1024B, 1025B ### 統合テスト 1. **ベンチマーク**: tiny/small/json シナリオ 2. **ストレステスト**: 100万回 alloc/free 3. **メモリリーク**: valgrind / AddressSanitizer --- ## 🎯 実装スケジュール ### Step 1: ヘッダー・基本実装 (30分) - `hakmem_tiny.h` 作成 - `hakmem_tiny.c` 骨格作成 - サイズクラス定義 ### Step 2: alloc/free実装 (45分) - `hak_tiny_alloc()` 実装 - `hak_tiny_free()` 実装 - ビットマップ操作実装 ### Step 3: 統合 (30分) - `hakmem.c` に統合 - Makefile 更新 - ビルド確認 ### Step 4: テスト (30分) - 基本動作テスト作成・実行 - ベンチマーク測定 --- **Total**: 約2時間(実装 + テスト) --- **Generated**: 2025-10-21 **実装者**: Claude (ultrathink mode)