Files
hakmem/CURRENT_TASK.md
Moe Charm (CI) 582ebdfd4f CURRENT_TASK: Registry 線形スキャン ボトルネック特定 (2025-11-05)
- perf 分析で superslab_refill が 28.51% CPU を消費
- Root cause: 262,144 エントリの線形スキャン (97.65% の hot instructions)
- 解決策: per-class registry (8×4096 = 32K entries)
- 期待効果: +200-300% (2.59M → 7.8-10.4M ops/s)
- Box Refactor は既に動いている (+463% ST, +131% MT)

次のアクション: Phase 1 実装 (per-class registry 変更)

詳細: PERF_ANALYSIS_2025_11_05.md

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-05 16:47:04 +09:00

1414 lines
54 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Current Task (2025-11-05)
## 🔔 最新アップデート (2025-11-05 16:30) 🔥🔥🔥
### ✅ Registry 線形スキャン ボトルネック特定!
**Perf 分析完了 → Root Cause 発見!**
**測定結果 (Larson, threads=4):**
```
HAKMEM: 3.62M ops/s (21.6% of system)
System: 16.76M ops/s (100%)
差: -78.4% 💀
```
**Perf プロファイリング結果:**
```
28.51% superslab_refill 💀💀💀 圧倒的ボトルネック
2.58% exercise_heap (ベンチマーク本体)
```
**問題:** アロケータ (superslab_refill) がベンチマーク本体 (exercise_heap) より CPU time を消費!
**Hot Instructions 分析 (perf annotate):**
```
32.36% cmp $0x3ffff,%r11d ← 262,143 回ループ!
16.78% inc %r13d ← カウンタ++
16.29% add $0x18,%rbx ← ポインタ進める
合計 97.65% の CPU time がループに集中
```
**Root Cause (core/hakmem_tiny_free.inc:917):**
```c
for (int i = 0; i < SUPER_REG_SIZE && scanned < scan_max; i++) {
// ^^^^^^^^^^^ 262,144 エントリを線形スキャン!
SuperRegEntry* e = &g_super_reg[i];
// ... class_idx 不一致でも全エントリをイテレート
}
```
**解決策: Per-class Registry**
```c
// Before: 全 class 混在
SuperRegEntry g_super_reg[262144];
// After: class ごとに分離
SuperRegEntry g_super_reg_by_class[TINY_NUM_CLASSES][4096];
// 8 classes × 4096 entries = 32K total
```
**期待効果:**
- スキャン対象: 262,144 → 4,096 エントリ (-98.4%)
- **期待改善: +200-300%** (2.59M → 7.8-10.4M ops/s)
- **System malloc の 54-73% まで到達可能!** 🎯
**詳細レポート:**
- `PERF_ANALYSIS_2025_11_05.md` - 完全分析 + 実装プラン
- Commit: `859027e` "Perf Analysis: Registry 線形スキャンがボトルネック"
- Branch: `perf-analysis-2025-11-05` (pushed to private repo)
**次のアクション:**
1. **Phase 1 実装** (1-2日): per-class registry に変更
- `core/hakmem_super_registry.{h,c}` 構造変更
- `core/hakmem_tiny_free.inc:917` スキャン簡素化
- 目標: 2.59M → 7.8M ops/s (+3倍!)
2. **Phase 2 実装** (追加 1日): 早期終了 + First-fit
- 最初の freelist 発見で即 return
- 目標: 7.8M → 11.7M ops/s (system の 82%!)
**重要:** Box Refactor は既に動いている!
- Single-thread: 0.46M → 2.59M ops/s (+463%!) ✅
- Multi-thread: 1.81M → 4.19M ops/s (+131%!) ✅
- 外部AIの最適化も効いている (Option A: Inline TLS cache access)
- **ただし Registry スキャンがボトルネックで system には届かず**
---
## 🔔 最新アップデート (2025-11-06 23:xx)
- Build 既定を Box RefactorPhase 6-1.7)に切替済み。
- Makefile に `-DHAKMEM_TINY_PHASE6_BOX_REFACTOR=1` を既定付与。
- 旧系へ切替: `make BOX_REFACTOR_DEFAULT=0 larson_hakmem`
- Larson 2s/4T・5s/4T でセグフォ再発なしを確認SLL-only, FAST_CAP=16, SS_ADOPT=1
- 次フェーズ: mimalloc 対決Larsonへ移行。Hot Tiny クラス向けの refill/binding 帯域最適化に集中。
推奨計測プロファイル(現時点)
- SLL-only Fast: `HAKMEM_TINY_TLS_SLL=1 HAKMEM_TINY_TLS_LIST=0 HAKMEM_TINY_HOTMAG=0`
- Fast tier: `HAKMEM_TINY_FAST_CAP=16`
- Refill: `HAKMEM_TINY_REFILL_COUNT_HOT=64`A/B: 48/64
- Pipeline: `HAKMEM_TINY_SS_ADOPT=1`publish→mail→adopt 通電)
再現コマンド2s/4T, 5s/4T
```
HAKMEM_TINY_REFILL_COUNT_HOT=64 \
HAKMEM_TINY_FAST_CAP=16 \
HAKMEM_TINY_TRACE_RING=0 HAKMEM_SAFE_FREE=0 \
HAKMEM_TINY_TLS_SLL=1 HAKMEM_TINY_TLS_LIST=0 HAKMEM_TINY_HOTMAG=0 \
HAKMEM_WRAP_TINY=1 HAKMEM_TINY_SS_ADOPT=1 \
./larson_hakmem 2 8 128 1024 1 12345 4
./larson_hakmem 5 8 128 1024 1 12345 4
```
デバッグ一発ログ(導通確認)
```
HAKMEM_TINY_REFILL_OPT_DEBUG=1 \
HAKMEM_TINY_TRACE_RING=0 HAKMEM_SAFE_FREE=0 \
HAKMEM_TINY_TLS_SLL=1 HAKMEM_TINY_TLS_LIST=0 HAKMEM_TINY_HOTMAG=0 \
HAKMEM_WRAP_TINY=1 HAKMEM_TINY_SS_ADOPT=1 \
./larson_hakmem 2 8 128 1024 1 12345 4
```
## 🎯 次の主目標mimalloc 対決: Larson/TinyHot
1) Hot Tiny リフィル最適化Box 4 境界の探索コスト縮減)
- [ ] `HAKMEM_TINY_REFILL_COUNT_HOT={48,64}` を A/B。L1d miss と IPC を `perf stat` で取得。
- [ ] `tiny_refill_try_fast()` の class<=3 優先化MailBox→Sticky/Hot の順)と早期 return の徹底。
- [ ] Drain のチェイン spliceを維持しつつ、不要ドレインの抑制remote_pending のみ)。
2) ベンチ・スナップショット
- [ ] `scripts/bench_capture_now.sh` で現構成を保存(日付入り)。
- [ ] `scripts/profiles/tinyhot_best.env``REFILL_COUNT_HOT`/`FAST_CAP` を追記しベストを固定。
3) 比較と回帰防止
- [ ] Larson 2s/4s × threads=1/4/8 で hakmem vs mimalloc を測定・記録。
- [ ] Guard/ASan を OFF のまま長時間(>30sを1回通して安定性を確認。
4) 伸びなければ(次の一手)
- [ ] class<=3 の探索順再編Sticky/Hot/Bench の順入替)を小さく A/B。
- [ ] それでも平坦なら、Front(最前段)の前倒しポップSLL 事前 popを A/BBox 5 内だけで完結)。
## 🔔 最新アップデート (2025-11-06 19:40)
- RemoteTrack Boxdebug-onlyを導入し、ードの状態遷移alloc→remote→drain→freelistを追跡。矛盾時は `REMOTE_TRACK_MISMATCH`BT を出力。
- SlabHandle を全採用経路に統一:所有権取得→`slab_drain_remote_full()`→ゼロ確認→採用失敗時は release。Box2/Box3 の境界を1箇所へ集約。
- TLS alloc 前に remote queue を opportunistic drain`superslab_alloc_from_slab`し、side-table 上に残るノードは guard モードで Fail-Fast。
- guard 走行では `REMOTE_TRACK_MISMATCH stage=alloc_ret` がまだ発生。原因は「remote queue が drain されず、採用境界をすり抜けて TLS 返却されたノードが存在」。
- 次手採用境界に“remote 残があれば採用しない”ゲートを追加し、配布直前に side-table を再確認guard 時のみ)して強制停止する。
## 🔔 最新アップデート (2025-11-04 12:20)
直近の構造改善(箱の積み直し v2
1) SlabHandle Boxcore/slab_handle.h, ~100行
- 所有権取得・リモートキュードレイン・メタデータアクセスをカプセル化
- 型安全なハンドルvalid==1 のときのみ drain/modify が可能)
- Invariant: drain/push/pop は「所有権取得後のみ」実行可境界1箇所
2) 6 箇所のリファクタリングSlabHandle 適用)
- tiny_refill.h: Sticky / Hot / Bench / Mailbox 採用箇所4箇所で、候補決定→SlabHandle 取得→remote_drain→bind の順へ整理
- tiny_mmap_gate.h: Registry scan の採用箇所1箇所を SlabHandle 化
- hakmem_tiny_free.inc: SuperSlab adopt path1箇所を SlabHandle 化
3) 所有権なし drain のバグ修正
- hakmem_tiny_superslab.h:376 の `ss_remote_drain_light()` が ownership 無しで drain していた点を是正
- 修正: `ss_owner_try_acquire()` で取得に成功した場合のみ `ss_remote_drain_to_freelist()` を実行
現状の問題(継続中)
- 依然 crashfault_addr=0x6261。Tiny Debug Ring にて以下を観測:
- [N] event=free_enter class=0 ptr=0x...28c0
- [N+1] event=free_enter class=3 ptr=0x...28c0 ← 同一ポインタを異なる class で 2 回 free
- 結論: freelist 破損ではなく、クラス判定ミス or 二重 freeUAFの可能性が高い。
- Hypothesis:
- hak_super_lookup() の再マップ(旧 SS → 新 SS 同一アドレスで別 classが、二重 free を“別 class”に見せている。
- 実態は上位レイヤ(呼び出し側)の二重 free である可能性が高い。
- 対応(デバッグ方針):
- `HAKMEM_SAFE_FREE=1` を既定ON推奨デバッグ期間
- SS free 境界でブロック整合チェックslab_base/offset/size/容量)と freelist 簡易スキャン(<=64で二重 free を検出。
- SS/Tiny の二重ルックアップ比較(両者が同時に見つかり class が不一致ならリングに記録)。
- `HAKMEM_SAFE_FREE_STRICT=1` なら FailFastSIGUSR2で即座に停止。
最優先課題は「FAST_CAP=0fast-tier OFF時に 4T Larson で再現する SuperSlab remote free の SEGV」を潰すことです。publish→mail→adopt は通電が確認できており、先に Box 2/3Remote/Ownershipを箱単位で健全化します。
### 症状Larson 2s, 4T, FAST_CAP=0
- `hak_tiny_free_superslab()``ss_remote_push()``tiny_publish_notify()` あたりで SIGSEGV。`fault_addr` は常に低い値(例: 0x6261で、invalid ポインタ参照。
- Debug Ring で直前イベントを記録すると、「class=4 の remote free → alloc → free_remote → alloc → …」が循環。ptr は SuperSlab 内に見えるが、キューに積まれた時点で破損疑い。
- FAST_CAP>0 に戻すと crash は発生せず、throughput ≈3.7M ops/s以前より低下。publish pipeline がゼロのままのため本質的な性能改善は未着手。
### 箱構成と境界
1. **FrontGatefast-tier/Hot/TLSの箱**
- `tiny_fast_pop/push`, `hotmag_pop/push`, TLS SLL/Magazine
- ★ 現在の仮説FAST_CAP=0 で front が完全にバイパスされる際、remote queue への戻し順序が乱れている
2. **RemoteQueuess_remote_push/dainの箱**
- SuperSlab remote_heads / remote_counts / slab_listed
- `tiny_publish_notify` が通知境界
3. **Mailbox/Publish の箱**
- `tiny_mailbox_publish/fetch`, `ss_partial_publish/adopt`
4. **Debug Ring可視化の箱**
- `HAKMEM_TINY_TRACE_RING=1` で alloc/free/publish 直前イベントをダンプ
現在の crash は FrontGate または RemoteQueue の内部バグが境界越えで露呈している状態 → 境界を固めて中/外どちらに異常があるか見極める。
### 進行中のタスク
1. **RemoteQueue 安全化(優先)**
- [ ] `ss_remote_push()` にポインタ境界チェック(スーパースラブ内か)と Debug Ring ログ追加
- [ ] remote push 後に `tiny_publish_notify` へ渡すフォーマットss+slab_idx`tiny_mailbox_publish` で検証用トレース
- [ ] `ss_remote_drain_to_freelist` 前後で freelist pointer をトレースし、破損発生位置を特定remote queue 内滞留→再 push の経路切り分け)
- [x] `tiny_remote_sentinel_set()` で 0x6261 汚染を一度だけ捕捉し BT + SIGUSR2 を吐くトラップを追加(再現経路の特定用)
- [x] remote side table を箱化 (`1<<20` + `tiny_remote_side_clear()` + `side_overflow` フォールバック) し、飽和時でも sentinel/next の整合を維持
- [ ] sentinel 汚染が発生した際の callstack を段階別set/scan/drainに収集し、同一ードへの二重 push実質 double free発生箇所を pinpoint
2. **FrontGate バイパス検証**
- [ ] `FAST_CAP=0` 時専用のトレースフラグ `HAKMEM_TINY_DEBUG_FAST0=1` で front 層を最小順序に固定Hot/TLS/Mag を飛ばして直接 remote → TLS リストへ流す)
- [ ] フラグ ON/OFF で crash が消えるか確認 → front 内バグか remote 内バグかを切り分け
3. **再現テスト・フラグ**
- [ ] `scripts/run_larson_defaults.sh tput``HAKMEM_TINY_FAST_CAP` オーバーライドを追加(忘れ防止)
- [ ] `scripts/` に crash 再現スクリプト `run_larson_fast0.sh` を用意2s/4T で SIGSEGV を取得)
4. **publish pipeline 調査(二次優先)**
- crash 解消後に `tiny_publish_notify` の発火率と mailbox drain を再計測し、mmap 偏重の根本原因へ戻る
### 参考コマンド
- Crash 再現:
`HAKMEM_TINY_FAST_CAP=0 HAKMEM_LARSON_TINY_ONLY=1 HAKMEM_TINY_USE_SUPERSLAB=1 ./larson_hakmem 2 8 128 1024 1 12345 4`
- Debug Ring ダンプ:
`HAKMEM_TINY_TRACE_RING=1 ... ./larson_hakmem ...`
`kill -USR2 <pid>` で途中ダンプ、`SIGSEGV` で最終ダンプ
- publish 通電検証(安全ラン):
`scripts/run_larson_defaults.sh tput 2 4`
現状整理Google系/Larson系ベンチの追い上げフェーズ
### 実施済み(即応)
- ベンチ実行時のデバッグ出力によるオーバーヘッドを除去(リリース既定で抑制)
- 変更ファイル: `core/hakmem_tiny_ultra_simple.inc`, `core/hakmem_tiny_metadata.inc`
- 方針: `HAKMEM_DEBUG_VERBOSE` が有効時のみ `fprintf` するようガード
- 効果: ログ出力がボトルネックになるケースを解消(特に tiny/mixed ベンチ)
- `bench_random_mixed_hakmem` を ULTRA_SIMPLE 版で再ビルド・再計測
- ビルド: `make bench_random_mixed_hakmem EXTRA_CFLAGS="-DHAKMEM_TINY_PHASE6_ULTRA_SIMPLE=1 -DHAKMEM_BUILD_RELEASE=1"`
- 旧: 23.49 M ops/sec → 新: 25.82 M ops/sec+9.9%
- Larson ベンチ2秒, 8128B, chunks=1024, rounds=1, seed=12345実行
- system 1T: 14.73 M/s, 4T: 16.76 M/s
- mimalloc 1T: 16.77 M/s, 4T: 16.77 M/s
- HAKMEM 1T: 2.52 M/s, 4T: 4.19 M/s
- `HAKMEM_LARSON_TINY_ONLY=1 HAKMEM_DISABLE_BATCH=1` でも同等(~2.56M / ~4.19M
### 観測と仮説Larson 遅さの主因)
- 既知の分析と一致: 再利用不足 → ページフォールト/ゼロ化増 → sys 時間が支配的
- Tiny フロントのヒット率が不足SLL を使うが、Larson パターンで十分に温まらない)
- Metadata 版Phase 6-1.6)は refill 未対応部分があり現状は封印、ULTRA_SIMPLE で進めるのが安全
### 当面の方針(追いつくまでの短期プラン)
1) Larson 用バイナリに ULTRA_SIMPLE を徹底適用してフロントのヒット率を最大化
- 目標: free/alloc ともに 34 命令の経路に乗せる(既に `free()` は alignment-guess 経路有効)
- ビルド: `EXTRA_CFLAGS="-DHAKMEM_TINY_PHASE6_ULTRA_SIMPLE=1 -DHAKMEM_BUILD_RELEASE=1"`
2) Mixed/Larson の再測定と perf 取得
- コマンド: `scripts/run_larson.sh -d 2 -t 1,4`
- 詳細: `scripts/run_larson_perf.sh`PF/IPC/branch/L1d を併記)
3) 迅速チューニング候補(小粒で効く順)
- Refill 個数の抑制64→16〜32で温まり時間短縮、TL-再利用密度を上げ PF を減らす
- SuperSlab サイズの下限を 1MB に固定(`HAKMEM_TINY_SS_MIN_MB=1`で初期PFを軽減
- 事前ウォームLarson開始前に `sll_refill_small_from_ss()` をサイズ帯毎に数回)
- size→class 変換の LUT 確認(既に O(1) だが、統合経路でも分岐予測を安定化)
4) 中期(必要なら)
- Dual Free Listslocal/remote 分離)を Tiny に導入(既存の設計を Tiny へ移植)
- Metadata 版の refill 実装を完了(ヘッダ +8B で owner 判定ゼロ化)し安定化後に切替検討
### 直近 TODO実行順
- [ ] Larson ULTRA_SIMPLE ビルドの固定化larson_hakmem に EXTRA_CFLAGS 反映)
- [ ] `scripts/run_larson.sh -d 2 -t 1,4` の再実行と結果更新
- [ ] `scripts/run_larson_perf.sh` で PF/CPU 内訳を取得してボトルネックの再確認
- [ ] Refill 個数/SSサイズのチューニングで 1T: ~10M, 4T: ~10M の域まで引き上げ
備考: この更新で、測定時のイズfprintfは排除済み。以降の差分は純粋にアルゴリズム/チューニング起因として評価可能。
---
## 🔧 バグ修正と3点セット実装2025-11-03 09:00
結論: 「free 経路の破綻」は修正済み。OOM は設計的な再利用探索の不足が残課題。
- 修正
- ULTRA_SIMPLE free を same-thread のみ直 push に制限。cross-thread free は従来経路へフォールバック。
- 変更: `core/hakmem.c:820`, `core/hakmem_tiny_ultra_simple.inc:96`
- OOM ワンショット診断errno/ss_size/alloc_size/RLIMIT/VmSize/RSS/SSカウンタを追加。
- 変更: `core/hakmem_tiny_superslab.c:182`
- 3点セット段階導入・既定OFF
1) remote queuecross-thread free 時に per-slab MPSC stack へ)
2) partial publish/adoptクラス別公開リング→refill 前に adopt
3) adopt 時の remote drain + owner 移譲best-effort
- 変更: `core/hakmem_tiny_superslab.h`, `core/hakmem_tiny.c`, `core/hakmem_tiny_free.inc`
- 有効化: `HAKMEM_TINY_SS_ADOPT=1`
- 観測Larson, 4T
- adopt OFF既定: ~4.19 M/s 安定、稀に ENOMEM 継続
- adopt ON: OOM は減るがゼロにはならず。1T は低下傾向(~2.32.4 M/s→チューニング要
- 次のチューニング(提案)
- `SS_PARTIAL_RING`2/4/8A/B、adopt 選好remote多い slab 優先)、採用頻度の抑制(クールダウン)
- perf statPF/DTLBの比較で改善度を定量化
使い方A/B:
```
# 既定adopt OFF
./larson_hakmem 2 8 128 1024 1 12345 4
# adopt ON3点セット有効、A/B計測
HAKMEM_TINY_SS_ADOPT=1 ./larson_hakmem 2 8 128 1024 1 12345 4
```
## 🎯 現在のミッション: Phase 6 - Learning-Based Tiny Allocator
**Status**: ✅ **Phase 6-1 完了!** 🚀🎉
**最新アップデート (2025-11-02 18:00):**
-**Phase 6-1: Ultra-Simple Fast Path 完了!** 🚀🚀🚀
- **驚異的な結果**: **478.60 M ops/sec** (64B LIFO)
- **System malloc の +174% 高速!** (174.69 M/s → 478.60 M/s)
- **現行 HAKMEM の +777% 高速!** (54.56 M/s → 478.60 M/s)
- **4.17 cycles/op** (理論的最小値に近い)
- **100% hit rate** (10M ops 中 miss 1回のみ)
- **実装**: "Simple Front + Smart Back" (HAKX Mid-Large +171% の成功パターン適用)
- Fast path: 3-4 命令 (tcache風 pop from free list)
- Backend: Simple mmap-based chunk allocator
- Files: `core/hakmem_tiny_simple.{h,c}` (200行)
- **なぜこんなに速い?**
1. Ultra-simple fast path (分岐予測完璧)
2. Perfect L1 cache locality (TLS array 64B)
3. Freed blocks 即再利用 (LIFO で 100% hit)
4. ゼロオーバーヘッド (magazine layers なし)
- **次のステップ**:
- [ ] Comprehensive benchmark (21 patterns)
- [ ] Memory efficiency 測定
- [ ] Phase 2: Learning layer 設計
---
## 📋 過去の試行 (Phase 5以前)
**Status (旧)**: ✅ Phase 2+1完了 → ❌ Phase 3失敗 → ❌ Phase 4-A1失敗 → ❌ **Phase 5-A失敗** → ❌ **Phase 5-B-Simple 失敗** 💥
**最新アップデート (2025-11-02 07:00):**
-**Phase 5-B-Simple 失敗**: **-71% (ST) / -35% (MT)** 💥💥💥
- **Single-threaded (bench_tiny_hot, 64B)**:
- System: 169.49 M ops/sec
- HAKMEM Phase 5-B: 49.91 M ops/sec
- **Regression: -71%** (3.4x slower!)
- **Multi-threaded (bench_mid_large_mt, 8-32KB, 2 threads)**:
- System: 11.51 M ops/sec
- HAKMEM Phase 5-B: 7.44 M ops/sec
- **Regression: -35%** (1.5x slower)
- ⚠️ **NOTE**: Mid/large benchmark tests 8-32KB allocations (outside Tiny range), not directly testing Phase 5-B
- **根本原因分析**:
1. **Magazine capacity ミスチューン**: 64 slots は ST workload には小さすぎる
- Batch=100 の場合、2回に1回は slow path に落ちる
- System allocator は tcache (7+ entries per size) で高速
2. **Migration logic オーバーヘッド**: Slow path での free list → Magazine migration が高コスト
- Batch migration (32 items) が頻繁に発生
- Pointer chase + atomic operations
3. **Dual Free Lists の誤算**: ST では効果ゼロ、むしろオーバーヘッド
- ST では remote_free は発生しない
- Dual structures のメモリ overhead のみ
4. **Unified Magazine の問題**: 統合で simplicity は得たが performance は失った
- 旧 HotMag (128 slots) + Fast + Quick の組み合わせのほうが高速だった
- 単純化 ≠ 高速化
- **教訓**:
-**Magazine unification 自体は良アイデア** (complexity 削減)
-**Capacity tuning が不適切** (64 slots → 128+ 必要)
-**Dual Free Lists は MT 専用** (ST で導入すべきでない)
-**Migration logic が重すぎる** (batch size 削減 or lazy migration 必要)
- **次のアクション**:
1. ⏮️ **Phase 5-B をロールバック** (git revert)
2. 📊 **Baseline 再測定** (clean state で確認)
3. 🤔 **Phase 5-B-v2 を検討** (Magazine unification only, Dual Free Lists なし)
4. 🎯 **Alternative approach**: Phase 6 系 (L25/SuperSlab 最適化) に移行
- 🚀 **Phase 5-B-Simple 開始** (2025-11-03 04:00): Dual Free Lists + Magazine統合 🎯
- **Phase 5-A-v2 をスキップする理由**:
1. **HAKMEM は既に O(1) size→class 変換** (`g_size_to_class_lut_1k[size]`)
2. **多層 cache が既に 95%+ hit** → direct cache 追加は効果薄い
3. **TLS 変数追加で cache pollution** リスク
4. **期待値**: ±0% (効果とオーバーヘッドが相殺)
- **Phase 5-B-Simple に集中** (期待: +15-23%)
- **2つの最適化を統合**:
1. **Dual Free Lists** (mimalloc Phase 5-B): +10-15%
- Local free で atomic 不要 (10+ cycles 削減)
- Cache locality 向上
2. **Magazine 統合** (シンプル化): +3-5%
- 3-4層 → 2層に削減
- TLS cache line を 3-4本 → 1-2本に削減
- Branch を 3-4回 → 1回に削減
- **実装計画** (3-4 days):
```c
// === Before (現状: 複雑) ===
// Layer 1: HotMag (classes 0-2)
if (g_hotmag_enable) ptr = hotmag_pop();
// Layer 2: Hot functions (classes 0-3)
if (g_hot_alloc_fn[cls]) ptr = tiny_hot_pop_classN();
// Layer 3: Fast cache
ptr = tiny_fast_pop(cls);
// Layer 4+: Slow path (single freelist + atomic)
ptr = hak_tiny_alloc_slow();
// === After (Phase 5-B-Simple: シンプル) ===
// Layer 1: Unified Magazine (統合版)
TinyUnifiedMag* mag = &g_tls_mag[cls];
if (mag->top > 0) return mag->items[--mag->top]; // ← 1 branch!
// Layer 2: Slow path (Dual Free Lists)
return tiny_alloc_slow_dual(cls); // ← local_free (no atomic!)
```
- **Dual Free Lists の核心**:
```c
typedef struct TinySlab {
// Phase 5-B: Dual Free Lists
void* local_free; // Local frees (no atomic!)
_Atomic(void*) thread_free; // Remote frees (atomic)
} TinySlab;
// Free: Local は atomic なし!
if (pthread_equal(slab->owner_tid, self)) {
*(void**)ptr = slab->local_free;
slab->local_free = ptr; // ← No atomic! 10+ cycles 削減
} else {
atomic_push(&slab->thread_free, ptr);
}
// Migration: Batch で効率化
if (!slab->free && slab->local_free) {
slab->free = slab->local_free; // Pointer swap only
slab->local_free = NULL;
}
```
- **期待効果まとめ**:
| 最適化 | 期待効果 | 累積 M ops/sec |
|--------|---------|----------------|
| Baseline | - | 16.53 |
| Dual Free Lists | +10-15% | 18.2-19.0 |
| Magazine 統合 | +3-5% | 18.7-20.0 |
| Branch 削減 | +2-3% | 19.1-20.6 |
| **合計** | **+15-23%** | **19.1-20.3 M ops/sec** 🎯 |
- **実装ステップ**:
- **Step 1** (Day 1): Unified Magazine 実装 & benchmark
- **Step 2** (Day 2): Dual Free Lists 追加 (TinySlab 構造変更)
- **Step 3** (Day 3): Free path 書き換え (local_free / thread_free 分離)
- **Step 4** (Day 4): Migration logic & 最終 benchmark
- **削減される TLS 変数**:
```c
// Before (~1600 bytes + flags)
__thread TinyHotMag g_tls_hot_mag[8]; // 1024 bytes
__thread void* g_fast_head[8]; // 64 bytes
__thread uint16_t g_fast_count[8]; // 16 bytes
__thread TinyQuickSlot g_tls_quick[8]; // 512 bytes
// After (2048 bytes のみ)
__thread TinyUnifiedMag g_tls_mag[8]; // 2048 bytes
```
- ❌ **Phase 5-A失敗**: **-3~-7.7%** (16.53 → 15.25-16.04 M ops/sec) 💥
- **実装**: Global `slabs_direct[129]` でO(1) direct page cache
- **結果**: 性能**大幅悪化**(期待+15-20% → 実際-3~-7.7%
- **根本原因**: **Thread-local vs Global の設計ミス**
```c
// hakmem_tiny.h - 間違った実装
typedef struct {
TinySlab* slabs_direct[129]; // ❌ Global = 全threadが共有
} TinyPool; // g_tiny_pool は global
// mimalloc - 正しい実装
typedef struct mi_heap_s {
mi_page_t* pages_free_direct[129]; // ✅ Heap ごと = thread-local
} mi_heap_t; // __thread mi_heap_t* heap
```
- **性能悪化の3つの要因**:
1. **pthread_self() オーバーヘッド**: 毎回 `tiny_self_pt()` syscall (~20-40 cycles)
2. **Remote slab hit**: Global cache が他 thread の slab を指す
→ Owner check → Cache clear → Fallback (無駄な3段階処理)
3. **余分な分岐**: 既存 fast path の前に新たな条件分岐層を追加
- **測定結果** (2回計測):
- Run 1: 15.25 M ops/sec (-7.7%)
- Run 2: 16.04 M ops/sec (-3.0%)
- Baseline: 16.53 M ops/sec
- **教訓**: **mimalloc の直訳は危険** - アーキテクチャ差異を理解すべき
- mimalloc: `mi_heap_t` は thread-local → `pages_free_direct` も thread-local
- HAKMEM: `TinyPool g_tiny_pool` は global → `slabs_direct` は全 thread 共有
- **Revert**: 全ての Phase 5-A 変更を完全に revert (16.33 M ops/sec に復元)
- **次の戦略**: Phase 5-A-v2 で **thread-local slabs_direct** を実装
```c
// 次回の正しい実装方針
__thread TinySlab* g_tls_slabs_direct[129]; // ✅ Thread-local
```
- 🎊 **Phase 5完了: 47% Gap の正体を解明!** 🔍
- **mimalloc 完全分析**: 10,000語超の詳細レポート作成完了
- **3つのファイル生成**:
- `MIMALLOC_ANALYSIS_REPORT.md` - 詳細技術分析
- `MIMALLOC_KEY_FINDINGS.md` - 要約版
- `MIMALLOC_IMPLEMENTATION_ROADMAP.md` - 実装計画
- **Gap の内訳判明**:
1. **Direct Page Cache (O(1))**: +15-20% ← **最大のボトルネック!**
2. **Dual Free Lists**: +10-15%
3. **Branch Hints + Lazy Updates**: +5-8%
- **重要な発見**: mimalloc も **intrusive linked list** を使用
→ Phase 3/4-A1 の「linked-list は最適」は**正しかった**
→ Gap は**マイクロ最適化**から来る(データ構造選択ではない)
- **Hot Path サイクル比較**:
- mimalloc: ~20 cycles (TLS 2 + O(1) lookup 3 + Pop 5)
- HAKMEM: ~30-35 cycles (TLS 3 + Binary search 5 + Atomic pop 15)
- **差分: 10-15 cycles → 47% の性能差**
- 📋 **次のアクション**: Phase 5-A 実装Direct Page Cache
- **期待効果**: +15-20% (16.53 → 19.0-19.8 M ops/sec)
- **Effort**: 1-2 days
- **Risk**: Low
- ❌ **Phase 4-A1失敗**: **-0.24%** (16.53 → 16.49 M ops/sec) 💥
- 実装: TLS-BUMP即値化immediate-value hot functions
- 結果: 性能**悪化**(期待+5-8% → 実際-0.24%
- 原因: **TLS-BUMPは mixed workloadで機能しない**
```c
// hakmem_tiny_refill.inc.h:258
if (meta->freelist != NULL) return NULL; // linear mode only
```
- 根本問題: bench_random_mixed (50% alloc, 50% free) では freelist が常に populated
- **TLS-BUMPは monotonic allocationのみ有効**連続allocのみ
- 教訓: **混在ワークロードでは linked-list が最適** - 追加層は純粋なオーバーヘッド
- 🚀 **Phase 4戦略決定** (2025-11-02 22:00)
- ChatGPT Pro相談完了構造的アプローチで+10-25%を狙う
- 核心: 現在の6-7層を**3層に統合**層あたり2-3ns削減
- 即効施策: TLS-BUMP即値化+5-8%期待) ← **失敗!**
- 中期施策: 小マガジン128化+3-5%+ 3層リファクタ+10-15%
- 長期施策: Mid/Large構造改革per-core arena + TL-Segment
- ❌ **Phase 3失敗教訓**: **+0.24% のみ** (16.53 → 16.57 M ops/sec)
- 実装: v3 allocator (magazine-based single-tier)
- 原因: 既存のlinked-listが既にmagazineより最適
- 教訓: **Linked list > Magazine array**(メモリアクセス少ない)
- ✅ **Phase 2+1完了**: **+1.8% 改善** (16.24 → 16.53 M ops/sec)
- Phase 2: TLS range check実装 (owner_slab高速化)
- Phase 1: free()順序変更 (Tiny → Mid MT)
- 結果: 理論通り動作、軽微な改善 ✅
- 📋 **次のアクション**: Phase 4-A1実装開始TLS-BUMP即値化
**Phase 2+1の教訓:**
1. ✅ TLS range check + 順序変更は**理論通り動作**50% overhead削減
2. ❌ free() overheadが想定より小さかった実測 ~8% vs 想定 ~21%
3. 💡 **シングルスレッドでは mutex overhead は誤差レベル**
4. 🎯 さらなる改善には**malloc/alloc側**を攻めるべき27% overhead!
**コスト分析Phase 2+1:**
```
Before (Mid → Tiny):
Mid null checks: 4 cycles
Mid mutex (empty): 15 cycles ← 想定より軽い!
Tiny registry: 15 cycles
Total: 34 cycles
After (Tiny → Mid):
TLS check: 2 cycles
Tiny registry: 15 cycles
Total: 17 cycles
Savings: 17 cycles (50% 削減)
全体への影響: 17 cycles × (free overhead 8%) ≈ +2% 🎯
```
---
## 📊 最新perf分析結果 (2025-11-01)
### ベンチマーク条件
- **ワークロード**: bench_random_mixed (8-128B, 16 size classes)
- **パラメータ**: 200K cycles, 400 ws, seed=1
- **スレッド**: 1 (シングルスレッド)
### パフォーマンス比較
| Allocator | Throughput | vs mimalloc |
|-----------|------------|-------------|
| HAKMEM | 16.46 M ops/sec | 68% |
| mimalloc | 24.21 M ops/sec | 100% |
**Gap: 32% slower** ⚠️
### 根本原因: アロケータオーバーヘッド
| Allocator | Total Overhead | malloc/alloc | free/delete |
|-----------|----------------|--------------|-------------|
| mimalloc | 17% | 7.35% | 9.77% |
| HAKMEM | **49%** | 27% | 21.64% |
**HAKMEM は 3x のCPUサイクルを消費**
---
## 🔴 Critical Bottlenecks (優先度順)
### 1. `free()` の無駄なMid Range チェック (Priority 1) 🔥
**問題**:
```
free(ptr)
Lock g_mid_registry mutex (2.29%) ← 全freeでロック!
Binary search g_mid_registry (7.08%) ← 8KB-32KB範囲チェック
Unlock mutex (3.93%)
hak_tiny_owner_slab (4.98%) ← 8B-1KB範囲チェック (本来これだけでOK!)
hak_tiny_free ← 実際のfree
```
**無駄**: 8-128Bのワークロードなのに、全freeでMid Range (8KB-32KB) をチェック!
- mutex lock/unlock: 6.22%
- mid_lookup: 7.08%
- **合計 13.3% のオーバーヘッド** が不要
**修正方針**:
```c
free(ptr)
Fast TLS range check (Tiny: 8B-1KB) ← NO MUTEX, 直接チェック
↓ (hit: ~90% for this workload)
hak_tiny_free
↓ (miss)
Check Mid registry (with mutex)
↓ (miss)
Check L25/other
```
**期待効果**: **~13% スループット向上** (16.46 → 18.6 M ops/sec)
---
### 2. `hak_tiny_owner_slab` の線形探索 (Priority 2) 🟡
**問題**:
- 現在: TLS slab listを線形探索 (3.50% overhead)
- mimalloc: ポインタビットパターンで高速判定
**修正方針** (mimalloc-style):
```c
// 1. Alignment check
if ((ptr & (SLAB_SIZE - 1)) != 0) return NULL; // Not slab-aligned
// 2. TLS range check
if (ptr < tls_heap_start || ptr >= tls_heap_end) return NULL;
// 3. Direct slab header access
SlabHeader* slab = (SlabHeader*)(ptr & ~(SLAB_SIZE - 1));
return slab;
```
**期待効果**: **~3% スループット向上** (3.50% → ~0.5%)
---
### 3. `hak_tiny_alloc_slow` の複雑なフォールバック (Priority 3) 🟢
**問題**:
- 4段階フォールバック: hotmag → TLS list → superslab → magazine
- 各段階でTLS変数アクセス + 分岐
**修正方針**:
```c
// 1. TLS magazine (most common)
if (fast cache has space) return pop();
// 2. Superslab (if enabled)
if (g_use_superslab && superslab_active) return superslab_alloc();
// 3. Central refill
return refill_from_central();
```
**期待効果**: **~2-3% スループット向上**
---
### 4. 関数インライン化 (Priority 4) 🟢
**候補**:
- `hak_tiny_alloc` → `malloc` にインライン
- `hak_tiny_owner_slab` → `free` にインライン
**期待効果**: **~2% スループット向上**
---
## 🎨 Phase 2詳細設計: mimalloc-style Fast Owner Check
### 現在の問題点
**現在の `hak_tiny_owner_slab()` 実装:**
```c
TinySlab* hak_tiny_owner_slab(void* ptr) {
int hash = registry_hash(slab_base); // 関数呼び出し + hash計算
for (int i = 0; i < SLAB_REGISTRY_MAX_PROBE; i++) // 最大8回ループ
// array access + 比較 + atomic load
if (ptr < start || ptr >= end) return NULL; // range validation
}
```
**コスト分析:**
- **Positive lookup** (Tiny allocation): hash + 1-2 probes + range check = **~10-15 cycles**
- **Negative lookup** (非Tiny): hash + 1-2 probes = **~8-10 cycles** ← Phase 1失敗の原因
### Phase 2新設計: Ultra-Fast Owner Check
**目標:**
- **Negative lookup**: **1-2 cycles** (現状: 8-10 cycles) → **~85% 削減** 🎯
- **Positive lookup**: **5-8 cycles** (現状: 10-15 cycles) → **~40% 削減**
**実装戦略:**
```c
// Phase 2: Ultra-fast owner check (mimalloc-style)
static inline TinySlab* hak_tiny_owner_slab_fast(void* ptr) {
// Step 1: TLS heap range check (1-2 cycles) ← KEY OPTIMIZATION
// Check if ptr is in this thread's Tiny heap range
if (ptr < g_tls_tiny_min || ptr >= g_tls_tiny_max) {
return NULL; // Outside TLS range → FAST NEGATIVE LOOKUP! ✅
}
// Step 2: Slab base calculation (1 cycle)
uintptr_t slab_base = (uintptr_t)ptr & ~(TINY_SLAB_SIZE - 1);
// Step 3: Registry lookup (2-3 cycles)
// Now only called for pointers IN TLS range (hit rate ~90%)
TinySlab* slab = registry_lookup(slab_base);
if (!slab) return NULL;
// Step 4: Range validation (1-2 cycles)
if (ptr < slab->base || ptr >= slab->base + TINY_SLAB_SIZE) {
return NULL;
}
return slab;
}
```
**最適化ポイント:**
1. **TLS range check を最初に実行** → Negative lookup を 1-2 cycles に!
2. Registry lookup は TLS range内のみ実行 → Hit rate ~90%
3. 既存のregistry_lookupを活用 → 安全性維持
### 実装要件
**1. TLS heap range tracking:**
```c
// hakmem_tiny.h
extern __thread void* g_tls_tiny_min;
extern __thread void* g_tls_tiny_max;
// hakmem_tiny.c
__thread void* g_tls_tiny_min = (void*)UINTPTR_MAX;
__thread void* g_tls_tiny_max = NULL;
// Update on slab allocation (in allocate_new_slab)
static inline void update_tls_tiny_range(void* slab_base) {
if (slab_base < g_tls_tiny_min) g_tls_tiny_min = slab_base;
void* slab_end = slab_base + TINY_SLAB_SIZE;
if (slab_end > g_tls_tiny_max) g_tls_tiny_max = slab_end;
}
```
**2. 既存コードとの互換性:**
- `hak_tiny_owner_slab()` をfast版に置き換え
- Registry lookup機構はそのまま維持 (thread-safe)
- SuperSlabは別途処理既存通り
### 期待効果
**Negative lookup高速化 (非Tiny allocations):**
- Before: hash (2-3 cycles) + probe (3-4 cycles) + compare = 8-10 cycles
- After: range check (1-2 cycles) = **~85% 削減** 🔥
**Positive lookup高速化 (Tiny allocations):**
- Before: hash + probe + range = 10-15 cycles
- After: range + registry + range = 5-8 cycles = **~40% 削減**
**Combined with Phase 1 (順序変更):**
```
Tiny allocations (90%):
Before: Mid mutex (13.6%) + owner_slab (8-10 cycles)
After: owner_slab_fast (1-2 cycles) → Tiny free
Savings: 13.6% × 0.9 = ~12% 🎯
Mid MT allocations (10%):
Before: owner_slab (8-10 cycles) + Mid mutex
After: owner_slab_fast (1-2 cycles) + Mid mutex
Savings: 8 cycles × 0.1 = ~0.5%
Total expected gain: ~12-13% (16.46 → 18.4-18.6 M ops/sec)
```
---
## 📋 実装タスクリスト
### ✅ Phase 1: free() チェック順序変更 - **完了 (revert含む)** ✅
**実装完了:**
- ✅ 1-1. `hakmem.c` free()のチェック順序を変更 (Tiny→Mid)
- ✅ 1-2. ベンチマーク測定: 16.34 M ops/sec (**-0.73% regression**)
- ✅ 1-3. perf再測定して原因分析: `hak_tiny_owner_slab()` が重い (8-10 cycles)
- ✅ 1-4. Phase 1を revert: 16.24 M ops/sec (baseline復帰)
**教訓:** `hak_tiny_owner_slab()` が重すぎて全freeに適用不可 → Phase 2で根本解決
---
### 🚀 Phase 2: owner_slab 高速化 - **実装中** 🚀
**目標:**
- Negative lookup: 8-10 cycles → **1-2 cycles** (~85%削減)
- Positive lookup: 10-15 cycles → **5-8 cycles** (~40%削減)
- Phase 2完了後、Phase 1再適用 → **~12-13%向上**
**タスクリスト:**
- [ ] 2-1. TLS range tracking変数追加
- [ ] `hakmem_tiny.h`: extern宣言追加
- [ ] `hakmem_tiny.c`: TLS変数定義
- [ ] `allocate_new_slab()`: range更新ロジック追加
- [ ] 2-2. `hak_tiny_owner_slab_fast()` 実装
- [ ] TLS range check (negative lookup高速化)
- [ ] Registry lookup (既存機構活用)
- [ ] Range validation (safety確保)
- [ ] 2-3. 既存 `hak_tiny_owner_slab()` を置き換え
- [ ] 関数名変更 or 実装差し替え
- [ ] 全呼び出し箇所で動作確認
- [ ] 2-4. ベンチマーク測定
- [ ] `./bench_random_mixed_hakmem 200000 400 1`
- [ ] 目標: ~16.24 M ops/sec (変化なし or 微増)
- [ ] 理由: owner_slab単体では効果小、Phase 1再適用で効果発揮
- [ ] 2-5. Phase 1再適用 (順序変更)
- [ ] `hakmem.c` free(): Tiny → Mid に変更
- [ ] ベンチマーク測定
- [ ] 目標: **18.4-18.6 M ops/sec (+12-13%)** 🎯
### 🎯 Phase 3: Allocation Hot Path簡素化 - **設計完了** 📐
**Status**: Phase 2+1完了 → Phase 3設計完了 (2025-11-01 22:00)
**目標**: malloc/allocのoverhead削減 (27% → 20%)
- **期待効果**: +5-10% (16.53 → 17.5-18.0 M ops/sec)
- **アプローチ**: mimalloc-style single-tier hot path
**現状分析 (perf):**
```
malloc/alloc overhead: 27%
- hak_tiny_alloc_slow: 9.33% (複雑なフォールバック)
- hak_tiny_alloc: 7.12% (hot path)
- malloc wrapper: 3.67%
- その他: ~7%
問題点:
❌ 6+段階のフォールバックチェーン
❌ 多数のTLS変数アクセス
❌ Heavy stack frame (14.05% in prologue!)
❌ Branch misprediction
```
**現在のフローチャート:**
```c
hak_tiny_alloc(size)
1. size → class_idx
2. ifdef BENCH_FASTPATH:
- SLL head check
- TLS Magazine check
- SLL refill
3. HotMag front (class <= 2)
4. Hot alloc functions (class 0-3)
5. tiny_fast_pop()
6. hak_tiny_alloc_slow() ← 9.33% overhead!
- HotMag refill
- TLS list refill
- SuperSlab fallback
```
**Phase 3新設計 (mimalloc-style):**
```c
void* hak_tiny_alloc_v3(size_t size) {
// 1. Size → class (branchless)
int class_idx = hak_tiny_size_to_class(size);
if (__builtin_expect(class_idx < 0, 0)) return NULL;
// 2. Single-tier TLS magazine (HOT PATH - 2-3 cycles)
TinyTLSMag* mag = &g_tls_mags[class_idx];
int top = mag->top;
if (__builtin_expect(top > 0, 1)) {
void* ptr = mag->items[--top].ptr;
mag->top = top;
return ptr; // ← 最速パス! 🚀
}
// 3. Refill + fallback (cold path)
return hak_tiny_alloc_slow_v3(size, class_idx);
}
static void* __attribute__((cold, noinline))
hak_tiny_alloc_slow_v3(size_t size, int class_idx) {
TinyTLSMag* mag = &g_tls_mags[class_idx];
// Step 1: Try refilling magazine from SuperSlab
if (mag_refill_from_superslab(class_idx, mag, 32) > 0) {
return mag->items[--mag->top].ptr;
}
// Step 2: Allocate new SuperSlab
return hak_tiny_alloc_superslab(class_idx);
}
```
**削減内容:**
```
Branches: 6+ → 2
TLS変数: 多数 → 1つ (mag)
Stack frame: Heavy → Minimal (inline候補)
Hot path cycles: ~20-30 → ~5-8 (60-70%削減) 🎯
```
**タスクリスト:**
- [ ] 3-1. `hak_tiny_alloc_v3()` 実装
- [ ] Single-tier magazine hot path
- [ ] Branchless size-to-class
- [ ] Minimal stack frame
- [ ] 3-2. `hak_tiny_alloc_slow_v3()` 簡素化
- [ ] 2-tier fallback (magazine refill → superslab)
- [ ] Remove HotMag/TLS list/fast_pop complexity
- [ ] `__attribute__((cold, noinline))`
- [ ] 3-3. Magazine refill最適化
- [ ] `mag_refill_from_superslab()` 専用関数
- [ ] Batch refill (32-64 items)
- [ ] Zero overhead on hit
- [ ] 3-4. ベンチマーク測定
- [ ] `./bench_random_mixed_hakmem 200000 400 1`
- [ ] 目標: **17.5-18.0 M ops/sec (+5-10%)** 🎯
- [ ] vs mimalloc: 72-74%
---
### Phase 4: 関数インライン化 (Phase 3成功後)
**Phase 4: インライン化**
- `hak_tiny_alloc_v3` → `malloc` (force inline)
- `hak_tiny_free` → `free` (force inline)
- 目標: +2-3% (18.0-19.0 M ops/sec)
---
## 🎯 マイルストーン
| Phase | Target Throughput | Actual Result | vs mimalloc | Status |
|-------|-------------------|---------------|-------------|--------|
| Baseline | 16.24 M ops/sec | 16.24 M ops/sec | 67% | ✅ Baseline |
| ~~Phase 1 (単体)~~ | ~~18.6 M ops/sec~~ | ~~16.34 M ops/sec~~ | ~~67%~~ | ❌ **FAILED** (-0.73%) |
| Phase 2 (単体) | ~16.2 M ops/sec | 15.70 M ops/sec | 65% | ✅ **完了** (-3.3%, 予想通り) |
| **Phase 2+1 Combined** | ~~18.4-18.6 M ops/sec~~ | **16.53 M ops/sec** | **68%** | ✅ **完了** (+1.8%) |
| ~~Phase 3 (v3 alloc)~~ | ~~17.5-18.0 M ops/sec~~ | ~~16.57 M ops/sec~~ | ~~68%~~ | ❌ **FAILED** (+0.24%) |
| Phase 4 (インライン化) | 18.0-19.0 M ops/sec | - | 74-79% | ⏳ TODO |
| **Ultimate Goal** | **22-24 M ops/sec** | - | **90-100%** | 🌟 Long-term Target |
**Phase 2+1の洞察 (2025-11-01 21:30):**
- ✅ Phase 2+1は**理論通り動作** (+1.8% 改善)
- ❌ 期待値(+12-13%)との乖離は**Mid MT mutexコスト見積もり誤り**が原因
- 💡 シングルスレッド、空registryでは mutex は超軽量(~15 cycles
- 🎯 **次の主戦場**: malloc/alloc側の27% overheadを攻める
---
## 📁 関連ドキュメント
- ✅ **perf分析レポート**: `docs/PERF_ANALYSIS_TINY_MIXED.md`
- 詳細なボトルネック分析
- perf annotate結果
- 最適化ロードマップ
- 📊 **ベンチマーク比較**: 他のAIちゃんのレビュー
- Mixed: HAKMEM 66.7% of mimalloc (weak)
- Mid MT: HAKMEM 129.4% of mimalloc (strong)
---
## 💡 Next Action (今すぐやること)
### 最初のステップ: free() Fast TLS Check実装
```bash
# 1. hakmem.c のfree()を開いて、Fast TLS checkを追加
vim core/hakmem.c
# 2. 変更箇所
# free() の冒頭 (guard check後) に:
# - TLS Tiny heap範囲チェック
# - Hit時は直接 hak_tiny_free()
# - Miss時は既存フロー
# 3. ビルド & ベンチマーク
make bench_random_mixed_hakmem
./bench_random_mixed_hakmem 200000 400 1
# 4. perf確認
perf record -F 999 -g ./bench_random_mixed_hakmem 200000 400 1
perf report --stdio -n --percent-limit 1
```
---
## 🔥 モチベーション
**目標**: Tiny Mixed Workloadで mimalloc に匹敵する性能
**現状**:
- Mid MT: **138% of mimalloc** ✅ (Already winning!)
- Tiny Mixed: **68% of mimalloc** ⚠️ (Need improvement!)
**今日の目標**:
- Phase 1実装で **77% of mimalloc** (16.46 → 18.6 M ops/sec)
**Let's optimize! 🚀**
---
## 🚀 Phase 4: 構造的最適化戦略2025-11-02策定
### 戦略概要
**目標**: mimalloc比90-100%到達現状68% → 22-24 M ops/sec
**核心アプローチ**:
1. **3層への統合**現在の6-7層を削減
2. **TLS-BUMP即値化**hot-class専用最適化
3. **段階的実装**(即効 → 中期 → 長期)
### Phase 4-A: 即効施策(今週実装)
#### A-1: TLS-BUMP即値化 + hot-class強化 ⚡
**期待効果**: +5-8%16.53 → 17.5-18.0 M ops/sec
**実装内容**:
- `g_ultra_bump_shadow`有効化(現在無効)
- hot-class16/32/64Bを即値化関数に書き換え
- 分岐ゼロ化cmov版も検討
**変更ファイル**:
- `core/hakmem_tiny_hot_pop.inc.h` - 即値化版関数
- `core/hakmem_tiny.c` - BUMP有効化
- `core/hakmem_tiny_config.c` - デフォルト設定
**コード例**:
```c
// Before (現在)
void* head = g_fast_head[class_idx];
if (head) {
g_fast_head[class_idx] = *(void**)head;
return head;
}
// After (A-1)
uint8_t* p = g_tls_bcur[0];
uint8_t* n = p + 16; // ← 即値!
if (likely(n <= g_tls_bend[0])) {
g_tls_bcur[0] = n;
return p; // ← 分岐1つ、TLS書き込み1回
}
return tiny_bump_refill_cold(0); // noinline
```
#### A-2: 小マガジン容量最適化 📦
**期待効果**: +3-5%
**実装内容**:
- class 0-28/16/32B: 128エントリ固定
- class 3+64B+: 64エントリ現状維持
- L1ヒット率向上、ワーキングセット最適化
**変更ファイル**:
- `core/hakmem_tiny_magazine.h` - 容量定数
### Phase 4-B: 中期施策1-2週間
#### B-1: 3層への統合リファクタリング 🏗️
**期待効果**: +10-15%(累積+20-25%
**実装内容**:
```c
void* hak_tiny_alloc_v4(size_t size) {
int k = hak_tiny_size_to_class(size);
if (unlikely(k < 0)) return NULL;
// Layer 1: TLS-BUMP (hot-class専用、即値化)
if (g_hot_alloc_fn[k]) {
void* p = g_hot_alloc_fn[k]();
if (likely(p)) return p;
}
// Layer 2: 小マガジン128
TinyTLSMag* mag = &g_tls_mags[k];
if (likely(mag->top > 0)) {
return mag->items[--mag->top].ptr;
}
// Layer 3: Slow (全部noinline/cold)
return hak_tiny_alloc_slow_v4(size, k);
}
```
**削減対象**:
- ❌ HAKMEM_TINY_BENCH_FASTPATHSLL系
- ❌ TinyHotMag複数層
- ❌ wrapper context handlingslow pathへ
- ❌ 過剰なTLS変数bcur/bendのみ保持
#### B-2: ACE簡素化4×4状態
**期待効果**: p95安定化、ホットパス干渉除去
**実装内容**:
- ブ4つ: BATCH, HOT_THRESHOLD, drain_mask, slab_lg
- 状態4つ: STEADY, BURST, REMOTE_HEAVY, MEM_TIGHT
- tick=150ms、観測は1/16Kサンプル
- ホットパス完全非干渉
### Phase 4-C: 長期施策2-4週間
#### C-1: Mid/Large TL-Segment
**期待効果**: Mid/Large単スレで2×改善
**実装内容**:
- 4-16KBページ単位のTLバンプ
- ページ内free-list連結生成最小化
- ≥64KBは直map再利用キャッシュLRU 64本
#### C-2: per-core arena + SPSC remote queue
**期待効果**: MT競合削減、2-3×改善
**実装内容**:
- スレッド→core固定
- cross-thread freeはSPSCリング
- allocのついでにdrainバッチ128-256
- レジストリcoreシャード化
### マイルストーン更新
| Phase | Target | Expected Result | vs mimalloc | Status |
|-------|--------|-----------------|-------------|---------|
| Baseline | 16.24 M | 16.24 M | 67% | ✅ Baseline |
| Phase 2+1 | 18.4-18.6 M | **16.53 M** | **68%** | ✅ 完了 (+1.8%) |
| ~~Phase 3~~ | ~~17.5-18.0 M~~ | ~~16.57 M~~ | ~~68%~~ | ❌ 失敗 (+0.24%) |
| **Phase 4-A** | **17.5-18.5 M** | - | **72-76%** | ⏳ **実装中** |
| Phase 4-B | 19.0-20.0 M | - | 78-83% | 📋 設計完了 |
| Phase 4-C | 22-24 M | - | 90-100% | 📐 構想中 |
### 48時間ロードマップ
**Day 1 (今日)**:
1. ✅ ChatGPT Pro相談完了
2. ✅ ドキュメント更新
3. 🔧 Phase 4-A1実装TLS-BUMP即値化
4. 🔧 ビルド & ベンチマーク
**Day 2 (明日)**:
1. 📊 Phase 4-A1結果分析
2. 🔧 Phase 4-A2実装小マガジン128
3. 📐 Phase 4-B詳細設計
4. 🚀 Phase 4-B実装開始判断
### 設計原則
1. **2レジスタ経路死守**: `bcur/bend`だけでalloc完結
2. **層は最小3段**: `TLS-BUMP → 小マガジン → Slow`
3. **ホット/コールド完全分離**: データもコードも64B境界分離
4. **統計はサンプルのみ**: 1/16384、ホットパスに書き込みなし
5. **ヘッダ非更新**: slowで同期、allocはTLSのみ
### 受け入れ基準
- Tiny-Hot 32/64/128B: **mimalloc ≥90-110%**
- Random Mixed: **mimalloc ≥90-105%**p95安定
- Mid/Large単: **≥80-100%**(段階的)
- Mid/Large MT: **×2改善** → 20-30%差まで短縮
- RSS: **予算±10%内**、MEM_TIGHTで守る
---
---
## 🚀 Phase 5: mimalloc 分析 & Direct Page Cache 実装
### Phase 5 Overview
**mimalloc 分析完了 (2025-11-03):**
- 47% Gap の根本原因を特定
- 3つの詳細レポート作成
- Phase 3/4-A1 の教訓を確認: linked-list は最適
**Key Findings:**
1. **Direct Page Cache (O(1))** が最大のボトルネック: +15-20%
2. mimalloc も intrusive linked list を使用Phase 3 の結論は正しい)
3. Gap はマイクロ最適化から来る(データ構造選択ではない)
---
### Phase 5-A: Direct Page Cache 実装
**Goal:** サイズ→ページ lookup を O(log n) から O(1) に
**Current (HAKMEM):**
```c
// Binary search through size classes - O(log n)
int class_idx = hak_tiny_size_to_class(size); // 3-5 comparisons
TinySlab* slab = g_tiny_pool.free_slabs[class_idx];
```
**Target (mimalloc-style):**
```c
// Direct array index - O(1)
TinySlab* slab = g_tiny_pool.slabs_direct[size >> 3]; // 1 cycle!
```
**Implementation Plan:**
**5-A-1. データ構造拡張**
```c
// core/hakmem_tiny.h
typedef struct {
TinySlab* free_slabs[TINY_NUM_CLASSES]; // Existing
TinySlab* full_slabs[TINY_NUM_CLASSES]; // Existing
TinySlab* slabs_direct[129]; // NEW: Direct cache (8-1024B)
// ... existing fields
} TinyPool;
```
**5-A-2. Direct cache の更新**
```c
// Slab allocation時に direct cache を populate
static TinySlab* allocate_new_slab(int class_idx) {
TinySlab* slab = /* ... existing allocation ... */;
// NEW: Populate direct cache for this size class
size_t block_size = g_tiny_class_sizes[class_idx];
for (size_t sz = block_size; sz < block_size + 8 && sz <= 1024; sz++) {
int idx = sz >> 3; // size / 8
if (g_tiny_pool.slabs_direct[idx] == NULL) {
g_tiny_pool.slabs_direct[idx] = slab;
}
}
return slab;
}
// Slab exhaustion時に direct cache をクリア
static void move_to_full_list(int class_idx, TinySlab* slab) {
/* ... existing code ... */
// NEW: Clear direct cache entries pointing to this slab
for (int i = 0; i < 129; i++) {
if (g_tiny_pool.slabs_direct[i] == slab) {
g_tiny_pool.slabs_direct[i] = NULL;
}
}
}
```
**5-A-3. Hot path 書き換え**
```c
void* hak_tiny_alloc(size_t size) {
if (size > 1024 || size == 0) return NULL;
// NEW: O(1) direct cache lookup
int idx = size >> 3; // size / 8
TinySlab* slab = g_tiny_pool.slabs_direct[idx];
if (__builtin_expect(slab != NULL, 1)) {
// Fast path: direct cache hit
void* ptr = pop_from_slab(slab);
if (__builtin_expect(ptr != NULL, 1)) {
return ptr; // ← 5 cycles saved!
}
// Slab exhausted, clear cache and fallback
g_tiny_pool.slabs_direct[idx] = NULL;
}
// Slow path: fallback to existing binary search
int class_idx = hak_tiny_size_to_class(size);
return hak_tiny_alloc_slow(class_idx);
}
```
**Expected Results:**
- **Cycle reduction:** 5 cycles per allocation (binary search elimination)
- **Throughput:** +15-20% (16.53 → 19.0-19.8 M ops/sec)
- **Memory overhead:** 1032 bytes (129 pointers)
- **Risk:** Low (fallback to existing path on miss)
**Timeline:** 1-2 days
---
### Phase 5-B: Dual Free Lists (Next)
**Goal:** Local/Remote free list 分離で atomic ops 削減
**Expected Results:** +10-15% additional
---
### Phase 5-C: Branch Hints + Flags (Next)
**Goal:** Predictable branch + bit-packed flags
**Expected Results:** +5-8% additional
---
## 📊 Phase 5 Roadmap
| Phase | Impact | Effort | Risk | Status |
|-------|--------|--------|------|--------|
| 5-A: Direct Cache | +15-20% | 1-2d | Low | 🔜 Next |
| 5-B: Dual Free Lists | +10-15% | 3-5d | Med | ⏳ Pending |
| 5-C: Branch Hints | +5-8% | 1-2d | Low | ⏳ Pending |
| **Total** | **+45%** | **1-2w** | **Low** | **16.53 → 24.0 M ops/sec** |
---
## 🚀 Phase 6: Learning-Based Tiny Allocator (2025-11-02~)
### 戦略: "Simple Front + Smart Back" (Mid-Large HAKX の真似)
**背景:**
- Phase 5-B 失敗: Magazine unification で -71% 💀
- 包括的ベンチマークで根本原因特定: **Fast path が複雑すぎる**
- Mid-Large HAKX は +171% で成功 → 同じアプローチを Tiny に適用
### 目標
- **Phase 1 (1週間)**: Ultra-Simple Fast Path → System の 70-80% (95-108 M ops/sec)
- **Phase 2 (1週間)**: 学習層追加 → System の 80-90% (108-122 M ops/sec)
- **Phase 3 (1週間)**: メモリ効率最適化 → System 同等速度 + メモリで勝つ 🏆
### 設計コンセプト
#### Front: Ultra-Simple (System tcache 風)
```c
void* hak_tiny_alloc(size_t size) {
int cls = size_to_class_inline(size);
void** head = &g_tls_cache[cls];
void* ptr = *head;
if (ptr) {
*head = *(void**)ptr; // 3-4 命令のみ!
return ptr;
}
return hak_tiny_alloc_slow_adaptive(size, cls);
}
```
#### Back: Smart (学習層)
- **Class Hotness Tracking**: どのサイズが hot/cold か学習
- **動的キャッシュ容量調整**: Hot → 256 slots, Cold → 16 slots
- **Adaptive Refill Count**: Miss rate に応じて 16-128 blocks
### Phase 1: Ultra-Simple Fast Path (進行中)
**実装内容:**
1. `core/hakmem_tiny_simple.c` 新規作成
2. TLS Free List ベースの fast path (3-4 命令)
3. SuperSlab からの refill (既存を流用)
**ファイル:**
- `core/hakmem_tiny_simple.c` - シンプル版 Tiny allocator
- `core/hakmem_tiny_simple.h` - ヘッダ
**ベンチマーク:**
- `bench_tiny_hot` で測定
- 目標: System の 70-80%
### 成功の鍵
1. **Fast path を System tcache と同等に** (3-4 命令)
2. **学習層で差別化** (動的容量調整)
3. **Mid-Large の成功パターンを適用** (+171% の実績)
### 関連ドキュメント
- [`benchmarks/results/TINY_PERFORMANCE_ANALYSIS.md`](benchmarks/results/TINY_PERFORMANCE_ANALYSIS.md) - 根本原因分析
- [`benchmarks/results/BENCHMARK_SUMMARY_2025_11_02.md`](benchmarks/results/BENCHMARK_SUMMARY_2025_11_02.md) - 包括的ベンチマーク結果