Files
hakmem/docs/archive/PHASE_6.12_TINY_POOL.md

290 lines
7.2 KiB
Markdown
Raw Normal View History

# 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)