- AllocGateStats 構造体追加(size2class/route/env/class分布) - malloc_tiny_fast にカウンタ埋め込み - ENV: HAKMEM_ALLOC_GATE_STATS (default 0) - 挙動変更なし(計測のみ) 計測結果: - Mixed: total=542k, size2class=0, route_calls=0, env_checks=275k, C4-C7=95.2% - size_to_class/route_for_class は完全削減済み(LUT 効果) - C4-C7 が 95% → ULTRA fast path が有効 - env_checks ≈ c7_calls → C7 ULTRA の ENV gate が毎回呼ばれる - C6-heavy: total=11 → malloc_tiny_fast はほぼ通らない(mid/pool 主体) 結論: - alloc gate は既に十分最適化済み(LUT + ULTRA で削減済み) - さらなる最適化余地は小さい(env_checks は軽量化済み、数%以下の効果) - 次フェーズでは free dispatcher (29%) や C7 ULTRA refill (7%) など、他のボトルネックを狙う 詳細: docs/analysis/ALLOC_GATE_ANALYSIS.md 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
19 KiB
TINY CPU Hotpath Userland Analysis (Phase49)
- プロファイル: Mixed 16–1024B, ws=400, iters=1,000,000
HAKMEM_BENCH_MIN_SIZE=16 HAKMEM_BENCH_MAX_SIZE=1024 HAKMEM_TINY_HEAP_PROFILE=C7_SAFE HAKMEM_TINY_C7_HOT=1 HAKMEM_TINY_HOTHEAP_V2=0 HAKMEM_TINY_LARSON_FIX=1 - コマンド:
perf stat -e cycles:u,instructions:u,branch-misses:u ./bench_random_mixed_hakmem 1000000 400 1
perf record -g -e cycles:u -o perf.data.mixed_u ./bench_random_mixed_hakmem 1000000 400 1
perf stat(userlandのみ)
- Throughput: 41,000,086 ops/s
- cycles:u=126,239,279 / instructions:u=324,810,642 → IPC≈2.57
- branch-misses:u=1,186,675
- time=0.0438s(user 0.0295s / sys 0.0143s)
perf record(cycles:u)上位シンボル(self%)
- free: 24.3% — front入口/libc wrapper 部分
- malloc: 18.0% — 同上
- main: 15.3% — ベンチハーネス
- tiny_heap_page_pop.lto_priv.0: 8.8% — TinyHeapBox ホット pop
- hak_super_lookup.lto_priv.*: 7.9% — Superslab 判定(front→TinyHeapBox 境界前)
- tiny_heap_page_becomes_empty.constprop.0: 5.9% — empty 遷移処理
- __memset_avx2_unaligned_erms: 4.0% — ユーザランド初期化(first-touch)
- tiny_region_id_write_header: 2.4% — header 書き込み
- その他: __pthread_self / hak_free_at / tiny_heap_meta_flush_page などが1〜2%台
所感
- ベンチ自身の malloc/free/main が大きいが、allocator 側では tiny_heap_page_pop / hak_super_lookup / tiny_heap_page_becomes_empty が目立つ。
- userland側でも memset が残っており、first-touch 削減(ヘッダ書き込み削減や初期化遅延)余地がある。
Phase50 で削るターゲット箱(提案)
- TinyHeapBox(C7/C6)の pop/empty + hak_super_lookup 前段
- super_lookup 依存の範囲チェックを軽量化 or キャッシュ化。
- pop/empty 内の分岐を整理し、C7 SAFE の理想パス(current_page固定)に寄せる。
- header write / memset を最小化する実験スイッチを検討。
C7 v3 ON, Mixed 16–1024B (ws=400, iters=1,000,000, userland cycles)
環境: HAKMEM_BENCH_MIN_SIZE=16 HAKMEM_BENCH_MAX_SIZE=1024 HAKMEM_TINY_HEAP_PROFILE=C7_SAFE HAKMEM_TINY_C7_HOT=1 HAKMEM_TINY_HOTHEAP_V2=0 HAKMEM_POOL_V2_ENABLED=0 HAKMEM_SMALL_HEAP_V3_ENABLED=1 HAKMEM_SMALL_HEAP_V3_CLASSES=0x80
- Throughput: 48.96M ops/s(前回 v3 ON と同レンジ)
- perf record
cycles:u(171 samples, release build without symbols):- 上位は
free/malloc/mainの無名フレームに潰れてしまい、非C7 Tiny front の細かい関数名が出ず。 - シンボル再取得には DEBUG/記号付きビルドで perf し直す必要あり。
- 上位は
- 所感: C7 v3 以外のホットパス特定には、size→class→route 前段や unified cache hit パスを再シンボル化して見る必要がある。現状のバイナリでは細部が見えない。
Mixed 16–1024B (ws=400, iters=1,000,000, userland cycles, C7 v3 ON, DEBUGビルド)
環境: HAKMEM_BENCH_MIN_SIZE=16 HAKMEM_BENCH_MAX_SIZE=1024 HAKMEM_TINY_HEAP_PROFILE=C7_SAFE HAKMEM_TINY_C7_HOT=1 HAKMEM_TINY_HOTHEAP_V2=0 HAKMEM_POOL_V2_ENABLED=0 HAKMEM_SMALL_HEAP_V3_ENABLED=1 HAKMEM_SMALL_HEAP_V3_CLASSES=0x80
ビルド: make clean && CFLAGS='-O2 -g …' USE_LTO=0 OPT_LEVEL=2 NATIVE=0 make bench_random_mixed_hakmem
- Throughput: 46.1–46.3M ops/s(リリース時の ~49M よりやや低いが符号付きビルドで許容)
- perf record
cycles:u -F5000 --call-graph dwarf(200 samples) の self% 上位(C7 v3 以外を抜粋):- free 23.5%, malloc 18.8%, main 13.7%(ベンチハーネス由来)
- tiny_region_id_write_header 6.7%
- ss_map_lookup 3.6%
- unified_cache_enabled 2.8%
- tiny_guard_is_enabled 2.2%
- classify_ptr 1.4%(size→class 判定系)
- mid_desc_lookup 1.3%
- hak_free_at 0.9%, hak_pool_mid_lookup 0.7%
- so_alloc/so_free 合計 ~7%(C7 v3 本体)
- 所感(非C7 Tiny front 目線):
- header 書き込み(tiny_region_id_write_header)が依然目立つ。
- Superslab 判定前の
ss_map_lookupが 3–4% 程度残っている。 - route/guard 判定(unified_cache_enabled / tiny_guard_is_enabled / classify_ptr)が合わせて ~6% 程度。
- 次は「size→class→route 前段+header」をフラット化するターゲットが有力。
TF3 事前計測(DEBUGシンボル, front v3+LUT ON, C7-only v3)
環境: HAKMEM_BENCH_MIN_SIZE=16 HAKMEM_BENCH_MAX_SIZE=1024 HAKMEM_TINY_HEAP_PROFILE=C7_SAFE HAKMEM_TINY_C7_HOT=1 HAKMEM_TINY_HOTHEAP_V2=0 HAKMEM_POOL_V2_ENABLED=0 HAKMEM_SMALL_HEAP_V3_ENABLED=1 HAKMEM_SMALL_HEAP_V3_CLASSES=0x80 HAKMEM_TINY_FRONT_V3_ENABLED=1 HAKMEM_TINY_FRONT_V3_LUT_ENABLED=1
ビルド: BUILD_FLAVOR=debug OPT_LEVEL=0 USE_LTO=0 EXTRA_CFLAGS=-g
ベンチ: perf record -F5000 --call-graph dwarf -e cycles:u -o perf.data.tiny_front_tf3 ./bench_random_mixed_hakmem 1000000 400 1
Throughput: 12.39M ops/s(DEBUG/-O0 相当)
ss_map_lookup: 7.3% self(free 側での ptr→SuperSlab 判定が主、C7 v3 でも多い)hak_super_lookup: 4.0% self(lookup fallback 分)classify_ptr: 0.64% self(free の入口 size→class 判定)mid_desc_lookup: 0.43% self(mid 経路の記述子検索)- そのほか: free/malloc/main が約 30% 強、header write 系は今回のデバッグログに埋もれて確認できず。
所感:
Phase HF1(DEBUG, front v3+LUT+fast classify+mid_desc_cache ON)
- ビルド:
CFLAGS='-O0 -g' USE_LTO=0 OPT_LEVEL=0 NATIVE=0 - ENV:
HAKMEM_PROFILE=MIXED_TINYV3_C7_SAFE,HAKMEM_MID_DESC_CACHE_ENABLED=1 - コマンド:
perf record -F 5000 --call-graph dwarf -e cycles:u -o perf.data.tiny_mixed_hf1 ./bench_random_mixed_hakmem 1000000 400 1 - self% 上位(perf_tiny_mixed_hf1.txt 抜粋):
- tiny_alloc_gate_fast 16.85%
- free 13.63% / malloc 13.34% / main 9.02%(ベンチ枠)
- __memset_avx2_unaligned_erms 5.65%(初期化)
- hak_super_registry_init 5.57%(初期化)
- so_alloc_fast 2.41%, unified_cache_push 2.23%
- tiny_front_v3_enabled 2.23%, tiny_front_v3_lut_lookup 2.21%
- smallobject_hotbox_v3_can_own_c7 1.94%
- tiny_region_id_write_header 1.82%
- ss_map_lookup 1.61%, mid_desc_lookup_cached 0.98%, classify_ptr 0.65% 所感: TF3 + mid_desc_cache 適用後、ss_map_lookup/self% は 1.6% まで沈み、tiny_region_id_write_header が引き続き ~1.8% で上位。次の削り候補は header 書き込み回数削減 or front前段の小枝刈り。
- front v3 + LUT ON でも free 側の
ss_map_lookup/hak_super_lookupが ~11% 程度残っており、ここを FAST classify で直叩きする余地が大きい。 classify_ptrは 1% 未満だが、ss_map_lookupとセットで落とせれば +5〜10% の目標に寄せられる見込み。
Front v3 snapshot 導入メモ
TinyFrontV3Snapshotを追加し、unified_cache_on / tiny_guard_on / header_modeを 1 回だけキャッシュする経路を front v3 ON 時に通すようにした(デフォルト OFF)。- Mixed 16–1024B (ws=400, iters=1M, C7 v3 ON, Tiny/Pool v2 OFF) で挙動変化なし(slow=1 維持)。ホットスポットは依然 front 前段 (
tiny_region_id_write_header,ss_map_lookup, guard/route 判定) が中心。
Front v3 size→class LUT メモ(Phase2-A 実装済み、A/B これから)
- ENV
HAKMEM_TINY_FRONT_V3_LUT_ENABLEDを追加(デフォルト OFF)。front v3 ON 時に size→class→route を Tiny 専用 LUT から 1 ルックアップで取得し、従来のhak_tiny_size_to_class+ route 読みを代替する。 - LUT は起動時に既存の size→class 変換と route スナップショットをそのまま写経して構築するため挙動は変えない。
- A/B (Mixed 16–1024B, ws=400, iters=1M, C7 v3 ON, Tiny/Pool v2 OFF, front v3 ON):
- LUT=0: 44.820M ops/s
- LUT=1: 45.231M ops/s(+0.9%)
- HEAP_STATS は TinyHeap v1 経路外のため出力なし。C7_PAGE_STATS は prepare_calls=2446 で変化なし。
Header v3 (C7-only) 簡易スキップ実験
- ENV:
HAKMEM_TINY_HEADER_V3_ENABLED/HAKMEM_TINY_HEADER_V3_SKIP_C7を追加。C7 v3 alloc 時だけ tiny_region_id_write_header を通さず 1byte store にする。 - Mixed 16–1024B (ws=400, iters=1M, front v3 ON, LUT ON, route_fast=0, Tiny/Pool v2 OFF):
- header_v3=0: 44.29M ops/s, C7_PAGE_STATS prepare_calls=2446
- header_v3=1 + SKIP_C7=1: 43.68M ops/s(約 -1.4%)、prepare_calls=2446、fallback/page_of_fail=0
- 所感: C7 v3 のヘッダ簡略だけでは perf 改善は見えず。free 側のヘッダ依存を落とす or header light/off を別箱で検討する必要あり。
TF3: ptr fast classify 実装後の A/B(C7-only v3, front v3+LUT ON)
- Releaseビルド, ws=400, iters=1M, ENV は TF3 基準 (
C7_SAFE, C7_HOT=1, v2/pool v2=0, v3 classes=0x80, front v3/LUT ON)。 - Throughput (ops/s):
- PTR_FAST_CLASSIFY=0: 33.91M
- PTR_FAST_CLASSIFY=1: 36.67M(約 +8.1%)
- DEBUG perf(同ENV, gate=1, cycles@5k, dwarf):
ss_map_lookupself が 7.3% → 0.9%、hak_super_lookupはトップから消失。代わりに TLS 内のページ判定 (smallobject_hotbox_v3_can_own_c7/so_page_of) が合計 ~5.5% へ移動。classify_ptrは 2–3% まで微増(外れ時のフォールバック分)。 - 所感: C7 v3 free の Superslab lookup 往復をほぼ除去でき、目標の +5〜10% に収まる結果。fast path 判定の TLS 走査が新たなホットスポットだが、現状コストは lookup より低く許容範囲。
Phase PERF-ULTRA-REBASE-1 Results (2025-12-11 19:43:49)
計測環境
- 日時: 2025-12-11 19:43:49
- ワークロード: Mixed 16-1024B, ws=8192, iters=10,000,000
- ENV設定:
HAKMEM_TINY_C7_ULTRA_FREE_ENABLED=1HAKMEM_TINY_C6_ULTRA_FREE_ENABLED=1HAKMEM_TINY_C5_ULTRA_FREE_ENABLED=1HAKMEM_TINY_C4_ULTRA_FREE_ENABLED=1- その他 ULTRA 以外のフラグは OFF(v6/v4/v5/free-front-v3 等)
- perf コマンド:
perf record -F 5000 --call-graph dwarf -e cycles:u - Throughput: 31.61M ops/s
- Samples: 1842 samples, 約1.36B cycles
perf report 主要関数 self% トップ20
- free: 25.48%(libc wrapper/ベンチ由来)
- main: 23.60%(ベンチマークハーネス)
- malloc: 21.13%(libc wrapper/ベンチ由来)
- tiny_c7_ultra_alloc: 7.66%
- tiny_c7_ultra_free: 3.50%
- so_free: 2.47%
- so_alloc_fast: 2.39%
- tiny_c7_ultra_page_of: 1.78%
- classify_ptr: 1.15%
- tiny_c7_ultra_segment_from_ptr: 0.96%
- tiny_front_v3_lut_lookup: 0.91%
- ss_map_lookup: 0.79%
- tiny_c5_ultra_free_fast: 0.69%
- hak_free_at: 0.68%
- tiny_c6_ultra_free_fast: 0.54%
- tiny_guard_is_enabled: 0.45%
- tiny_c6_ultra_free_tls: 0.34%
- tiny_heap_page_becomes_empty: 0.23%
- tiny_c4_ultra_free_fast: 0.17%
- tiny_c5_ultra_free_tls: 0.17%
カテゴリ別集計
- ベンチマーク関連(main + free + malloc wrapper): 70.21%
- C4-C7 ULTRA free関数群の合計: 5.41%
- tiny_c7_ultra_free: 3.50%
- tiny_c5_ultra_free_fast: 0.69%
- tiny_c6_ultra_free_fast: 0.54%
- tiny_c6_ultra_free_tls: 0.34%
- tiny_c4_ultra_free_fast: 0.17%
- tiny_c5_ultra_free_tls: 0.17%
- C7 ULTRA alloc: 7.66%
- so_alloc系(v3 backend alloc): 3.60%
- so_alloc_fast: 2.39%
- so_alloc: 1.21%
- so_free系(v3 backend free): 2.47%
- gate/front関連: 2.51%
- classify_ptr: 1.15%
- tiny_front_v3_lut_lookup: 0.91%
- tiny_guard_is_enabled: 0.45%
- page_of/segment判定: 2.74%
- tiny_c7_ultra_page_of: 1.78%
- tiny_c7_ultra_segment_from_ptr: 0.96%
- ss_map_lookup(Superslab判定): 0.79%
- hak_free_at: 0.68%
- tiny_heap_page_becomes_empty: 0.23%
分析コメント
-
C7 ULTRA alloc が最大ホットスポット(7.66%)
- C7 ULTRA の allocate パスが allocator 内で最も重いボトルネック
- 次点は ULTRA free 群(5.41%)だが、alloc が約1.4倍重い
-
so_alloc系(v3 backend)が3.60%で続く
- C7 v3 の backend alloc 処理が依然として可視
- so_free は2.47%でバランス良好
-
page_of/segment判定が2.74%
- tiny_c7_ultra_page_of(1.78%)とsegment_from_ptr(0.96%)の合計
- ULTRA free内でのptr→page/segment解決コストが目立つ
-
gate/front前段は2.51%に留まる
- classify_ptr(1.15%)、LUT lookup(0.91%)、guard判定(0.45%)
- 以前のフェーズより改善されており、現時点では相対的に軽い
-
ss_map_lookup は0.79%まで低下
- TF3 + PTR_FAST_CLASSIFY の効果で Superslab lookup が大幅減
- 依然残っているが、優先度は下がった
-
header書き込みが不可視
- tiny_region_id_write_header が上位20に入っていない(< 0.17%)
- ULTRA経路では header 書き込みコストが削減されている可能性
次の候補箱(ボトルネック優先順位)
-
最優先: C7 ULTRA alloc(7.66%)
- tiny_c7_ultra_alloc の内部最適化が最大の削減ポテンシャル
- キャッシュヒット率向上、TLS構造簡素化、分岐削減などを検討
-
第2優先: C4-C7 ULTRA free群(5.41%)
- 特に tiny_c7_ultra_free(3.50%)が中心
- page_of/segment判定(2.74%)との重複があるため、ptr解決の高速化が有効
-
第3優先: so_alloc系 backend(3.60%)
- C7 v3 の backend alloc 処理の軽量化
- fast path のインライン化や TLS キャッシュ強化
-
第4優先: page_of/segment判定(2.74%)
- ptr→page/segment 解決の最適化
- TLS キャッシュや LUT ベースの高速化を検討
-
監視対象: gate/front前段(2.51%)
- 現状は許容範囲だが、さらなる改善余地あり
- classify_ptr の fast path 強化や LUT の効率化
所感
- C7 ULTRA alloc が明確な最大ボトルネックとして浮上。次のフェーズでは alloc パスの内部最適化(TLS キャッシュヒット率向上、構造簡素化、分岐削減)に注力すべき。
- ULTRA free 群も5.41%と無視できないが、alloc が約1.4倍重いため、alloc を先に削るのが効率的。
- gate/front 前段は以前のフェーズより改善されており、現時点での優先度は下がった。
- header 書き込みが上位20に入っていないのは、ULTRA 経路での削減効果が出ている可能性がある。
Phase PERF-ULTRA-REBASE-2: C4-C7 ULTRA free最適化後の hotpath分析 (2025-12-11)
計測条件
- ENV:
HAKMEM_PROFILE=MIXED_TINYV3_C7_SAFE - ULTRA: C4-C7 全て ON
- v4/v5/v6/free-front-v3: OFF
- ワークロード: Mixed 16-1024B (1M iter, ws=400)
- perf コマンド:
perf record -F 5000 --call-graph dwarf -e cycles:u - Throughput: 42.63M ops/s(前回 31.61M から大幅改善、ws=400 vs 8192の差)
Mixed 16-1024B ホットパス(self% 上位)
| 順位 | 関数 | self% | 分類 |
|---|---|---|---|
| #1 | free | 27.88% | ベンチマーク wrapper |
| #2 | tiny_alloc_gate_fast | 23.57% | alloc gate/front |
| #3 | main | 17.66% | ベンチマーク harness |
| #4 | malloc | 6.94% | ベンチマーク wrapper |
| #5 | tiny_region_id_write_header.lto_priv.0 | 5.30% | header 書き込み |
| #6 | tiny_region_id_write_header.constprop.0 | 2.77% | header 書き込み |
| #7 | hak_super_lookup.lto_priv.1.lto_priv.0 | 0.69% | Superslab 判定 |
| #8 | hak_pool_free | 0.64% | pool free |
分析コメント
WARNING: サンプル数が極めて少ない(238 samples)
- perf 計測時のサンプル数が238と非常に少なく、統計的信頼性が低い
- ベンチマーク時間が0.023s(23ms)と極めて短く、iters=1M / ws=400 の組み合わせでワークロードが軽すぎた可能性
- 結果として、allocator 本体のホットパス(ULTRA alloc/free)がほとんど可視化されていない
可視化されたボトルネック:
- tiny_alloc_gate_fast: 23.57% - alloc の gate/front 入口が最大のホットスポット
- header 書き込み合計: 8.07%(lto_priv.0: 5.30% + constprop.0: 2.77%)
- ULTRA 関連関数が不可視: tiny_c7_ultra_alloc や ULTRA free 群が上位20に入っていない
- so_alloc/so_free も不可視: v3 backend 処理も検出されず
前フェーズ(PERF-ULTRA-REBASE-1)との比較:
- 前回(ws=8192, iters=10M): C7 ULTRA alloc 7.66%, ULTRA free群 5.41%, so_alloc 3.60%
- 今回(ws=400, iters=1M): これらが全て不可視(< 0.63%)
- サンプル数: 前回1842 samples vs 今回238 samples(約1/8)
結論:
- 計測条件の再検討が必要: ワークロードを重くする(iters を 10M に増やす、または ws を拡大)
- 現状の結果では次の最大ボトルネックを確定できない
- gate/front(23.57%)と header(8.07%)が可視範囲での上位だが、これは軽すぎるワークロードで allocator 本体が見えていない可能性が高い
C6-heavy ホットパス(self% 上位)
計測条件:
- ENV:
HAKMEM_PROFILE=C6_HEAVY_LEGACY_POOLV1 - Throughput: 26.49M ops/s
- Samples: 292 samples(やはり少ない)
| 順位 | 関数 | self% | 分類 |
|---|---|---|---|
| #1 | hak_pool_free_v1_impl.part.0 | 19.89% | pool v1 free |
| #2 | hak_pool_try_alloc_v1_impl.part.0 | 14.46% | pool v1 alloc |
| #3 | free | 11.80% | ベンチマーク wrapper |
| #4 | hak_free_at.constprop.0 | 9.26% | 解放処理 |
| #5 | pthread_once@GLIBC_2.2.5 | 8.21% | 初期化同期 |
| #6 | hak_super_lookup.lto_priv.1.lto_priv.0 | 7.07% | Superslab 判定 |
| #7 | malloc | 6.81% | ベンチマーク wrapper |
| #8 | __GI___pthread_self | 2.62% | TLS アクセス |
| #9 | hak_pool_free | 1.08% | pool free wrapper |
分析:
- pool v1 が支配的: alloc 14.46% + free 19.89% = 34.35%
- ULTRA は可視化されず: C6/C5/C4 ULTRA 関数が上位に入っていない
- pthread_once が8.21%: 初期化同期のオーバーヘッドが目立つ(ワークロードが軽い証拠)
所感: C6-heavy でも pool v1 が主要経路として機能しているが、ULTRA の効果測定には不十分なサンプル数
Phase ALLOC-GATE-OPT-1 計測前の前提 (2025-12-11)
最新 perf(REBASE-3) より:
- tiny_alloc_gate_fast: self% ≈ 18%
- tiny_route_for_class_calls: 267,967 calls (alloc 側が主体)
- FREE_DISPATCHER では ENV/route が既に snapshot で削減済み
→ alloc 側が未最適化の可能性が高い
計測目的:
- size→class 変換の回数(毎回か?)
- route_for_class 呼び出し回数(毎回か?初期化時のみか?)
- alloc-side ENV check 回数(C4-C7 ULTRA の ENV gate 等)
- クラス別分布(C0〜C7 のどれが主体か)
期待される発見:
- route_for_class が alloc 毎に呼ばれているなら → snapshot 化で削減可能
- size_to_class が重いなら → インライン化・LUT 化
- C4〜C7 が 80% 以上なら → class-specific fast path 検討