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>
290 lines
7.2 KiB
Markdown
290 lines
7.2 KiB
Markdown
# 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)
|