Files
hakmem/docs/status/CURRENT_TASK_FULL.md
Moe Charm (CI) 0f071bf2e5 Update CURRENT_TASK with 2025-11-29 critical bug fixes
Summary of completed work:
1. Header Corruption Bug - Root cause fixed in 2 freelist paths
   - box_carve_and_push_with_freelist()
   - tiny_drain_freelist_to_sll_once()
   - Result: 20-thread Larson 0 errors ✓

2. Segmentation Fault Bug - Missing function declaration fixed
   - superslab_allocate() implicit int → pointer corruption
   - Fixed in 2 files with proper includes
   - Result: larson_hakmem stable ✓

Both bugs fully resolved via Task agent investigation
+ Claude Code ultrathink analysis.

Updated files:
- docs/status/CURRENT_TASK_FULL.md (detailed analysis)
- docs/status/CURRENT_TASK.md (executive summary)

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-29 06:29:02 +09:00

84 KiB
Raw Blame History

Current Task (2025-11-29)

COMPLETED: Header Corruption & Segfault Root Cause Fixes (2025-11-29)

背景

Claude Code (ultrathink) + Task agent による2つの critical bug の根治完了。


🐛 Bug 1: Class 1 Header Corruption

症状:

[TLS_SLL_HDR_RESET] cls=1 base=0x... got=0x00 expect=0xa1 count=0

ChatGPT Phase 1/2 対処:

  • TLS SLL pop 時に header を書き直す対処療法を実装
  • 結果: エラー頻度は減ったが根治せず

Root Cause Analysis (Task agent):

  • freelist から block を pop → TLS SLL に push する2つのパスで header 未復元
  • Freelist blocks には stale data (0x00) が残っている
  • Header validation 時に corruption として検出

修正箇所:

  1. core/box/carve_push_box.c:193-198 - box_carve_and_push_with_freelist()

    • Freelist pop 後、TLS SLL push 前に header restoration 追加
    • Commit: 3c6c76cb1
  2. core/hakmem_tiny_free.inc:74-79 - tiny_drain_freelist_to_sll_once()

    • Dead code path (HAKMEM_TINY_DRAIN_TO_SLL で activate)
    • 同様に header restoration 追加
    • Commit: a94344c1a

Fix Pattern:

// CRITICAL FIX: Restore header BEFORE pushing to TLS SLL
#if HAKMEM_TINY_HEADER_CLASSIDX
*(uint8_t*)p = HEADER_MAGIC | (class_idx & HEADER_CLASS_MASK);
#endif

Results:

  • 20-thread Larson test: Header corruption 完全消滅 (0 errors)
  • 4-thread test: 残り1エラーも解消 (drain path 修正後)
  • 全 freelist → TLS SLL paths で header restoration 完了

🐛 Bug 2: larson_hakmem Segmentation Fault

症状:

$ ./larson_hakmem 10 8 128 1024 1 12345 4
Segmentation fault (core dumped)

Root Cause Analysis (Task agent via gdb + coredump):

Location: core/superslab_head.c:87 in expand_superslab_head()

SuperSlab* new_chunk = superslab_allocate(head->class_idx);

The Problem:

  1. superslab_allocate() の declaration が欠如
  2. C99 標準により implicit int return type を仮定
  3. 実際は SuperSlab* (64-bit pointer) を返す
  4. Compiler は 32-bit int として扱い、pointer corruption 発生

Corruption Mechanism:

1. superslab_allocate() returns 0x00005555eba00000 in %rax
2. Compiler expects int, reads only %eax: 0xeba00000
3. Assembly: movslq %eax,%rbp  (sign-extend to 64-bit)
4. Bit 31 is set (0xeba00000 > 0x7fffffff) → sign extension
5. Result: 0xffffffffeba00000 (invalid pointer)
6. Dereference → SEGMENTATION FAULT

修正箇所 (Commit: 6d40dc741):

  1. core/hakmem_tiny_superslab_internal.h:11

    • #include "box/ss_allocation_box.h" 追加
    • superslab_head.c が transitive に include
  2. core/hakmem_super_registry.c:3

    • #include "box/ss_allocation_box.h" 追加
    • 同様の implicit declaration を修正

Warnings Eliminated:

✅ "implicit declaration of function 'superslab_allocate'"
✅ "type of 'superslab_allocate' does not match original declaration"
✅ "code may be misoptimized unless '-fno-strict-aliasing' is used"

Results:

  • larson_hakmem が安定動作 (segfault 消滅)
  • 2/4 threads 全テスト通過
  • 複数 run で stability 確認

Impact:

  • Severity: CRITICAL (affects all SuperSlab expansion)
  • Frequency: Intermittent (~50% - depends on bit 31 of returned pointer)
  • Scope: All multi-threaded workloads using SuperSlab

