322 lines
9.6 KiB
Markdown
322 lines
9.6 KiB
Markdown
|
|
# Phase PERF-ULTRA-ALLOC-OPT-1 実装計画
|
|||
|
|
|
|||
|
|
**フェーズ名**: Phase PERF-ULTRA-ALLOC-OPT-1: C7 ULTRA alloc 内部最適化
|
|||
|
|
|
|||
|
|
**目的**: C7 ULTRA alloc(現在 7.66% self%)の hot path を直線化し、5-6% まで削減
|
|||
|
|
|
|||
|
|
**期待効果**: 全体 Mixed throughput で +2-3M ops/s(31.6M ops/s → 33-35M ops/s)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 背景
|
|||
|
|
|
|||
|
|
### Phase PERF-ULTRA-REBASE-1 の発見
|
|||
|
|
|
|||
|
|
2025-12-11 の perf 計測結果(C4-C7 ULTRA 全て ON, Mixed 16-1024B, 10M cycles):
|
|||
|
|
|
|||
|
|
| 順位 | 関数 | self% | 判定 |
|
|||
|
|
|------|------|-------|------|
|
|||
|
|
| #1 | **C7 ULTRA alloc** | **7.66%** | ← **最大ボトルネック(新規発見)** |
|
|||
|
|
| #2 | C4-C7 ULTRA free群 | 5.41% | 次の候補 |
|
|||
|
|
| #3 | gate/front前段 | 2.51% | ✅ 既に十分薄い |
|
|||
|
|
| #4 | header関連 | < 0.17% | ✅ ULTRA で削減済み |
|
|||
|
|
|
|||
|
|
### 戦略転換
|
|||
|
|
|
|||
|
|
**これまで**: v4/v5/v6 などの新世代を追加
|
|||
|
|
**今後**: 既に当たりが出ている **ULTRA 内部を細かく削る**
|
|||
|
|
|
|||
|
|
理由:
|
|||
|
|
- v6/v5 拡張は -12〜33% の大幅回帰(C5/C4 対応で失敗)
|
|||
|
|
- gate/front や header はもう改善の余地が少ない
|
|||
|
|
- C7 ULTRA alloc の 7.66% を 5-6% に削れば全体効果 2-3%
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 実装対象ファイル
|
|||
|
|
|
|||
|
|
### 主要ファイル
|
|||
|
|
|
|||
|
|
- **core/box/tiny_c7_ultra_free_box.h**
|
|||
|
|
- TinyC7UltraFreeTLS 構造体定義
|
|||
|
|
- tiny_c7_ultra_alloc_fast() / tiny_c7_ultra_alloc_cold() の宣言
|
|||
|
|
|
|||
|
|
- **core/box/tiny_c7_ultra_free_box.c**
|
|||
|
|
- tiny_c7_ultra_alloc_fast() 実装(メイン最適化対象)
|
|||
|
|
- TLS context の定義と初期化
|
|||
|
|
|
|||
|
|
- **core/front/malloc_tiny_fast.h**
|
|||
|
|
- tiny_c7_ultra_alloc() の呼び出し箇所
|
|||
|
|
- alloc dispatcher からの pop ロジック
|
|||
|
|
|
|||
|
|
### 参照/確認ファイル
|
|||
|
|
|
|||
|
|
- **core/box/tiny_c7_ultra_free_env_box.h**
|
|||
|
|
- tiny_c7_ultra_free_enabled() - ENV gate の状態確認
|
|||
|
|
|
|||
|
|
- **core/link_stubs.c** / **core/hakmem.c**
|
|||
|
|
- 起動時の ENV 初期化タイミング確認
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 実装施策
|
|||
|
|
|
|||
|
|
### 1. TLS ヒットパスの直線化
|
|||
|
|
|
|||
|
|
**確認項目**:
|
|||
|
|
|
|||
|
|
```c
|
|||
|
|
// tiny_c7_ultra_alloc_fast() のコード流
|
|||
|
|
TinyC7UltraFreeTLS* ctx = tiny_c7_ultra_free_tls();
|
|||
|
|
if (ctx->count > 0) {
|
|||
|
|
void* base = ctx->freelist[--ctx->count];
|
|||
|
|
return tiny_base_to_user_inline(base);
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**チェック項目**:
|
|||
|
|
- [ ] `tiny_c7_ultra_free_enabled()` が hot path に含まれていないか
|
|||
|
|
- → lazy init 後は ENV 参照が不要。guard は不要。
|
|||
|
|
- → if (!tiny_c7_ultra_free_enabled()) return NULL; のような early guard は削除
|
|||
|
|
- [ ] `tiny_c7_ultra_free_tls()` が毎回呼ばれているか
|
|||
|
|
- → TLS pointer cache で 1 回だけ取得し、ローカル変数に格納
|
|||
|
|
- → 複数回アクセスなら cache 再利用
|
|||
|
|
- [ ] hot path に条件分岐が何個あるか(目標: 1-2個)
|
|||
|
|
- count > 0 チェックのみ
|
|||
|
|
- ユーザーポインタ計算は単純な演算のみ
|
|||
|
|
|
|||
|
|
**実装例**:
|
|||
|
|
|
|||
|
|
```c
|
|||
|
|
// 最適化前の可能性が高い形
|
|||
|
|
void* tiny_c7_ultra_alloc_fast(void) {
|
|||
|
|
if (!tiny_c7_ultra_free_enabled()) return NULL;
|
|||
|
|
TinyC7UltraFreeTLS* ctx = tiny_c7_ultra_free_tls();
|
|||
|
|
if (ctx == NULL) return NULL; // ← 不要な null check
|
|||
|
|
if (ctx->count == 0) return NULL;
|
|||
|
|
...
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 最適化後(expected)
|
|||
|
|
void* tiny_c7_ultra_alloc_fast(void) {
|
|||
|
|
// tiny_c7_ultra_free_enabled() check は呼び出し側で済ませる
|
|||
|
|
// (malloc_tiny_fast.h で if (tiny_c7_ultra_free_enabled()) { tiny_c7_ultra_alloc_fast(); } の形)
|
|||
|
|
TinyC7UltraFreeTLS* ctx = __thread_local_tls_c7; // 直接 TLS access
|
|||
|
|
if (likely(ctx->count > 0)) {
|
|||
|
|
void* base = ctx->freelist[--ctx->count];
|
|||
|
|
return tiny_base_to_user_inline(base);
|
|||
|
|
}
|
|||
|
|
return NULL;
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. TLS freelist レイアウト最適化
|
|||
|
|
|
|||
|
|
**確認項目**:
|
|||
|
|
|
|||
|
|
```c
|
|||
|
|
// TinyC7UltraFreeTLS 構造体の定義確認
|
|||
|
|
typedef struct TinyC7UltraFreeTLS {
|
|||
|
|
void* freelist[TINY_C7_ULTRA_FREE_CAP]; // 128 * 8B = 1024B
|
|||
|
|
uint8_t count; // + 1B = 1025B
|
|||
|
|
uintptr_t seg_base, seg_end; // + 16B = 1041B
|
|||
|
|
} TinyC7UltraFreeTLS;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**チェック項目**:
|
|||
|
|
- [ ] freelist と count が 1 cache line(64B)に収まるか
|
|||
|
|
- freelist[TINY_C7_ULTRA_FREE_CAP] サイズを確認(デフォルト 128 要素 = 1024B)
|
|||
|
|
- → 1024B は L1 キャッシュ(通常 32-64KB)を超過しているので、count のみが hot
|
|||
|
|
- → count アクセスは cache line 1 本で十分
|
|||
|
|
- → freelist アクセスは cache misses が避けられない(alloc pop は 128 個のブロックを引き出すのに 1 個アクセスだから許容)
|
|||
|
|
|
|||
|
|
- [ ] alloc hot data(count)が先頭に配置されているか
|
|||
|
|
- 現行は freq[capacity/count] が後ろ
|
|||
|
|
- → count を先頭に移動すれば、alloc pop 時の cache line access 1 本(count チェック)で済む
|
|||
|
|
|
|||
|
|
- [ ] seg_base / seg_end は確実に slow path(free 時の segment learning)か
|
|||
|
|
- → alloc は seq_base を参照しない → hot path から外すべき
|
|||
|
|
|
|||
|
|
**実装例**:
|
|||
|
|
|
|||
|
|
```c
|
|||
|
|
// 最適化後(期待値)
|
|||
|
|
typedef struct TinyC7UltraFreeTLS {
|
|||
|
|
// Hot path 用(64B以内想定)
|
|||
|
|
uint16_t count; // ← alloc pop で必須
|
|||
|
|
uint8_t pad;
|
|||
|
|
void* freelist[128]; // ← large block, only used for pop
|
|||
|
|
|
|||
|
|
// Cold path 用(segment learning など)
|
|||
|
|
uintptr_t seg_base, seg_end; // free 時のみ
|
|||
|
|
} TinyC7UltraFreeTLS;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3. segment / page_meta アクセスの確認
|
|||
|
|
|
|||
|
|
**確認項目**:
|
|||
|
|
|
|||
|
|
```c
|
|||
|
|
// tiny_c7_ultra_free_fast() の segment learning ロジック確認
|
|||
|
|
if (unlikely(ctx->seg_base == 0)) {
|
|||
|
|
SuperSlab* ss = ss_fast_lookup(base);
|
|||
|
|
ctx->seg_base = (uintptr_t)ss;
|
|||
|
|
ctx->seg_end = ctx->seg_base + (1u << ss->lg_size);
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**チェック項目**:
|
|||
|
|
- [ ] segment learning は cold path(unlikely)として外出しされているか
|
|||
|
|
- → 初回 free で 1 回だけ実行
|
|||
|
|
- → 以降のすべての free は segment cached チェックで済む
|
|||
|
|
- → alloc は segment を参照しない
|
|||
|
|
|
|||
|
|
- [ ] alloc hot path に page_meta access が混入していないか
|
|||
|
|
- → alloc は count チェックと base pop だけ
|
|||
|
|
- → page_meta は free 側のみで十分
|
|||
|
|
|
|||
|
|
- [ ] ss_fast_lookup() が cache miss 時のみか
|
|||
|
|
- → segment learning 時(unlikely)のみ
|
|||
|
|
- → これは現状で十分(1 回だけ)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 計測戦略
|
|||
|
|
|
|||
|
|
### Phase 0: ベースライン(実装前)
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# C7-only bench
|
|||
|
|
HAKMEM_TINY_C7_ULTRA_FREE_ENABLED=1 \
|
|||
|
|
perf record -F 5000 --call-graph dwarf -e cycles:u \
|
|||
|
|
./bench_allocators_hakmem C7 1000000 400 1
|
|||
|
|
|
|||
|
|
# Mixed bench
|
|||
|
|
HAKMEM_TINY_C7_ULTRA_FREE_ENABLED=1 HAKMEM_FREE_PATH_STATS=1 \
|
|||
|
|
perf record -F 5000 --call-graph dwarf -e cycles:u \
|
|||
|
|
./bench_random_mixed_hakmem 10000000 8192 1
|
|||
|
|
|
|||
|
|
# perf report で self% を記録
|
|||
|
|
perf report --stdio | grep -A 5 "tiny_c7_ultra_alloc"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**期待値**: C7 ULTRA alloc の self% = **7.66%**(ベースライン)
|
|||
|
|
|
|||
|
|
### Phase 1: 各施策の実装と計測
|
|||
|
|
|
|||
|
|
実装後、同じ条件で 3 回計測して平均を取る:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 最適化後の計測(同じコマンド)
|
|||
|
|
for i in 1 2 3; do
|
|||
|
|
HAKMEM_TINY_C7_ULTRA_FREE_ENABLED=1 \
|
|||
|
|
perf record -F 5000 --call-graph dwarf -e cycles:u \
|
|||
|
|
./bench_random_mixed_hakmem 10000000 8192 1
|
|||
|
|
perf report --stdio > perf_opt_run$i.txt
|
|||
|
|
done
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**目標**:
|
|||
|
|
- self% = 5-6%(削減幅 2-3%)
|
|||
|
|
- throughput = +2-3M ops/s(31.6M → 33-35M ops/s)
|
|||
|
|
|
|||
|
|
### Phase 2: 詳細分析
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 各関数の self% を比較表にまとめる
|
|||
|
|
# - tiny_c7_ultra_alloc_fast vs tiny_c7_ultra_alloc_cold
|
|||
|
|
# - ss_fast_lookup / segment learning の slow path confirmation
|
|||
|
|
# - freelist access パターン
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 検証チェックリスト
|
|||
|
|
|
|||
|
|
実装完了時、以下をすべて確認:
|
|||
|
|
|
|||
|
|
- [ ] コンパイル成功(warning なし)
|
|||
|
|
- [ ] リンク成功
|
|||
|
|
- [ ] C7-only bench で SEGV/assert なし
|
|||
|
|
- [ ] Mixed bench で SEGV/assert なし
|
|||
|
|
- [ ] perf 計測で self% が 5-6% に達している
|
|||
|
|
- [ ] throughput が +2-3M ops/s 改善
|
|||
|
|
- [ ] 各関数の分岐数が max 2-3(直線化目標)
|
|||
|
|
- [ ] TLS access が 1 回だけ(cache 再利用)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 実装時の注意点
|
|||
|
|
|
|||
|
|
### 1. ENV チェックの配置
|
|||
|
|
|
|||
|
|
```c
|
|||
|
|
// ❌ Hot path 内で ENV check
|
|||
|
|
if (tiny_c7_ultra_free_enabled()) { // ← hot path で毎回?
|
|||
|
|
...
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ✅ 呼び出し側(malloc_tiny_fast.h dispatcher)でチェック
|
|||
|
|
if (tiny_c7_ultra_free_enabled()) {
|
|||
|
|
void* p = tiny_c7_ultra_alloc_fast();
|
|||
|
|
if (p) return p;
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. TLS cache 再利用
|
|||
|
|
|
|||
|
|
```c
|
|||
|
|
// ❌ 複数回 TLS access
|
|||
|
|
TinyC7UltraFreeTLS* ctx = tiny_c7_ultra_free_tls();
|
|||
|
|
if (ctx->count > 0) {
|
|||
|
|
void* base = ctx->freelist[--ctx->count]; // ← 1回目
|
|||
|
|
...
|
|||
|
|
}
|
|||
|
|
if (ctx->count > 0) { // ← 2回目の access
|
|||
|
|
...
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ✅ ローカル変数に cache
|
|||
|
|
TinyC7UltraFreeTLS* ctx = tiny_c7_ultra_free_tls();
|
|||
|
|
uint16_t count = ctx->count;
|
|||
|
|
if (likely(count > 0)) {
|
|||
|
|
void* base = ctx->freelist[--count];
|
|||
|
|
ctx->count = count;
|
|||
|
|
return tiny_base_to_user_inline(base);
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3. 構造体レイアウトの確認
|
|||
|
|
|
|||
|
|
```c
|
|||
|
|
// 実装後、以下で hot field 配置を確認
|
|||
|
|
// (gdb) p sizeof(TinyC7UltraFreeTLS)
|
|||
|
|
// (gdb) p &ctx->count, &ctx->freelist
|
|||
|
|
// → count が freelist より先頭にあることを確認
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 次フェーズへのつなぎ
|
|||
|
|
|
|||
|
|
### このフェーズの成功条件
|
|||
|
|
|
|||
|
|
- self% が 5-6% に低下
|
|||
|
|
- throughput が +2-3M ops/s 達成
|
|||
|
|
- 新しい bottleneck が浮上(page_of/segment 判定, so_alloc など)
|
|||
|
|
|
|||
|
|
### Phase 2 の候補(自動昇格)
|
|||
|
|
|
|||
|
|
self% が 5-6% に達したら、次は **C4-C7 ULTRA free群(5.41%)** の軽量化を検討:
|
|||
|
|
- free side のTLS push パス直線化
|
|||
|
|
- page_of / segment 判定との連携最適化
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 参考資料
|
|||
|
|
|
|||
|
|
- **ホットパス分析**: `docs/analysis/TINY_CPU_HOTPATH_USERLAND_ANALYSIS.md`
|
|||
|
|
- **C7 ULTRA 設計**: `docs/analysis/TINY_C7_ULTRA_DESIGN.md`
|
|||
|
|
- **全体方針**: `CURRENT_TASK.md` 「Phase PERF-ULTRA-ALLOC-OPT-1 計画」
|
|||
|
|
|