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>
7.2 KiB
7.2 KiB
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 (スラブ単位)
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 (グローバル状態)
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) 高速パス)
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) 高速パス)
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]++;
}
🔍 ビットマップ操作(高速化の肝)
空きブロック検索
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; // 空きなし
}
ビット設定/クリア
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 への統合
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 exporttest_hakmem.c- Tiny Pool テスト追加Makefile- hakmem_tiny.o 追加
🧪 テスト計画
単体テスト
- 基本動作: 8クラス × alloc/free
- スラブ遷移: 空き → 満杯 → 空き
- ビットマップ正確性: 全ブロック exhaustive test
- 境界条件: 0B, 1B, 1024B, 1025B
統合テスト
- ベンチマーク: tiny/small/json シナリオ
- ストレステスト: 100万回 alloc/free
- メモリリーク: 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)