🎯 成功要因

  1. 対処療法 vs 根治の見極め

    • ChatGPT の Phase 1/2 を「対処療法」と正しく評価
    • Root cause (freelist header restoration) を追求
  2. Task Agent の戦略的活用

    • Header corruption: 全 freelist paths を網羅的探索
    • Segfault: Assembly レベルの詳細解析
  3. 段階的アプローチ

    • 調査 → 修正 → テスト → Commit
    • 各段階で完全性を追求
  4. 詳細な記録

    • 3つの commit message に root cause + mechanism を完全記録
    • 将来の参考資料として価値あり

📝 Commits

a94344c1a Fix: Restore headers in tiny_drain_freelist_to_sll_once()
3c6c76cb1 Fix: Restore headers in box_carve_and_push_with_freelist()
6d40dc741 Fix: Add missing superslab_allocate() declaration

🔜 Next Steps

Both critical bugs are now fully resolved. The allocator is stable for:

  • Multi-threaded Larson benchmarks
  • Header validation enabled
  • SuperSlab expansion under load

Recommend continuing with performance optimization work.


🔴 Phase 6-2.4: SuperSlab Guess Loop SEGV Fix (ARCHIVED - 2025-11-07)

問題

bench_random_mixed_hakmembench_mid_large_mt_hakmem が即座に SEGV

再現:

./bench_random_mixed_hakmem 50000 2048 1234567  # → Exit 139 (SEGV)
./bench_mid_large_mt_hakmem 2 10000 512 42       # → Exit 139 (SEGV)

根本原因Ultrathink Task完全解析

Location: core/box/hak_free_api.inc.h:92-95 (guess loop)

The Bug:

for (int lg=21; lg>=20; lg--) {
    SuperSlab* guess=(SuperSlab*)((uintptr_t)ptr & ~mask);
    if (guess && guess->magic==SUPERSLAB_MAGIC) {  // ← SEGV
        // guess が unmapped memory → dereference で SEGV
    }
}

Why SEGV:

  1. Registry lookup 失敗alloc が SuperSlab 以外から)
  2. Guess loop で 1MB/2MB align した guess を計算
  3. メモリマップ検証なし
  4. guess->magic で unmapped memory dereference → SEGV

Why Benchmark Differences:

  • Larson ( works): All from SuperSlab → registry hit → guess loop スキップ
  • random_mixed ( SEGV): Diverse sizes → non-SuperSlab allocs → guess loop → SEGV
  • mid_large_mt ( SEGV): Large allocs → non-SuperSlab → guess loop → SEGV

Why LD_PRELOAD Works:

  • hak_core_init.inc.h:119-121 で SuperSlab をデフォルト無効化
  • → SS-first path スキップ → guess loop 回避 → SEGV なし

修正試行1: Guess Loop 削除

Applied: core/box/hak_free_api.inc.h:92-95 削除

Result: Still SEGV別の箇所に問題あり

次のアクション

  • SEGV の新しい場所を特定gdb/ASan
  • Registry lookup が失敗する根本原因を調査
  • Complete report: SEGV_ROOT_CAUSE_COMPLETE.md

