## Phase PERF-ULTRA-REBASE-1 実施 - C4-C7 ULTRA 全て ON 状態での CPU ホットパス計測 - Mixed 16-1024B, 10M cycles での perf 分析 - **発見**: C7 ULTRA alloc が新しい最大ボトルネック(7.66% self%) ## ホットパス分析結果 | 順位 | 関数 | 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% の大幅回帰 - gate/front や header はもう改善の余地が少ない - C7 ULTRA alloc の 7.66% → 5-6% 削減で全体効果 2-3% ## Phase PERF-ULTRA-ALLOC-OPT-1 計画策定 - ターゲット: tiny_c7_ultra_alloc() の hot path を直線化 - 施策: 1. TLS ヒットパスの直線化(env check/snapshot 削除) 2. TLS freelist レイアウト最適化(L1 キャッシュ親和性) 3. segment/page_meta アクセスの確認(slow path 確認) - 計測: C7-only + Mixed での A/B テスト - 期待: 7.66% → 5-6%、全体で +2-3M ops/s ## ドキュメント更新 - CURRENT_TASK.md: PERF-ULTRA-REBASE-1 結果と ALLOC-OPT-1 計画を追記 - TINY_C7_ULTRA_DESIGN.md: Phase PERF-ULTRA-ALLOC-OPT-1 セクション追加 - NEW: docs/analysis/PERF_ULTRA_ALLOC_OPT_1_PLAN.md - 詳細な実装計画書 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
9.6 KiB
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 ヒットパスの直線化
確認項目:
// 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 チェックのみ
- ユーザーポインタ計算は単純な演算のみ
実装例:
// 最適化前の可能性が高い形
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 レイアウト最適化
確認項目:
// 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 から外すべき
実装例:
// 最適化後(期待値)
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 アクセスの確認
確認項目:
// 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: ベースライン(実装前)
# 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 回計測して平均を取る:
# 最適化後の計測(同じコマンド)
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: 詳細分析
# 各関数の 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 チェックの配置
// ❌ 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 再利用
// ❌ 複数回 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. 構造体レイアウトの確認
// 実装後、以下で 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 計画」