647 lines
18 KiB
Markdown
647 lines
18 KiB
Markdown
|
|
# Slab Registry ON/OFF 切り替え設計 - 完全分析レポート
|
|||
|
|
|
|||
|
|
**調査日**: 2025-10-22
|
|||
|
|
**目的**: Registry の有無で性能が大きく変わるため、簡単に切り替えてベンチマーク比較できる仕組みを導入
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 📊 **現状の問題**
|
|||
|
|
|
|||
|
|
### パフォーマンス差(Phase 6.12.1)
|
|||
|
|
|
|||
|
|
| Pattern | 1-thread | 4-thread |
|
|||
|
|
|---------|----------|----------|
|
|||
|
|
| **Registry ON** | +0.8% ✅ | **-93.8%** ❌ (Race Condition) |
|
|||
|
|
| **Registry OFF** | -2.9% | **-22.4%** (Cache line ping-pong) |
|
|||
|
|
|
|||
|
|
### 切り替えの課題
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 現状: git revert が必要
|
|||
|
|
git log --oneline -5
|
|||
|
|
git revert abc123
|
|||
|
|
make clean && make
|
|||
|
|
./bench_allocators_hakmem
|
|||
|
|
|
|||
|
|
# これを環境変数で切り替えたい!
|
|||
|
|
HAKMEM_USE_REGISTRY=0 ./bench_allocators_hakmem
|
|||
|
|
HAKMEM_USE_REGISTRY=1 ./bench_allocators_hakmem
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🔍 **影響範囲の分析**
|
|||
|
|
|
|||
|
|
### Registry 実装の箇所(hakmem_tiny.c)
|
|||
|
|
|
|||
|
|
| 行番号 | 関数/変数 | 種別 | 影響 |
|
|||
|
|
|--------|----------|------|------|
|
|||
|
|
| 16 | `g_slab_registry[1024]` | グローバル変数 | 8KB メモリ |
|
|||
|
|
| 25-92 | `registry_hash/register/unregister/lookup` | 内部関数 | 67行 |
|
|||
|
|
| 128-134 | `allocate_new_slab()` | Registry登録 | 6行 |
|
|||
|
|
| 147 | `release_slab()` | Registry解除 | 1行 |
|
|||
|
|
| 158-166 | `hak_tiny_owner_slab()` | Registry検索 | 8行 |
|
|||
|
|
| 223 | `hak_tiny_init()` | Registry初期化 | 1行 |
|
|||
|
|
|
|||
|
|
**合計**: ~83行のコード、6箇所の呼び出し
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## ⚖️ **3つのパターン比較**
|
|||
|
|
|
|||
|
|
### Pattern 1: コンパイル時フラグ (`#ifdef`)
|
|||
|
|
|
|||
|
|
#### 実装案
|
|||
|
|
|
|||
|
|
```c
|
|||
|
|
// hakmem_tiny.h
|
|||
|
|
#define HAKMEM_USE_SLAB_REGISTRY 1 // 0 = OFF, 1 = ON
|
|||
|
|
|
|||
|
|
#if HAKMEM_USE_SLAB_REGISTRY
|
|||
|
|
extern SlabRegistryEntry g_slab_registry[SLAB_REGISTRY_SIZE];
|
|||
|
|
#endif
|
|||
|
|
|
|||
|
|
// hakmem_tiny.c
|
|||
|
|
#if HAKMEM_USE_SLAB_REGISTRY
|
|||
|
|
SlabRegistryEntry g_slab_registry[SLAB_REGISTRY_SIZE];
|
|||
|
|
|
|||
|
|
static TinySlab* registry_lookup(uintptr_t slab_base) {
|
|||
|
|
// ... O(1) lookup ...
|
|||
|
|
}
|
|||
|
|
#endif
|
|||
|
|
|
|||
|
|
TinySlab* hak_tiny_owner_slab(void* ptr) {
|
|||
|
|
uintptr_t slab_base = (uintptr_t)ptr & ~(TINY_SLAB_SIZE - 1);
|
|||
|
|
|
|||
|
|
#if HAKMEM_USE_SLAB_REGISTRY
|
|||
|
|
return registry_lookup(slab_base); // O(1)
|
|||
|
|
#else
|
|||
|
|
// O(N) list traversal
|
|||
|
|
for (int class_idx = 0; class_idx < TINY_NUM_CLASSES; class_idx++) {
|
|||
|
|
for (TinySlab* slab = g_tiny_pool.free_slabs[class_idx]; slab; slab = slab->next) {
|
|||
|
|
if ((uintptr_t)slab->base == slab_base) return slab;
|
|||
|
|
}
|
|||
|
|
for (TinySlab* slab = g_tiny_pool.full_slabs[class_idx]; slab; slab = slab->next) {
|
|||
|
|
if ((uintptr_t)slab->base == slab_base) return slab;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return NULL;
|
|||
|
|
#endif
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### メリット/デメリット
|
|||
|
|
|
|||
|
|
| 項目 | 評価 | 詳細 |
|
|||
|
|
|------|------|------|
|
|||
|
|
| **オーバーヘッド** | ⭐⭐⭐⭐⭐ | **0 cycles** - コンパイル時に完全に最適化 |
|
|||
|
|
| **コードサイズ** | ⭐⭐⭐⭐⭐ | 最小(未使用コードは削除) |
|
|||
|
|
| **ベンチマーク比較** | ⭐⭐ | **2回ビルドが必要** |
|
|||
|
|
| **実装の容易さ** | ⭐⭐⭐⭐ | 6箇所に `#if` 追加 |
|
|||
|
|
| **メンテナンス性** | ⭐⭐⭐ | 2つの経路を維持(ビルド時検証必要) |
|
|||
|
|
|
|||
|
|
#### 切り替え方法
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# Registry ON
|
|||
|
|
make clean
|
|||
|
|
sed -i 's/HAKMEM_USE_SLAB_REGISTRY 0/HAKMEM_USE_SLAB_REGISTRY 1/' hakmem_tiny.h
|
|||
|
|
make bench
|
|||
|
|
|
|||
|
|
# Registry OFF
|
|||
|
|
make clean
|
|||
|
|
sed -i 's/HAKMEM_USE_SLAB_REGISTRY 1/HAKMEM_USE_SLAB_REGISTRY 0/' hakmem_tiny.h
|
|||
|
|
make bench
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### Pattern 2: ランタイム環境変数 (`HAKMEM_USE_REGISTRY`)
|
|||
|
|
|
|||
|
|
#### 実装案
|
|||
|
|
|
|||
|
|
```c
|
|||
|
|
// hakmem_tiny.c
|
|||
|
|
static int g_use_registry = 1; // Default ON (0 = OFF, 1 = ON)
|
|||
|
|
|
|||
|
|
void hak_tiny_init(void) {
|
|||
|
|
if (g_tiny_initialized) return;
|
|||
|
|
|
|||
|
|
// Read environment variable
|
|||
|
|
char* env = getenv("HAKMEM_USE_REGISTRY");
|
|||
|
|
if (env) {
|
|||
|
|
g_use_registry = atoi(env);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
g_tiny_initialized = 1;
|
|||
|
|
|
|||
|
|
if (g_use_registry) {
|
|||
|
|
memset(g_slab_registry, 0, sizeof(g_slab_registry));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Lite P1 pre-allocation (省略)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
TinySlab* hak_tiny_owner_slab(void* ptr) {
|
|||
|
|
if (!ptr || !g_tiny_initialized) return NULL;
|
|||
|
|
|
|||
|
|
uintptr_t slab_base = (uintptr_t)ptr & ~(TINY_SLAB_SIZE - 1);
|
|||
|
|
|
|||
|
|
if (g_use_registry) {
|
|||
|
|
return registry_lookup(slab_base); // O(1)
|
|||
|
|
} else {
|
|||
|
|
// O(N) list traversal
|
|||
|
|
for (int class_idx = 0; class_idx < TINY_NUM_CLASSES; class_idx++) {
|
|||
|
|
for (TinySlab* slab = g_tiny_pool.free_slabs[class_idx]; slab; slab = slab->next) {
|
|||
|
|
if ((uintptr_t)slab->base == slab_base) return slab;
|
|||
|
|
}
|
|||
|
|
for (TinySlab* slab = g_tiny_pool.full_slabs[class_idx]; slab; slab = slab->next) {
|
|||
|
|
if ((uintptr_t)slab->base == slab_base) return slab;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return NULL;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static TinySlab* allocate_new_slab(int class_idx) {
|
|||
|
|
// ... (省略) ...
|
|||
|
|
|
|||
|
|
// Registry登録(条件付き)
|
|||
|
|
if (g_use_registry) {
|
|||
|
|
uintptr_t slab_base = (uintptr_t)aligned_mem;
|
|||
|
|
if (!registry_register(slab_base, slab)) {
|
|||
|
|
// Registry full - cleanup and fail
|
|||
|
|
free(slab->bitmap);
|
|||
|
|
free(slab->base);
|
|||
|
|
free(slab);
|
|||
|
|
return NULL;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return slab;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static void release_slab(TinySlab* slab) {
|
|||
|
|
if (!slab) return;
|
|||
|
|
|
|||
|
|
if (g_use_registry) {
|
|||
|
|
uintptr_t slab_base = (uintptr_t)slab->base;
|
|||
|
|
registry_unregister(slab_base);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ... (省略) ...
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### メリット/デメリット
|
|||
|
|
|
|||
|
|
| 項目 | 評価 | 詳細 |
|
|||
|
|
|------|------|------|
|
|||
|
|
| **オーバーヘッド** | ⭐⭐⭐⭐ | **~2-5 cycles** - 単純な if分岐(予測可能) |
|
|||
|
|
| **コードサイズ** | ⭐⭐⭐ | 両方の経路を含む(+30行程度) |
|
|||
|
|
| **ベンチマーク比較** | ⭐⭐⭐⭐⭐ | **1回ビルド、環境変数で即切り替え** |
|
|||
|
|
| **実装の容易さ** | ⭐⭐⭐⭐⭐ | 3箇所に `if (g_use_registry)` 追加 |
|
|||
|
|
| **メンテナンス性** | ⭐⭐⭐⭐ | 両方の経路が常に実行可能(CI/テストで検証容易) |
|
|||
|
|
|
|||
|
|
#### 切り替え方法
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 1回ビルドするだけ!
|
|||
|
|
make bench
|
|||
|
|
|
|||
|
|
# Registry ON
|
|||
|
|
HAKMEM_USE_REGISTRY=1 ./bench_allocators_hakmem
|
|||
|
|
|
|||
|
|
# Registry OFF
|
|||
|
|
HAKMEM_USE_REGISTRY=0 ./bench_allocators_hakmem
|
|||
|
|
|
|||
|
|
# 自動比較スクリプト
|
|||
|
|
bash bench_registry_comparison.sh
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### Pattern 3: 関数ポインタ切り替え
|
|||
|
|
|
|||
|
|
#### 実装案
|
|||
|
|
|
|||
|
|
```c
|
|||
|
|
// hakmem_tiny.c
|
|||
|
|
static TinySlab* (*g_owner_slab_impl)(void* ptr) = NULL;
|
|||
|
|
|
|||
|
|
static TinySlab* owner_slab_registry(void* ptr) {
|
|||
|
|
uintptr_t slab_base = (uintptr_t)ptr & ~(TINY_SLAB_SIZE - 1);
|
|||
|
|
return registry_lookup(slab_base); // O(1)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
static TinySlab* owner_slab_list(void* ptr) {
|
|||
|
|
uintptr_t slab_base = (uintptr_t)ptr & ~(TINY_SLAB_SIZE - 1);
|
|||
|
|
|
|||
|
|
// O(N) list traversal
|
|||
|
|
for (int class_idx = 0; class_idx < TINY_NUM_CLASSES; class_idx++) {
|
|||
|
|
for (TinySlab* slab = g_tiny_pool.free_slabs[class_idx]; slab; slab = slab->next) {
|
|||
|
|
if ((uintptr_t)slab->base == slab_base) return slab;
|
|||
|
|
}
|
|||
|
|
for (TinySlab* slab = g_tiny_pool.full_slabs[class_idx]; slab; slab = slab->next) {
|
|||
|
|
if ((uintptr_t)slab->base == slab_base) return slab;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return NULL;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void hak_tiny_init(void) {
|
|||
|
|
if (g_tiny_initialized) return;
|
|||
|
|
|
|||
|
|
// Read environment variable
|
|||
|
|
char* env = getenv("HAKMEM_USE_REGISTRY");
|
|||
|
|
int use_registry = env ? atoi(env) : 1;
|
|||
|
|
|
|||
|
|
// Set function pointer (1回だけ分岐)
|
|||
|
|
g_owner_slab_impl = use_registry ? owner_slab_registry : owner_slab_list;
|
|||
|
|
|
|||
|
|
g_tiny_initialized = 1;
|
|||
|
|
|
|||
|
|
if (use_registry) {
|
|||
|
|
memset(g_slab_registry, 0, sizeof(g_slab_registry));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ... (省略) ...
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
TinySlab* hak_tiny_owner_slab(void* ptr) {
|
|||
|
|
if (!ptr || !g_tiny_initialized) return NULL;
|
|||
|
|
return g_owner_slab_impl(ptr); // 間接呼び出し
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### メリット/デメリット
|
|||
|
|
|
|||
|
|
| 項目 | 評価 | 詳細 |
|
|||
|
|
|------|------|------|
|
|||
|
|
| **オーバーヘッド** | ⭐⭐⭐ | **~5-10 cycles** - 間接呼び出し(分岐予測器を使えない) |
|
|||
|
|
| **コードサイズ** | ⭐⭐⭐⭐ | 両方の経路を別関数に分離(読みやすい) |
|
|||
|
|
| **ベンチマーク比較** | ⭐⭐⭐⭐⭐ | **1回ビルド、環境変数で即切り替え** |
|
|||
|
|
| **実装の容易さ** | ⭐⭐⭐ | 関数分離 + ポインタ初期化(やや複雑) |
|
|||
|
|
| **メンテナンス性** | ⭐⭐⭐⭐⭐ | 2つの実装が完全に分離(独立テスト可能) |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🎯 **推奨パターン: Pattern 2 (ランタイム環境変数)**
|
|||
|
|
|
|||
|
|
### 選択理由
|
|||
|
|
|
|||
|
|
#### 1️⃣ **ベンチマーク比較の容易さ(最重要!)**
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 1回ビルド → 環境変数で即切り替え
|
|||
|
|
make bench
|
|||
|
|
|
|||
|
|
# 自動比較スクリプトで一発測定
|
|||
|
|
bash bench_registry_comparison.sh
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
ユーザーの要望「簡単に切り替えて比較」を完璧に満たす。
|
|||
|
|
|
|||
|
|
#### 2️⃣ **オーバーヘッドが実質ゼロ**
|
|||
|
|
|
|||
|
|
- **if分岐のコスト**: ~2-5 cycles
|
|||
|
|
- **Registry自体のコスト**: ~50-100 cycles (hash + probing)
|
|||
|
|
- **O(N)探索のコスト**: ~200-500 cycles (8 classes × 2 lists)
|
|||
|
|
|
|||
|
|
**オーバーヘッド比**: 2-5 / 50-100 = **2-10%**(誤差範囲)
|
|||
|
|
|
|||
|
|
#### 3️⃣ **実装が最も簡単**
|
|||
|
|
|
|||
|
|
```diff
|
|||
|
|
+ static int g_use_registry = 1; // 1行追加
|
|||
|
|
|
|||
|
|
void hak_tiny_init(void) {
|
|||
|
|
+ char* env = getenv("HAKMEM_USE_REGISTRY");
|
|||
|
|
+ if (env) g_use_registry = atoi(env); // 2行追加
|
|||
|
|
|
|||
|
|
- memset(g_slab_registry, 0, sizeof(g_slab_registry));
|
|||
|
|
+ if (g_use_registry) { // 1行追加
|
|||
|
|
+ memset(g_slab_registry, 0, sizeof(g_slab_registry));
|
|||
|
|
+ }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
TinySlab* hak_tiny_owner_slab(void* ptr) {
|
|||
|
|
- return registry_lookup(slab_base);
|
|||
|
|
+ if (g_use_registry) { // 1行追加
|
|||
|
|
+ return registry_lookup(slab_base);
|
|||
|
|
+ } else {
|
|||
|
|
+ // O(N) fallback (10行追加)
|
|||
|
|
+ }
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**合計**: ~15行の追加のみ
|
|||
|
|
|
|||
|
|
#### 4️⃣ **メンテナンス性が高い**
|
|||
|
|
|
|||
|
|
- 両方の経路が常にコンパイルされる → ビルドエラーが即座に発覚
|
|||
|
|
- CI/テストで両方をテスト可能
|
|||
|
|
- デバッグも容易(環境変数を変えるだけ)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 📝 **具体的な実装コード(Pattern 2)**
|
|||
|
|
|
|||
|
|
### Step 1: グローバル変数追加(hakmem_tiny.c)
|
|||
|
|
|
|||
|
|
```c
|
|||
|
|
// ============================================================================
|
|||
|
|
// Global State
|
|||
|
|
// ============================================================================
|
|||
|
|
|
|||
|
|
static TinyPool g_tiny_pool;
|
|||
|
|
int g_tiny_initialized = 0;
|
|||
|
|
|
|||
|
|
// +++ 追加 +++
|
|||
|
|
static int g_use_registry = 1; // Default ON (0 = OFF, 1 = ON)
|
|||
|
|
// +++ ここまで +++
|
|||
|
|
|
|||
|
|
SlabRegistryEntry g_slab_registry[SLAB_REGISTRY_SIZE];
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Step 2: 初期化関数修正(hak_tiny_init)
|
|||
|
|
|
|||
|
|
```c
|
|||
|
|
void hak_tiny_init(void) {
|
|||
|
|
if (g_tiny_initialized) return;
|
|||
|
|
|
|||
|
|
// +++ 追加 +++
|
|||
|
|
// Read environment variable
|
|||
|
|
char* env = getenv("HAKMEM_USE_REGISTRY");
|
|||
|
|
if (env) {
|
|||
|
|
g_use_registry = atoi(env);
|
|||
|
|
}
|
|||
|
|
// +++ ここまで +++
|
|||
|
|
|
|||
|
|
g_tiny_initialized = 1;
|
|||
|
|
|
|||
|
|
// +++ 修正 +++
|
|||
|
|
if (g_use_registry) {
|
|||
|
|
memset(g_slab_registry, 0, sizeof(g_slab_registry));
|
|||
|
|
}
|
|||
|
|
// +++ ここまで +++
|
|||
|
|
|
|||
|
|
// Lite P1 pre-allocation (省略)
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Step 3: owner_slab 修正
|
|||
|
|
|
|||
|
|
```c
|
|||
|
|
TinySlab* hak_tiny_owner_slab(void* ptr) {
|
|||
|
|
if (!ptr || !g_tiny_initialized) return NULL;
|
|||
|
|
|
|||
|
|
uintptr_t slab_base = (uintptr_t)ptr & ~(TINY_SLAB_SIZE - 1);
|
|||
|
|
|
|||
|
|
// +++ 追加 +++
|
|||
|
|
if (g_use_registry) {
|
|||
|
|
return registry_lookup(slab_base); // O(1)
|
|||
|
|
} else {
|
|||
|
|
// O(N) list traversal fallback
|
|||
|
|
for (int class_idx = 0; class_idx < TINY_NUM_CLASSES; class_idx++) {
|
|||
|
|
// Check free list
|
|||
|
|
for (TinySlab* slab = g_tiny_pool.free_slabs[class_idx]; slab; slab = slab->next) {
|
|||
|
|
if ((uintptr_t)slab->base == slab_base) {
|
|||
|
|
return slab;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
// Check full list
|
|||
|
|
for (TinySlab* slab = g_tiny_pool.full_slabs[class_idx]; slab; slab = slab->next) {
|
|||
|
|
if ((uintptr_t)slab->base == slab_base) {
|
|||
|
|
return slab;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return NULL; // Not found
|
|||
|
|
}
|
|||
|
|
// +++ ここまで +++
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Step 4: allocate_new_slab 修正
|
|||
|
|
|
|||
|
|
```c
|
|||
|
|
static TinySlab* allocate_new_slab(int class_idx) {
|
|||
|
|
// ... (省略) ...
|
|||
|
|
|
|||
|
|
slab->next = NULL;
|
|||
|
|
|
|||
|
|
// +++ 修正 +++
|
|||
|
|
if (g_use_registry) {
|
|||
|
|
uintptr_t slab_base = (uintptr_t)aligned_mem;
|
|||
|
|
if (!registry_register(slab_base, slab)) {
|
|||
|
|
// Registry full - cleanup and fail
|
|||
|
|
free(slab->bitmap);
|
|||
|
|
free(slab->base);
|
|||
|
|
free(slab);
|
|||
|
|
return NULL;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
// +++ ここまで +++
|
|||
|
|
|
|||
|
|
g_tiny_pool.slab_count[class_idx]++;
|
|||
|
|
return slab;
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Step 5: release_slab 修正
|
|||
|
|
|
|||
|
|
```c
|
|||
|
|
static void release_slab(TinySlab* slab) {
|
|||
|
|
if (!slab) return;
|
|||
|
|
|
|||
|
|
// +++ 修正 +++
|
|||
|
|
if (g_use_registry) {
|
|||
|
|
uintptr_t slab_base = (uintptr_t)slab->base;
|
|||
|
|
registry_unregister(slab_base);
|
|||
|
|
}
|
|||
|
|
// +++ ここまで +++
|
|||
|
|
|
|||
|
|
free(slab->base);
|
|||
|
|
free(slab->bitmap);
|
|||
|
|
g_tiny_pool.slab_count[slab->class_idx]--;
|
|||
|
|
free(slab);
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🚀 **ベンチマーク自動化スクリプト**
|
|||
|
|
|
|||
|
|
### bench_registry_comparison.sh
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
#!/bin/bash
|
|||
|
|
# Registry ON/OFF 性能比較スクリプト
|
|||
|
|
|
|||
|
|
set -e
|
|||
|
|
|
|||
|
|
# Build benchmark
|
|||
|
|
echo "Building benchmark..."
|
|||
|
|
make bench
|
|||
|
|
|
|||
|
|
# Output file
|
|||
|
|
RESULT_FILE="registry_comparison_$(date +%Y%m%d_%H%M%S).txt"
|
|||
|
|
|
|||
|
|
echo "========================================" | tee -a "$RESULT_FILE"
|
|||
|
|
echo "Slab Registry ON/OFF Comparison" | tee -a "$RESULT_FILE"
|
|||
|
|
echo "Date: $(date)" | tee -a "$RESULT_FILE"
|
|||
|
|
echo "========================================" | tee -a "$RESULT_FILE"
|
|||
|
|
|
|||
|
|
# Registry ON
|
|||
|
|
echo "" | tee -a "$RESULT_FILE"
|
|||
|
|
echo "【Registry ON】" | tee -a "$RESULT_FILE"
|
|||
|
|
HAKMEM_USE_REGISTRY=1 ./bench_allocators_hakmem 2>&1 | tee -a "$RESULT_FILE"
|
|||
|
|
|
|||
|
|
# Registry OFF
|
|||
|
|
echo "" | tee -a "$RESULT_FILE"
|
|||
|
|
echo "【Registry OFF】" | tee -a "$RESULT_FILE"
|
|||
|
|
HAKMEM_USE_REGISTRY=0 ./bench_allocators_hakmem 2>&1 | tee -a "$RESULT_FILE"
|
|||
|
|
|
|||
|
|
echo "" | tee -a "$RESULT_FILE"
|
|||
|
|
echo "========================================" | tee -a "$RESULT_FILE"
|
|||
|
|
echo "Results saved to: $RESULT_FILE" | tee -a "$RESULT_FILE"
|
|||
|
|
echo "========================================" | tee -a "$RESULT_FILE"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 実行方法
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# ビルド + 自動比較
|
|||
|
|
bash bench_registry_comparison.sh
|
|||
|
|
|
|||
|
|
# 出力例:
|
|||
|
|
# ========================================
|
|||
|
|
# Slab Registry ON/OFF Comparison
|
|||
|
|
# ========================================
|
|||
|
|
#
|
|||
|
|
# 【Registry ON】
|
|||
|
|
# string-builder: 10,471 ns (vs mimalloc 18ns = 582倍)
|
|||
|
|
# token-stream: 98 ns (vs mimalloc 9ns = 11倍)
|
|||
|
|
# small-objects: 5 ns (vs mimalloc 3ns = 1.7倍)
|
|||
|
|
#
|
|||
|
|
# 【Registry OFF】
|
|||
|
|
# string-builder: 7,355 ns (vs mimalloc 18ns = 409倍)
|
|||
|
|
# token-stream: 97 ns (vs mimalloc 9ns = 11倍)
|
|||
|
|
# small-objects: 5 ns (vs mimalloc 3ns = 1.7倍)
|
|||
|
|
#
|
|||
|
|
# Diff: -29.7% (Registry OFF の方が速い!)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 📊 **期待される比較結果フォーマット**
|
|||
|
|
|
|||
|
|
### 理想的な出力形式
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
========================================
|
|||
|
|
Slab Registry Performance Comparison
|
|||
|
|
========================================
|
|||
|
|
Date: 2025-10-22 14:30:00
|
|||
|
|
|
|||
|
|
Configuration:
|
|||
|
|
- Compiler: gcc 11.4.0
|
|||
|
|
- Flags: -O2 -Wall -Wextra
|
|||
|
|
- Threads: 1 / 4
|
|||
|
|
|
|||
|
|
Scenario: string-builder (8-64B small allocations)
|
|||
|
|
----------------------------------------
|
|||
|
|
Registry ON: 10,471 ns/op | 95,503 ops/sec
|
|||
|
|
Registry OFF: 7,355 ns/op | 136,010 ops/sec
|
|||
|
|
Diff: -29.7% | +42.4% ✅ (Registry OFF が速い)
|
|||
|
|
|
|||
|
|
Scenario: token-stream (16-128B mixed allocations)
|
|||
|
|
----------------------------------------
|
|||
|
|
Registry ON: 98 ns/op | 10,204,082 ops/sec
|
|||
|
|
Registry OFF: 97 ns/op | 10,309,278 ops/sec
|
|||
|
|
Diff: -1.0% | +1.0% (ほぼ同等)
|
|||
|
|
|
|||
|
|
Scenario: small-objects (32-256B allocations)
|
|||
|
|
----------------------------------------
|
|||
|
|
Registry ON: 5 ns/op | 200,000,000 ops/sec
|
|||
|
|
Registry OFF: 5 ns/op | 200,000,000 ops/sec
|
|||
|
|
Diff: 0.0% | 0.0% (同等)
|
|||
|
|
|
|||
|
|
========================================
|
|||
|
|
Summary:
|
|||
|
|
- Registry ON: 有利なシナリオなし
|
|||
|
|
- Registry OFF: string-builder で 42.4% 高速化
|
|||
|
|
- 推奨: Registry OFF + Atomic修正で Race Condition解消
|
|||
|
|
========================================
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🔬 **追加調査: Hybrid Approach**
|
|||
|
|
|
|||
|
|
### Option A: デフォルト ON + デバッグビルド切り替え
|
|||
|
|
|
|||
|
|
```c
|
|||
|
|
// hakmem_tiny.h
|
|||
|
|
#ifndef HAKMEM_DEBUG_REGISTRY
|
|||
|
|
#define HAKMEM_DEFAULT_REGISTRY 1 // Release: Registry ON
|
|||
|
|
#else
|
|||
|
|
#define HAKMEM_DEFAULT_REGISTRY 0 // Debug: Registry OFF
|
|||
|
|
#endif
|
|||
|
|
|
|||
|
|
// hakmem_tiny.c
|
|||
|
|
static int g_use_registry = HAKMEM_DEFAULT_REGISTRY;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# Release ビルド(Registry ON デフォルト)
|
|||
|
|
make bench
|
|||
|
|
|
|||
|
|
# Debug ビルド(Registry OFF デフォルト、環境変数で ON 可能)
|
|||
|
|
make bench CFLAGS="-O2 -DHAKMEM_DEBUG_REGISTRY"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**メリット**: 本番はデフォルト ON、デバッグは OFF で安全性重視
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🏆 **最終推奨**
|
|||
|
|
|
|||
|
|
### **Pattern 2 (ランタイム環境変数) を採用すべき理由**
|
|||
|
|
|
|||
|
|
1. **ユーザー要望を完全に満たす**: 「簡単に切り替えて比較」が環境変数1つで実現
|
|||
|
|
2. **オーバーヘッドが実質ゼロ**: if分岐 2-5 cycles は Registry/O(N) のコスト(50-500 cycles)に対して 1-10% の誤差範囲
|
|||
|
|
3. **実装が最も簡単**: ~15行の追加のみ、既存コードへの影響最小
|
|||
|
|
4. **メンテナンス性が高い**: 両方の経路が常にビルド → CI/テストで検証容易
|
|||
|
|
5. **将来の拡張に対応**: Atomic Registry, Mutex追加などの試行錯誤が容易
|
|||
|
|
|
|||
|
|
### **実装スケジュール**
|
|||
|
|
|
|||
|
|
| Step | 作業 | 時間 |
|
|||
|
|
|------|------|------|
|
|||
|
|
| 1 | グローバル変数追加 | 1分 |
|
|||
|
|
| 2 | `hak_tiny_init()` 修正 | 3分 |
|
|||
|
|
| 3 | `hak_tiny_owner_slab()` 修正(O(N) fallback追加) | 10分 |
|
|||
|
|
| 4 | `allocate_new_slab()` 修正 | 3分 |
|
|||
|
|
| 5 | `release_slab()` 修正 | 2分 |
|
|||
|
|
| 6 | ビルド + テスト | 5分 |
|
|||
|
|
| 7 | ベンチマーク自動化スクリプト作成 | 10分 |
|
|||
|
|
|
|||
|
|
**合計**: **34分で完了可能!**
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 🎯 **次のアクション**
|
|||
|
|
|
|||
|
|
1. **Pattern 2 実装** (34分)
|
|||
|
|
2. **bench_registry_comparison.sh 作成** (10分)
|
|||
|
|
3. **1-thread / 4-thread 両方で比較測定** (30分)
|
|||
|
|
4. **結果に基づいて Registry ON/OFF を決定**:
|
|||
|
|
- Registry OFF が全体的に速い → OFF をデフォルトに
|
|||
|
|
- Registry ON が特定シナリオで速い → Atomic修正 + Race Condition解消
|
|||
|
|
|
|||
|
|
**Total**: **1時間15分で完全解決!** ✅
|
|||
|
|
|
|||
|
|
にゃ! 🐱🚀
|