🐛 新しい観測 (2025-11-07 19:10)

  • HAKMEM_TINY_USE_SUPERSLAB=1 HAKMEM_TINY_MEM_DIET=0bench_random_mixed_hakmem を gdb 監視。class 7 の TinySlabMeta (meta@0x7ffff6a00060) にハードウェアウォッチを張ったところ、sll_refill_batch_from_ss 内で meta->freelist0x00000000000000e2 に化ける瞬間を捕捉。
  • 同じ SuperSlab 上の実ブロック (例: 0x7ffff6a3ec00) の先頭ワードにウォッチを追加すると、hak_tiny_free_superslabtrc_splice_to_sll までは正しい next ポインタが書かれているが、その後ユーザープログラム (bench_random_mixed.cslot[idx][0] = ... を書く地点) で 1byte 書き込みが入り、先頭ワードが 0xe2 に上書きされる。
  • つまり「まだアプリに貸し出しているブロック」が freelist に再露出しており、sll_refill_batch_from_ss*(void**)node を読んだ瞬間に利用者データをポインタとして扱って SEGV になっている。
  • 該当 run では direct freelist push (hak_tiny_free_superslab) で *(void**)ptr = prev が実行されているログも取れているため、別経路TLS spill / bg spill / remote drainで stale head が復活している可能性が高い。
  • Fail-Fast instrumentationtrc_pop_from_freelist() に SuperSlab 範囲チェックを追加)を入れたところ、[TRC_FAILFAST] stage=freelist_next cls=7 node=0x7eab7b20fc53 base=0x7eab7b200000 limit=0x7eab7b400000 で即座に abort。meta->freelist からポップしたノードの「次ポインタ」が SuperSlab 範囲外(= ユーザー書き換え)であることが確認できた。
  • HAKMEM_TINY_REFILL_FAILFAST=2tiny_failfast_log()hak_tiny_free_superslab / TLS spill / BG spill / remote drain の各箇所に挿入。ログを見ると すべての問題ノードが stage=free_local_box= 同一スレッド freeで登録 されており、node=0x7e37d560fc53 のように 64B 非整列のアドレスが freelist に入っている ことがわかった。
  • HAKMEM_TINY_BG_SPILL=0 / HAKMEM_TINY_TLS_LIST=0 / HAKMEM_TINY_FAST_CAP=0 などの箱単位 A/B を実施しても Fail-Fast は継続。クラス 7 だけでなくクラス 6 でも node=0x15 のように壊れた値が freelist に現れるため、原因は spill/remote ではなく「同一スレッド free に渡ってくる ptr 自体が壊れている(= ユーザが持っているポインタが既にズレている)」ラインが濃厚。
  • tiny_free_local_box() に SuperSlab/アライン検証を噛ませ、さらに tiny_debug_track_alloc_ret() でも配布直後のポインタを検証した結果、割り当て段階で既に壊れたポインタを返している ことが確定。
    • 例: [TRC_FAILFAST_PTR] stage=alloc_ret_range cls=7 slab_idx=0 ptr=0x7ffff6a0fc00 reason=out_of_capacity base=0x7ffff6a00000 limit=0x7ffff6c00000 cap=63 used=63 offset=64512= capacity 個目のブロック)。
    • 例: [TRC_FAILFAST_PTR] stage=alloc_ret_align cls=7 slab_idx=0 ptr=0x7ffff6a0f835 reason=misaligned ... cap=63 used=62 offset=635411024B 未満の端数 709 を含む異常アドレス)。
      meta->usedmeta->capacity の境界処理、または slab_idx==0 のヘッダ調整あたりで off-by-one / 加算漏れが起きており、存在しないブロックを線形 carve で組み立てている疑いが濃厚。

次のアクション

  1. sll_refill_batch_from_ss に Fail-Fast を追加し、meta->freelist / *(void**)node が SuperSlab 範囲・アラインメント外だった場合に即座にログアボートclass, slab_idx, node, next, remote_heads も記録)。
  2. hak_tiny_free_superslab / tls_list_spill_excess / bg_spill_drain_class など meta->freelist = node を行う箇所で、prev が当該 SuperSlab 範囲かどうかをチェックするワンショットログを差し込み、どの経路で stale pointer が混入しているか切り分ける。
  3. BG系ENVは2025-12 cleanupで廃止常時OFF固定。計測A/BはTLS_LISTやFAST_CAPなど現存ブのみで実施する。

📊 ベンチマーク行列サマリ (2025-11-07)

実施: Larson + Suiterandom_mixed / mid_large_mt / vm_mixed / tiny_hotを system / mimalloc / HAKMEM で横並び計測し、CSV保存。

  • 保存先Larson: bench_results/larson/20251107_131427/results.csv
  • 保存先Suite : bench_results/suite/<timestamp>/results.csv

要点(この環境)

  • Larson 4T: HAKMEM ≒ system ≒ mimalloc≈3.35M ops/s→ 上限到達(勝ち)
  • Larson 1T: HAKMEM は差 ≈911%3.03M vs 3.35M)→ 詰めれば勝ち筋
  • random_mixed161024B: HAKMEM ≪ system/mimalloc例: 5.9M vs 5356M→ 大差(要対策)
  • mid_large_mt832KiB, MT: HAKMEM ≪ system/mimalloc1.05M vs 8.89.0M)→ 大差(要対策)
  • vm_mixed512KB<2MB: HAKMEM ≪ system~0.137M)→ 大差(要対策)
  • tiny_hot32B/64B: HAKMEM 8085M vs system/mimalloc ~181186M → 1/2水準要対策

ログとスクリプト

  • Larson 行列実行: scripts/bench_larson_matrix.shCSV + raw 保存)
  • Suite 一括実行: scripts/bench_suite_matrix.shCSV + raw 保存)

🏁「全部勝つ」プラン(優先度順の打ち手)

Phase A即効A/B、1日

  • Larson 1T911%差の解消)

    • 特化分岐 ON: HAKMEM_TINY_SPECIALIZE_MASK=0x0F8/16/32/64Bで branch 減Box 5
    • adopt=OFF1T、FAST_CAP=16/32 A/B、PGOtiny_hot/larsonで最短パス強化
    • 期待: 3.03M → 3.103.18Msystem 3.35M に接近)
  • tiny_hot80M → 120M+ を目標)

    • Strict Front/branchless pop の微最適化Box 5 内だけ、境界不変)
    • SLL cap/REFILL のホット帯 A/BREFILL_COUNT_HOT=48/64, FAST_CAP=16/32
    • 期待: +3040%(単体ベンチでの指標)

Phase B中規模、23日

  • random_mixed5.9M → 3040M を目標)

    • TLS/SLL ヒット率向上FrontGate Box の早期 return、MAG→SLL 経路の分岐簡素化)
    • free 経路の境界コスト削減Box 2/3 内で副作用封じ、Box 4 で一括処理)
    • 特化 + PGO の組み合わせを sweepスクリプト化
  • mid_large_mt1.05M → 68M を目標)

    • L2.5/Large のバッチ/Flush/Harvest のチューニング(箱内のみ、境界不変)
    • Backpressurebg remote / flush 閾値)を MT に合わせて最適化
    • 大サイズ再利用の BigCache/L25 ゲートを A/BHAKMEM_BIGCACHE_*
  • vm_mixed~0.137M → 12M を目標)

    • 512KB<2MB 帯の再利用強化BigCacheL25 方向)
    • mmap/munmap 頻度低減のためのバッチ化・しきい値調整(箱内)

Phase C検証と固定化

  • 各ベンチで 510 回の連続実行 → 中央値を CSV 追記、グラフ化
  • 勝ち構成を bench_results_archive/ に保存、ENV プリセット化profiles/*.env

TODO実行リスト当面のアクション

  • Larson 1T: SPECIALIZE_MASK/FAST_CAP/PGO の A/B を実施し CSV 追記
  • tiny_hot: Strict Front + REFILL/HOT の sweep32/64B
  • random_mixed: FrontGate Box の早期 return A/B、free 境界軽量化 A/B
  • mid_large_mt: L25/BigCache の閾値・バッチ・bg_remote A/B
  • vm_mixed: L2.5 帯の再利用ゲート/バッチ化 A/B
  • スイート行列scripts/bench_suite_matrix.shの繰返し回数を増やし中央値取得

Phase 6-2.4: SuperSlab SEGV fix2025-11-07

現象(修正前)

  • Tiny alloc は成功するが、free 時に SuperSlab を見つけられず magic=0 → invalid pointer → SEGV。
  • Direct-link と LD_PRELOAD で挙動が異なり、前者は g_invalid_free_mode=1 により skip→リーク→崩壊、後者は libc へフォールバックで一見動作(実際はリーク)。

修正内容

  • core/box/hak_free_api.inc.h
    • Guess ループ(未マップ領域への生読み取り)を削除。
    • Header 参照前に hak_is_memory_readable()mincore ベース、fallback のみで使用)で可読性を確認。
  • core/hakmem_internal.h
    • hak_is_memory_readable(void*) を追加mincore 1byte で可読チェック)。
  • レジストリの同期も正規化済Phase 6-2.3 補強):SuperRegEntry.base を atomic 化、acq/rel 統一。

検証(直近実測)

  • random_mixedcycles=200k, ws=4096: 2.84M ops/s修正前: SEGV
  • random_mixedcycles=400k, ws=8192: 2.92M ops/s修正前: SEGV
  • mid_large_mtthreads=4, cycles=40k, ws=2048: 2.00M ops/s修正前: SEGV
  • Larson 4T: 0.838M ops/s → 0.838M ops/s変化なし、安定

備考

  • mincore は fallback 経路のみで使用され、ホットパスに入らないため性能影響は無視できる水準。
  • 追加のデバッグノブ:HAKMEM_SUPER_REG_DEBUG=1register/unregister 一発)/ HAKMEM_SUPER_REG_REQTRACE=1invalid-magic 時に 1MB/2MB base+magic を一発)

🔍 SuperSlab registry デバッグ進捗 (2025-11-07)

  • SuperRegEntry.base_Atomic uintptr_t 化し、登録/解除/lookup で acquire/release を正規化。
  • 追加ノブ:
    • HAKMEM_SUPER_REG_DEBUG=1 → register/unregister を1行ログ出力例: [SUPER_REG] register base=...)。
    • HAKMEM_SUPER_REG_REQTRACE=1 → invalid-magic 時に 1MB/2MB の base/magic を一発表示。

現状観測

  • bench_random_mixed_hakmem / bench_mid_large_mt_hakmem は短ランでもセグフォ再現。stderr 冒頭は初期化ログと大量の [SUPER_REG] register ... のみで、unregister は未視認。
  • HAKMEM_SUPER_REG_REQTRACE=1 を ON にした直リンク短ランでは、現段階で [SUPER_REG_REQTRACE] ... 行は出ず(= header 経由の invalid-magic 発火前に崩れている)。
  • Asan PRELOAD (LD_PRELOAD=libasan.so:libhakmem_asan.so) で system 版を実行し stack/ログを /tmp/asan_{rand,midmt,vm}_sys.* に保存済み。次は stack 抽出と CURRENT_TASK への貼付を予定。

次ステップ

  1. 直リンク短ラン + HAKMEM_SUPER_REG_REQTRACE=1 + SIGUSR2Tiny Debug Ringの組合せで、hak_super_lookup 前後の順序を突き止める。
  2. /tmp/asan_* ログから [SUPER_REG] register の時系列と Asan stack を抽出し、free→lookup→unregister の競合がないか記録。
  3. 必要に応じて hak_tiny_free の入口に Fail-FastSLL上限/SS範囲アサートを追加し、異常を早期に顕在化させる。

🚨 SuperSlab ON での直リンク random_mixed 再現 (2025-11-07)

再現手順(直リンク・短ラン)

env HAKMEM_TINY_USE_SUPERSLAB=1 HAKMEM_TINY_MEM_DIET=0 \
  ./bench_random_mixed_hakmem 200000 4096 1234567

結果: SEGVEXIT 139。stderr 先頭には [ELO]/[Batch]/[ACE] と大量の [SUPER_REG] register ... が出力されるが、unregister は未視認。

Asan PRELOAD での stack 採取system 版)

make -j asan-shared-alloc
env HAKMEM_TINY_USE_SUPERSLAB=1 HAKMEM_TINY_MEM_DIET=0 \
  LD_PRELOAD="$(gcc -print-file-name=libasan.so):$PWD/libhakmem_asan.so" \
  ASAN_OPTIONS="halt_on_error=1:abort_on_error=1:alloc_dealloc_mismatch=1:detect_leaks=0:fast_unwind_on_malloc=0" \
  ./bench_random_mixed_system 200000 4096 1234567 2> /tmp/asan_rand_ss.err

期待: invalid free/lookup 近傍のバックトレース + [SUPER_REG] の登録ログを取得。

reqtrace + リング強制SEGV前の痕跡

env HAKMEM_TINY_USE_SUPERSLAB=1 HAKMEM_TINY_MEM_DIET=0 \
  HAKMEM_SUPER_REG_REQTRACE=1 HAKMEM_SUPER_REG_DEBUG=1 \
  ./bench_random_mixed_hakmem 50000 2048 1234567 2> /tmp/rand_reqtrace_ss.err & pid=$!; \
  sleep 1; kill -USR2 $pid; wait $pid

メモ: 上記のログ抽出・stack 解析後、必要に応じて free 入口の FailFast 追加や、lookup の lg/align 判定の一時バイアスで切り分け予定。

Phase 6-2.3: Active Counter Bug Fix (2025-11-07)

問題発見

HAKMEM 直リンク 4T で free(): invalid pointer クラッシュを発見:

再現手順:

./larson_hakmem 10 8 128 1024 1 12345 4
# → Exit 134: free(): invalid pointer
# → [DEBUG] superslab_refill returned NULL (OOM)

症状:

  • System malloc/mimalloc: 3.3M ops/s
  • HAKMEM 1T: 838K ops/s (-75%) ⚠️
  • HAKMEM 4T: 起動直後にクラッシュ

根本原因Ultrathink Task Agent 調査)

Active Counter Double-Decrement in P0 Batch Refill

core/hakmem_tiny_refill_p0.inc.h:103 で freelist から TLS cache にブロックを移動する際、active counter をインクリメントし忘れ:

1. Free → カウンタ減算 ✅
2. Remote drain → freelist に追加(カウンタ変更なし) ✅
3. P0 batch refill → TLS に移動(カウンタ増加忘れ)❌ ← バグ!
4. 次の Free → カウンタ減算 ❌ ← ダブルデクリメント!

結果:カウンタアンダーフロー → SuperSlab が「満杯」 → OOM → クラッシュ

修正内容

File: core/hakmem_tiny_refill_p0.inc.h:103

 trc_splice_to_sll(class_idx, &chain, &g_tls_sll_head[class_idx], &g_tls_sll_count[class_idx]);
-// NOTE: from_freelist は既に used/active 計上済みのブロックの再循環。
+// FIX: Blocks from freelist were decremented when freed, must increment when allocated
+ss_active_add(tls->ss, from_freelist);

検証結果

設定 修正前 修正後
デフォルト 4T クラッシュ 838K ops/s
安定性2回実行 - 同じスコア

残課題

HAKMEM_TINY_REFILL_COUNT_HOT=64 でクラッシュ再発

HAKMEM_TINY_REFILL_COUNT_HOT=64 ./larson_hakmem 10 8 128 1024 1 12345 4
# → Exit 134: free(): invalid pointer (class=4 で OOM)

暫定診断:

  • Class 0-3 が want=64 で大量 refill → メモリ過剰消費
  • Class 4 がメモリ不足で OOM
  • 原因: TLS cache 過剰蓄積またはメモリリーク?

次のアクション候補:

  1. TLS cache サイズ制限確認(HAKMEM_TINY_FAST_CAP
  2. メモリリーク詳細調査valgrind
  3. デフォルト refill count と Class 0-3 vs 4 の比較

🎉 デバッグ完了 (2025-11-07 - Sanitizer)

結論

  • HAKMEM allocator は ASan / UBSan で健全性を確認済み。メモリ破壊や未定義動作は検出されず、現状の箱境界は安全に動作。

検証ルート(便利ターゲット)

  • make asan-preload-run THREADS=4 — メモリ破壊チェック(安定; 非ASan本体+LD_PRELOADでASan先頭
  • make ubsan-mailbox-run THREADS=4 — Mailbox/Remote 健全性チェック(安定)
  • make asan-preload-mailbox-lite THREADS=4 — 短時間の境界チェック(安定, 5s/CHPT=256, RemoteGuard+Ring

補足(運用ガイド)

  • ASan直リンクの4Tは環境依存で Shadow 予約に失敗するため、当面は PRELOAD 方式を既定とし、Mailbox 有効系は UBSan を用いる。
  • CI/スクリプトは上記ターゲットを用いることで再起動ループの回避と安定検証が可能。

🆘 最優先: 強制終了/再起動ループの緊急対応sanitizer/環境周り)

現象

  • ベンチ実行や補助プロセス実行中に強制終了SIGKILL/abortし、環境全体が再起動ループに入ることがある。
  • ASan直リンクlarson_hakmem_asan{,_alloc}の4T実行で高頻度に ReserveShadowMemoryRange failed (ENOMEM) が発生。
  • Ready/Mailbox ON + ASan ではプロセスが沈黙/強制終了するケースありstderr/outとも0バイト

暫定診断(根本原因候補)

  • AddressSanitizer の Shadow メモリ確保が他領域と衝突し ENOMEM → ランタイムが abort。
  • LD_PRELOAD順序/ASLR/マップ数制限(vm.max_map_count/LTO+最適化との相性で、MT下の Shadow 予約が不安定化。
  • Ready/Mailbox ON でメモリマップ挙動が変化し、ASan の reserve 失敗トリガを踏みやすい。

安全な回避経路(検証済み)

  • ASan: 本体は非ASanlarson_system)、LD_PRELOAD=$(gcc -print-file-name=libasan.so):./libhakmem_asan.so でランタイム先頭ロード。
    • 4T 安定完走Ready/Mailbox OFF: ~3.25M ops/s を確認。
  • UBSan: make ubsan-larson-alloc + Ready/Mailbox ON で 4T 完走(~3.35M ops/s

緊急対応の優先順位FailSafe

  1. 既定の「計測/検証」ルートを sanitizer-safe に切替(当面の安定化)
    • ASanが必要: scripts/run_larson_asan_preload.sh 4Ready/Mailbox OFF
    • Ready/Mailbox ON検証: UBSanに切替 HAKMEM_WRAP_TINY=1 HAKMEM_TINY_SS_ADOPT=1 ./larson_hakmem_ubsan_alloc …
  2. 再起動ループ回避の運用ガード
    • 長時間ASan 4T禁止、まず短時間/小負荷(例: ch/thread 256, sleep 5で段階実行。
    • CI/スクリプトで ASan 直リンク4Tをスキップし、LD_PRELOAD 方式へ統一。
  3. 根治(後追い・環境依存)
    • オプション: ASAN_OPTIONS=quarantine_size_mb=8:malloc_context_size=5 等でメモリ圧縮(効果限定的)。
    • vm.max_map_count 引き上げ、ASLR制御、最適化/LTO無効化要環境合意

当面の実行手順(コピペ可)

# ASanReady/Mailbox OFF, 4T安定
./scripts/run_larson_asan_preload.sh 4

# UBSanReady/Mailbox ON, 4T安定
make -j ubsan-larson-alloc >/dev/null
HAKMEM_WRAP_TINY=1 HAKMEM_TINY_SS_ADOPT=1 ./larson_hakmem_ubsan_alloc 10 8 128 1024 1 12345 4

チェックリストPR/レビュー時)

  • ASan直リンク4Tを実行していないPRELOAD方式へ誘導
  • Ready/Mailbox ON の4Tは UBSan で計測している。
  • ベンチスクリプトで異常終了時に即時中断しループしない。
  • 影響範囲のログ(ワンショット/リング)を残すが常時多出力は避ける。

🔔 最新アップデート (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):

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

// 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)

  • 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=64A/B: 48/64
  • Pipeline: HAKMEM_TINY_SS_ADOPT=1publish→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

🔧 箱化フェーズ(進行中)

現状2025-11-06 21:xx

  • Phase 1 完了(安全・即効の分離)

    • 退出ダンプ([EXIT DEBUG]/SS会計を箱化
      • core/box/hak_exit_debug.inc.h を導入し、hakmem.c から関数本体を除去
    • KPIユーティリティ/proc, RSS など)を箱化
      • core/box/hak_kpi_util.inc.h を導入し、hak_get_kpi() を移動
    • ビルド・2s/4T スモーク OKThroughput ≈ 4.19M ops/s, 回帰なし)
  • Free/Tiny/SS 導通の暫定診断短ランでOOMを避けつつ

    • 追加カウンタ: g_free_wrapper_calls, g_hak_tiny_free_calls, g_free_ss_enter, g_free_local_box_calls, g_free_remote_box_calls, g_ss_active_dec_calls
    • 短ラン結果: free() は大量に呼ばれているが、Tiny/SS側の free 経路には一度も到達していない(全て 0
    • OOM傾向はこの導通不全が主因。長時間ランは回避し、短ランで追跡継続。

次にやることPhase 2: 中核APIの箱化 + 診断の最小追加)

  1. hak_alloc_at / hak_free_at の箱化(見通し改善・安全に戻せる設計)

    • 新規: core/box/hak_alloc_api.inc.halloc本体
    • 新規: core/box/hak_free_api.inc.hfree本体
    • hakmem.c から本体を除去し、1 行 include へ差し替え
    • 目的: hakmem.c を 500行級へ圧縮し、中央ハブとしての見通しを確保
  2. Free 導通の最小スケスケ(短ラン限定・ワンショット)

    • ENV: HAKMEM_FREE_ROUTE_TRACE=1 で最初の N 件だけ分類ログ
      • super(registry ok/miss) / mid(hit/miss) / l25(hit/miss) / unknown
    • OOMを避けるため 2s/4T のみで実行。Tiny/SSへ届かない原因を最短で特定。
  3. Phase 3 の見積もり(必要時)

    • init/shutdown の箱化(core/box/hak_core_init.inc.h
    • 最終目標: hakmem.c を中央 include ハブ(~400600行に固定

A/B・戻せる設計

  • すべて .inc.h の差し替え1行で段階導入。問題が出たら即時リバート可。

タイムライン(目安)

  • Phase 2: 12時間箱化 + 1時間2s/4T 短ラン×数回)
  • Phase 3: 12時間箱化 + 30分スモーク

🧱 本日の着手L2.5/L2 キャッシュ → ベンチ)

  • 目的: vm/mixed での大サイズ≥512KBの再利用性を引き上げ、mimalloc/system に肉薄/逆転。
  • 仕様(箱理論):
    • BigCache-L25 ゲートA/B
      • env HAKMEM_BIGCACHE_L25=1 で、512KB<2MB のサイズも per-site BigCache を利用。
      • 境界1箇所alloc/free のみ(他経路には侵食しない)。
    • Fail-Fast と戻せる設計env で即時OFF可。
  • 実装:
    • core/hakmem.c に BigCache-L25 ヒット/プット分岐を追加A/Bフラグで制御
    • 既存 BigCache≥2MBは維持しつつ、L2.5 も同一箱を使って簡素化。
  • ベンチ:
    • ハーネス復元(bench_allocators_{hakmem,system})。
    • 4シナリオ × 3アロケータsystem/mimalloc/HAKMEM× 5回の自動化を強化し、CSV保存。
      • シナリオ案: random_mixed(161024B, 1T), mid_large_mt(832KiB, 4T), larson(8128B, 4T), redis-like(161024B, 1T, LD_PRELOAD)
      • 出力: bench_results/auto/<timestamp>/*.csvops/s, 備考列にENV
    • クイックCSV5×4シナリオ×3アロケータbench_results_archive/ に保存。

次アクション:

  1. HAKMEM_BIGCACHE_L25=1 で quick_full_benchmark を再実行し、vm/mixed の改善を確認。
  2. 改善が見られれば、THPゲートとdecommitバッチのA/Bを追加実装箱と境界は現行踏襲

🎯 次の主目標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 のみ)。
  1. ベンチ・スナップショット
  • scripts/bench_capture_now.sh で現構成を保存(日付入り)。
  • scripts/profiles/tinyhot_best.envREFILL_COUNT_HOT/FAST_CAP を追記しベストを固定。
  1. 比較と回帰防止
  • Larson 2s/4s × threads=1/4/8 で hakmem vs mimalloc を測定・記録。
  • Guard/ASan を OFF のまま長時間(>30sを1回通して安定性を確認。
  1. 伸びなければ(次の一手)
  • 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_MISMATCHBT を出力。
  • SlabHandle を全採用経路に統一:所有権取得→slab_drain_remote_full()→ゼロ確認→採用失敗時は release。Box2/Box3 の境界を1箇所へ集約。
  • TLS alloc 前に remote queue を opportunistic drainsuperslab_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箇所
  1. 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 化
  1. 所有権なし 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を箱単位で健全化します。その後、L2.5/L2 BigCache のA/Bを本番ハーネスで収集CSVします。


🚀 後段つよつよ大作戦mimalloc 撃破作戦)

目標Objective

  • 「Larson8128B」と「mid/mixed」代表ワークロードで mimalloc を撃破 or 同等に接近する。
    • 直近ターゲット10秒計測
      • Larson 4T: ≥ 1214M ops/s段階目標、最終mimalloc≒16.7M ops/s に接近
      • Mid/Large MT 4T: systemの80%→100% 到達
      • Random Mixed 1T: 23x 改善PF/sys 抑制で底上げ)

作戦Box Theory に基づく後段強化)

  • Adopt/Ready 優先箱取り出しO(1)
    • Ready Listper-class slab hintを最前段で採用。publish/remote/first-free で push、refill で pop→bind。
    • Ready系ENVは2025-12 cleanupで廃止常時ON固定, budget=1, width=TINY_READY_RING。REG_SCAN_MAXのみ有効。
  • Registry/探索の削減箱
    • per-class registry の窓幅をさらにチューニング64→32→16。firstfit で即帰還。
    • HAKMEM_TINY_REG_SCAN_MAX をマトリクスで最適点探索。
  • Superslab/Mmap Gatemustadoptbeforemmap 強化)
    • adopt×2yield前後+ Ready→Mailbox→Registry の順固定。mmap は最終手段。
    • Gate内で sticky を先行、必要に応じて small ドレイン(所有権必須)。
  • L2.5/L2 BigCacheVM寄り
    • L2.5512KB<2MBも persite BigCache A/BHAKMEM_BIGCACHE_L25=1)。
    • 狭帯域512KB1MBシナリオでヒット率を上げ、PF/sys を可視に低減。

可視化/計測(スクリプト整備済み)

  • CSV出力マトリクスreps=5/10, 10秒ラン
    • Larson triad: benchmarks/scripts/run_larson_matrix.sh 2,10 1,4 REPS
    • Mid/Large MT: benchmarks/scripts/run_mid_large_mt_matrix.sh 1,4 CYCLES WS REPS
    • Random Mixed: benchmarks/scripts/run_random_mixed_matrix.sh CYCLES WS REPS
    • VM MixedL2.5 A/B: benchmarks/scripts/run_vm_mixed_matrix.sh CYCLES WS REPS
    • Redis-likeLD_PRELOAD: benchmarks/scripts/run_redis_matrix.sh THREADS CYCLES OPS REPS
  • perf statPF/dTLB/IPC/branchesを10秒ランに併記Larson/Mid中心

TODO短期ロードマップ

  1. Larson 2s→10s1T/4Tで REG_SCAN_MAX × READY × ADOPT のA/BCSV+perf
  2. Mid/Large MT 10s1T/4Tで採用窓とGate強化の最適点探索CSV+perf
  3. VM Mixed 狭帯域512KB1MBで L25=ON/OFF の差を定量CSV
  4. Redis LD_PRELOAD 安定化Tiny安全モード→段階的拡張
  5. ベスト設定を benchmarks/RESULTS_SNAPSHOT.md に反映、benchmarks/README.md に推奨ENV追記

リスク/フォールバック

  • READY/ADOPT はA/Bガード付きenvで即時切替可。Gate強化も1箇所の境界内で適用/解除する。
  • L2.5 BigCache は狭帯域で先行検証(広帯域ではオーバーヘッド優位になりやすい)。

症状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_idxtiny_mailbox_publish で検証用トレース
    • ss_remote_drain_to_freelist 前後で freelist pointer をトレースし、破損発生位置を特定remote queue 内滞留→再 push の経路切り分け)
    • tiny_remote_sentinel_set() で 0x6261 汚染を一度だけ捕捉し BT + SIGUSR2 を吐くトラップを追加(再現経路の特定用)
    • 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 tputHAKMEM_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.shPF/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_RING2/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):

      // === 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 の核心:

      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 変数:

      // 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 の設計ミス
      // 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 を実装
      // 次回の正しい実装方針
      __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で機能しない
      // 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% のオーバーヘッド が不要

修正方針:

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):

// 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変数アクセス + 分岐

修正方針:

// 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_allocmalloc にインライン
  • hak_tiny_owner_slabfree にインライン

期待効果: ~2% スループット向上


🎨 Phase 2詳細設計: mimalloc-style Fast Owner Check

現在の問題点

現在の hak_tiny_owner_slab() 実装:

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% 削減

実装戦略:

// 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:

// 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

現在のフローチャート:

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):

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_v3malloc (force inline)
  • hak_tiny_freefree (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実装

# 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 - デフォルト設定

コード例:

// 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%

実装内容:

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):

// 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):

// Direct array index - O(1)
TinySlab* slab = g_tiny_pool.slabs_direct[size >> 3];  // 1 cycle!

Implementation Plan:

5-A-1. データ構造拡張

// 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 の更新

// 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 書き換え

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 風)

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% の実績)

関連ドキュメント