pool v1 flatten: break down free fallback causes and normalize mid_desc keys
This commit is contained in:
255
CURRENT_TASK.md
255
CURRENT_TASK.md
@ -1,5 +1,125 @@
|
||||
## HAKMEM 状況メモ (2025-12-05 更新 / C7 Warm/TLS Bind 反映)
|
||||
|
||||
### Phase80: mid/smallmid Pool v1 flatten(C6-heavy)
|
||||
- 目的: mid/smallmid の pool v1 ホットパスを薄くし、C6-heavy ベンチで +5〜10% 程度の底上げを狙う。
|
||||
- 実装: `core/hakmem_pool.c` に v1 専用のフラット化経路(`hak_pool_try_alloc_v1_flat` / `hak_pool_free_v1_flat`)を追加し、TLS ring/lo hit 時は即 return・その他は従来の `_v1_impl` へフォールバックする Box に分離。ENV `HAKMEM_POOL_V1_FLATTEN_ENABLED`(デフォルト0)と `HAKMEM_POOL_V1_FLATTEN_STATS` でオンオフと統計を制御。
|
||||
- A/B(C6-heavy, ws=400, iters=1M, `HAKMEM_BENCH_MIN_SIZE=257` / `MAX_SIZE=768`, `POOL_V2_ENABLED=0`, Tiny/Small v2/v3 は従来どおり):
|
||||
- flatten OFF (`POOL_V1_FLATTEN_ENABLED=0`): Throughput ≈ **23.12M ops/s**、`[POOL_V1_FLAT] alloc_tls_hit=0 alloc_fb=0 free_tls_hit=0 free_fb=0`。
|
||||
- flatten ON (`POOL_V1_FLATTEN_ENABLED=1`): Throughput ≈ **25.50M ops/s**(約 +10%)、`alloc_tls_hit=499,870 alloc_fb=230 free_tls_hit=460,450 free_fb=39,649`。
|
||||
- 所感: 自スレッド TLS fast path を太らせるだけで目標どおり +10% 程度の改善が得られた。まだ free_fb がそこそこ残っているため、次に詰めるなら page_of / 自スレ判定の精度を上げて free_fb を削るフェーズ(Pool v1 flatten Phase2)を検討する。運用デフォルトは引き続き `POOL_V1_FLATTEN_ENABLED=0`(安全側)とし、bench/実験時のみ opt-in。
|
||||
|
||||
### Phase81: Pool v1 flatten Phase2(free_fb 内訳の可視化)
|
||||
- 変更: flatten stats に free fallback の理由別カウンタを追加(page_null / not_mine / other)。`hak_pool_free_v1_flat` で mid_desc 取得失敗 → page_null、owner 不一致等 → not_mine、その他 → other として集計。
|
||||
- ベンチ(C6-heavy, 1M/400, Release, Tiny/Pool v2 OFF, small v3 OFF, `POOL_V1_FLATTEN_ENABLED=1`):
|
||||
- flatten OFF: **23.68M ops/s**(stats 0)。
|
||||
- flatten ON : **25.90M ops/s**(約 +9.4%)、`alloc_tls_hit=499,870 alloc_fb=230 free_tls_hit=460,060 free_fb=40,039 page_null=40,039 not_mine=0 other=0`。
|
||||
- 所感: free fallback はほぼすべて mid_desc 取得失敗(page_null)によるもの。owner mismatch は 0。次は page_of/mid_desc 判定を精度アップさせ、free_fb をさらに削る余地がある。デフォルトは引き続き flatten OFF(安全側)。
|
||||
|
||||
### Phase82: mid_desc マスク整合(free_fb 削減の第一歩)
|
||||
- 変更: `mid_desc_register/lookup/adopt` が扱うページアドレスを `POOL_PAGE_SIZE` で正規化し、mmap の 64KiB 非アラインでも lookup が一致するように修正。flatten stats は page_null/not_mine/other もダンプするよう拡張済み。
|
||||
- ベンチ(C6-heavy, 1M/400, Release, tiny/pool v2 OFF, LEGACY tiny, flatten ON):
|
||||
- flatten OFF: **23.68M ops/s**(参考)。
|
||||
- flatten ON : **26.70M ops/s**(約 +13% vs OFF)、`alloc_tls_hit=499,871 alloc_fb=229 free_tls_hit=489,147 free_fb=10,952 page_null=3,476 not_mine=7,476 other=0`。
|
||||
- 所感: page_null が大幅減少、not_mine が顕在化。owner 判定を少し緩め/精度アップする余地はあるが、デフォルトは引き続き flatten OFF(実験のみ ON)。
|
||||
|
||||
### PhaseA/B (SmallObject HotBox v3 C7-only 通電)
|
||||
- 追加: `HAKMEM_SMALL_HEAP_V3_ENABLED/CLASSES` gate、route `TINY_ROUTE_SMALL_HEAP_V3`、front の v3 経路(fallback 付き)。
|
||||
- 型/IF: `core/box/smallobject_hotbox_v3_box.h` に so_page/class/ctx+stats、TLS 初期化を実装。`smallobject_cold_iface_v1.h` で v1 Tiny への Cold IF ラッパ(C7 専用)を用意。
|
||||
- Hot path: `core/smallobject_hotbox_v3.c` で so_alloc/so_free を実装(current/partial freelist を持つ)。C7 で refill は Tiny v1 からページを借り、freelist を v3 で carve。retire 時に v1 へ返却。fail 時は v1 にフォールバック。
|
||||
- デフォルト: ENV 未指定時は C7-only で v3 ON(`HAKMEM_SMALL_HEAP_V3_ENABLED` 未設定かつ CLASSES 未設定で class7 に v3 を適用)。`HAKMEM_SMALL_HEAP_V3_ENABLED=0` または CLASSES から bit7 を外せばいつでも v1 経路に戻せるようにしている。
|
||||
|
||||
### Phase65-c7-v3-HEAP_STATS(C7-only v3 A/B 追加確認)
|
||||
- 短尺 20k/ws=64:
|
||||
- v3 OFF: **40.91M ops/s**, `HEAP_STATS[7] fast=11015 slow=1`。
|
||||
- v3 ON (`CLASSES=0x80`): **56.43M ops/s**、`SMALL_HEAP_V3_STATS` で `alloc_refill=49 fb_v1=0 page_of_fail=0`(短尺ウォームアップ由来の refill)。segv/assert なし。
|
||||
- 長尺 1M/ws=400:
|
||||
- v3 OFF: **38.29M ops/s**, `HEAP_STATS[7] fast=550099 slow=1`。
|
||||
- v3 ON: **50.25M ops/s**、`alloc_refill=5077 fb_v1=0 page_of_fail=0`。slow は v1 と同等レンジ。
|
||||
- Mixed 16–1024B 1M/ws=400(参考):
|
||||
- v3 OFF: **42.35M ops/s**, `HEAP_STATS[7] fast=283169 slow=1`。
|
||||
- v3 ON: **49.60M ops/s**, `alloc_refill=2446 fb_v1=0 page_of_fail=0`。
|
||||
- 結論: HEAP_STATS 的にも slow≈1 を維持しつつ、v3 ON は C7-only/Mixed で大きくプラス。デフォルトでは C7-only v3 を ON(ENV 未指定で ENABLED=1, CLASSES=0x80 相当)としつつ、混乱や回帰時に備えて `SMALL_HEAP_V3_ENABLED=0` / クラスマスクでいつでも v1 に戻せるようにしている。***
|
||||
|
||||
### Phase63: C6 v2 A/B(bench専用マスクでの初回計測)
|
||||
- C6-heavy (min=257/max=768, ws=400, iters=1M, PROFILE=C7_SAFE, C7_HOT=1, v2 stats ON)
|
||||
- v2 OFF (`HOTHEAP_V2=0`): **42.15M ops/s**, HEAP_STATS[7] fast=283169 slow=1。
|
||||
- v2 ON (`HOTHEAP_V2=1`, classes=0x40): **29.69M ops/s**(大幅回帰)。HEAP_STATS[6] fast=266914 slow=16。v2 stats cls6 route_hits=0 / free_fb_v1=266930 で実質 v1 経路に落ちており、v2 専用処理が活きていない。
|
||||
- 所感: C6 v2 を有効にすると現状大きく劣化。C6 マスクは研究用のまま(本線は C7 v1/v2 のみ)。
|
||||
- Mixed 16–1024B (ws=400, iters=1M, PROFILE=C7_SAFE, C7 v2 ON, v2 stats ON)
|
||||
- C7 v2 only (`classes=0x80`): **45.07M ops/s**、HEAP_STATS[7] fast=283170 slow=2276、v2 alloc_lease/refill=2276。
|
||||
- C6+C7 v2 (`classes=0xC0`): **35.65M ops/s**(大幅回帰)。HEAP_STATS[6] fast=137306 slow=1、cls7 slow=2276。v2 stats cls6 route_hits=0(C6 依然 v1)、cls7 refills=2276。
|
||||
- 所感: C7 v2 は long-run で安定していたが、今回の 1M/400 では refill が 2,276 件まで増えスルーが低下。C6 を v2 に乗せる構成は混在でも回帰が大きく、当面研究箱のままに固定。
|
||||
|
||||
### Phase64: C6 v2 route 修正 / C7 v2 refill 再トリアージ
|
||||
- front の route switch を汎用化し、class6 でも `TINY_ROUTE_HOTHEAP_V2` を直接呼ぶよう修正。v2 stats に route 値を追加。
|
||||
- C6-heavy v2 ON (classes=0x40, ws=400, iters=100k): route_hits=26660, alloc_refill=1, fallback_v1=0、throughput **35.52M ops/s**(v1よりは低めだが v2 パスが有効に)。
|
||||
- C7-only 20k/ws=64 v2 ON: HEAP_STATS[7] slow=48、v2 alloc_refill=48(v2 OFF は slow=1)。Mixed 20k/ws=256 v2 ON でも alloc_refill=42。短尺では refill 多発が残っており原因再調査中。
|
||||
- Next: C7-only 長尺でも slow/refill が増えているかを再確認し、refill_slow/partial ポリシーを見直す。C6 v2 は route/gate が通るようになったので、性能 A/B を改めて計測(未実施)。デフォルトは引き続き v2 OFF。
|
||||
|
||||
### Phase71: PoolHotBox v2 初回 A/B(Cold IF=v1)→ 失敗
|
||||
- C6-heavy (min=2048/max=8192, ws=400, iters=1M, PROFILE=C7_SAFE, Tiny v2 OFF)
|
||||
- v2 OFF (`POOL_V2_ENABLED=0`): **30.57M ops/s**。
|
||||
- v2 ON (`POOL_V2_ENABLED=1 POOL_V2_CLASSES=0x7F/0x1`): 実行直後に **SIGABRT**(gdb で `hak_pool_free_v2_impl` → `pool_hotbox_v2_page_of` の fail-fast で abort)。10k 短尺でも同様に abort し、`POOL_V2_STATS` は出力されず。
|
||||
- 所感: v2 alloc→v1 free の混線か page_of 範囲判定で落ちており、構造A/B がまだ通らない。デフォルトは v1 のまま維持。次ステップでは route/gate と page_of 整合を確認し、free_fb_v1 が跳ねない形に直してから再A/B する。
|
||||
|
||||
### Phase65/66: v2 refill 可視化(C7短尺)と C6 v2 調査の前段
|
||||
- C7-only (ws=64, iters=20k, v2 ON, stats ON): HEAP_STATS[7] fast=11016 slow=48。新設の refill stats で `refill_with_current=0 / refill_with_partial=0` → current/partial が空の状態で 48 回 refill 発生していることを確認(retire 0)。
|
||||
- v2 OFF 同条件: slow=1(baseline)。短尺の refill 多発は依然再現するが、current/partial を失ってから refill しているパターンに絞れた。
|
||||
- C6 v2 の性能トリアージは未着手(route は修正済み)。次ステップで C6-heavy / Mixed A/B を再取得し、route_hits>0 でのスループット/slow を確認する。
|
||||
- Phase65 後半(長尺本命プロファイル)
|
||||
- C7-only 1M/ws=400: v2 OFF **38.24M ops/s**, v2 ON **38.68M ops/s**(HEAP_STATS[7] fast=550099 slow=1、refill=1)。
|
||||
- Mixed 16–1024B 1M/ws=400: v2 OFF **41.78M ops/s**, v2 ON **41.94M ops/s**(HEAP_STATS[7] fast=283169 slow=1)。refill は 1 件に収束し、fail/fallback なし。
|
||||
- 結論: 短尺の refill≒50 はウォームアップ由来。本命プロファイルでは v2 ON/OFF で slow≈1 に張り付き、性能も ±5% 以内(むしろ微プラス)。
|
||||
- 運用方針: デフォルト構成では `HAKMEM_TINY_HOTHEAP_V2=0` を維持しつつ、C7-only の bench/pro プロファイルでは `HAKMEM_TINY_HOTHEAP_V2=1 HAKMEM_TINY_HOTHEAP_CLASSES=0x80` を opt-in 推奨とする(Mixed/本番では明示しない限り OFF)。
|
||||
|
||||
### Phase59: C7 HotHeap v2 Mixed A/B(C7-only 研究箱の現状)
|
||||
- Mixed 16–1024B(ws=256, iters=20k, `PROFILE=C7_SAFE`, v2 C7-only, v2 stats ON)で v2 ON/OFF を比較:
|
||||
- v2 OFF (`HAKMEM_TINY_HOTHEAP_V2=0`): **45.11M ops/s**, HEAP_STATS[7] fast=5691 slow=1。
|
||||
- v2 ON (`HAKMEM_TINY_HOTHEAP_V2=1`, classes=0x80): **46.21M ops/s**(約 +2.4%)、HEAP_STATS[7] fast=5692 slow=45。
|
||||
- HOTHEAP_V2_C7_STATS: route_hits=5692, alloc_fast=5692, alloc_lease/refill=45, cold_refill_fail=0, page_retired=4, fallback_v1=0。
|
||||
- C7_PAGE_STATS: prepare_calls=45 → refill が多く current/partial を握り切れていない。
|
||||
- 方針: v2 は Mixed でも微プラスだが slow_prepare が増えている。refill 後のページを温存する/partial を活用するポリシー調整で slow≈1 を目指す。デフォルトは引き続き v2 OFF(C7_SAFE v1 本線)。
|
||||
|
||||
### Phase60: C7 v2 空ページ保持ポリシー導入(partial 温存+追加 stats)
|
||||
- 変更: `tiny_hotheap_class_v2` に `max_partial_pages`(C7 デフォルト 2)と `partial_count` を追加し、free で `used==0` のページは retire せず partial に温存。上限超のみ retire。partial push/pop/peak と retire_v2 を v2 stats に追加。
|
||||
- ベンチ:
|
||||
- C7-only (ws=64, iters=20k, PROFILE=C7_SAFE):
|
||||
- v2 OFF: 41.94M ops/s, HEAP_STATS[7] fast=11015 slow=1。
|
||||
- v2 ON: 50.43M ops/s, HEAP_STATS[7] fast=11016 slow=48。HOTHEAP_V2_C7_STATS: alloc_lease=48 (=refill), partial_push/pop/peak=0, retire_v2=0。
|
||||
- Mixed 16–1024B (ws=256, iters=20k, PROFILE=C7_SAFE):
|
||||
- v2 OFF: 42.82M ops/s, HEAP_STATS[7] fast=5691 slow=1。
|
||||
- v2 ON: 47.71M ops/s, HEAP_STATS[7] fast=5692 slow=42。HOTHEAP_V2_C7_STATS: alloc_lease=42 (=refill), partial_push/pop/peak=0, retire_v2=0。
|
||||
- 所感: slow_prepare は refill 回数と一致し、空ページがほぼ出ないため partial/retire はまだ発火していない。v2 は C7-only/Mixed ともプラスだが、refill=40〜50 が残る。ページ容量/lease 戦略や空ページを作る負荷での検証が次課題。デフォルトは引き続き v2 OFF(研究箱扱い)。
|
||||
|
||||
### Phase61: C7 v2 長尺 Mixed (ws=400, iters=1M) 安定性チェック
|
||||
- プロファイル: Mixed 16–1024B, ws=400, iters=1M, PROFILE=C7_SAFE, C7_HOT=1, LARSON_FIX=1, v2 classes=0x80, STATS_BOX ON, STATS_BATCH=0。
|
||||
- ベンチ:
|
||||
- v2 OFF: **41.58M ops/s**, HEAP_STATS[7] fast=283169 slow=1。fail/fallback=0。
|
||||
- v2 ON: **42.41M ops/s**(約 +2%)、HEAP_STATS[7] fast=283169 slow=1。v2 statsは特記なし(fail/fallbackなし)。
|
||||
- 所感: 長尺でも v2 ON で回帰なく微プラスを維持。slow=1 に張り付き、短尺で見えた refill 多発は再現せず。引き続きデフォルトは v2 OFF のまま研究箱扱い。
|
||||
|
||||
### Phase61': C7 v2 短尺 Mixed 再確認(ws=256, iters=20k)
|
||||
- プロファイル: Mixed 16–1024B, ws=256, iters=20k, PROFILE=C7_SAFE, C7_HOT=1, LARSON_FIX=1, v2 classes=0x80。
|
||||
- ベンチ:
|
||||
- v2 OFF: **43.27M ops/s**, HEAP_STATS[7] fast=5691 slow=1。
|
||||
- v2 ON: **44.57M ops/s**(約 +3%)、HEAP_STATS[7] fast=5691 slow=1。
|
||||
- 所感: 短尺でも slow_prepare は v2 OFF/ON ともに 1 件に収まり、fail/fallback も 0 で安定。Phase59 時点で見えていた「slow≈refill で 45 程度」という状態から改善され、C7 v2 は C7-only / Mixed / 短尺・長尺いずれでも v1 C7_SAFE を上回る構造になった。運用デフォルトは引き続き v2 OFF だが、bench/研究プロファイルでは C7 v2 を本命候補として扱える状態。
|
||||
|
||||
### Phase68: mid/smallmid・pool 方面への次ターゲット整理
|
||||
- 現状: mid/smallmid (257–768B メイン) のベースラインは **HAKMEM ≈28–29M ops/s** に対し mimalloc ≈54M / system ≈15M。Tiny 16–1024B は ~41–42M と比べ、mid/pool 側が大きく劣る。
|
||||
- ホットスポット: perf では `hak_pool_try_alloc/free`, `memset`, `mid_desc_lookup` が主因。pf/sys は小さく、CPU 側命令数削減がボトルネック。
|
||||
- 目標: mid/smallmid で +5〜10%(28–29M → 30–32M)をまず達成すること。
|
||||
- 方針: Tiny v2/C6 v2 は研究箱のまま固定し、pool/smallmid の Hot Box 化設計に着手(新規 POOL_V2_BOX_DESIGN を作成)。運用デフォルトは変えず、実装は段階的に A/B できるようゲート前提で進める。
|
||||
|
||||
### Phase62: C6 v2 実験箱の足場を追加(コード実装のみ、デフォルト OFF)
|
||||
- 変更:
|
||||
- TinyHotHeap v2 を C6 でも動くように拡張(Hot Box ロジックを class_idx パラメータ化、stats をクラス配列化、TLS 初期化で C6 も partial 保持 2 枚に設定)。
|
||||
- Route/Front は既存の 1 LUT + 1 switch をそのまま利用し、`HAKMEM_TINY_HOTHEAP_V2=1 HAKMEM_TINY_HOTHEAP_CLASSES=0x40` で C6 v2 を opt-in。
|
||||
- Cold IF は v1 TinyHeap をラップする既存実装を流用(refill/retire のみ触る)。fallback 記録/カウンタもクラス別に整備。
|
||||
- 状態:
|
||||
- デフォルトは v2 OFF(C7 SAFE v1 が本線)。C6 v2 は bench/研究専用の opt-in。ベンチ未実施(次フェーズで C6-heavy / Mixed A/B を取得予定)。
|
||||
- C7 v2 の安定性・性能は維持(C6 追加による挙動変化はなし)。
|
||||
|
||||
### Phase 36: TinyHotHeap v2 を「Hot Box」として再定義(設計ドキュメント整備)
|
||||
- 状況: HotHeap v2 は Phase35 まで「v1 TinyHeap/C7 SAFE の上に乗るラッパ」で、Mixed では構造的に勝てない状態だったため、**いったん棚上げ** の扱いになっていた。
|
||||
- 方針転換: `docs/analysis/TINY_HEAP_V2_DESIGN.md` に Phase36 セクションを追加し、TinyHeap v2 自体を per-thread Hot Box(TinyHotHeapBox v2)として再定義。Superslab/Tier/Remote/Stats/Learning はすべて外側の Cold Box に落とし、境界を
|
||||
@ -13,6 +133,29 @@
|
||||
- 実装ガイド: `docs/design/TINY_HOTHEAP_V2_IMPLEMENTATION_GUIDE.md`(本フェーズで骨子を追加、実装担当 AI/開発者向けの指示書)。
|
||||
- 詳細設計: `docs/analysis/TINY_HEAP_V2_DESIGN.md`(Phase36+ セクションに A/B/C 案と Box 構造を集約)。
|
||||
|
||||
### Phase 36+ (ChatGPT Pro フィードバック統合 / TinyHeap v2 ロードマップ再定義)
|
||||
- 状況整理:
|
||||
- Mixed 16–1024B: HAKMEM ≈41M ops/s / mimalloc ≈113M / system ≈92M → HAKMEM は mimalloc の ~36%、system の ~45%。
|
||||
- mid/smallmid: HAKMEM ≈28–29M / mimalloc ≈54M / system ≈15M → mid/pool は mimalloc の ~50%、system の ~2×。
|
||||
- Superslab/OS: SS_OS_STATS では 1M ops あたり `alloc≈2 free≈3 madvise≈2` 程度で、OS 呼び出しは支配的ではない。WarmPool (C7) は hit≈99%。
|
||||
- pf: ≈6.6k/1M ops はほぼ first-write 起因と推定され、HugePage/ヘッダ軽量化実験でも大きく減らせていない。
|
||||
- ChatGPT Pro からの提案(要約):
|
||||
- 次に大きく変えるべきは TinyHeap v2 の Hot 層であり、「v1 の上に乗るラッパ」ではなく **v1 と并列の Hot Box** として再設計する。
|
||||
- Superslab/Tier/Guard/Stats/Learning は v1/v2 共通の Cold Box とし、Hot→Cold の境界は共通インタフェース(`TinyColdIface` 的なもの)に集約する。
|
||||
- TinyHeap v2 は C5–C7 をカバーしつつ、rollout は C7-only → C6 → C5 の順に段階的に行う。v1 は常に fallback/safe path として残し、PolicySnapshot で `tiny_heap_version[class]` を切り替える。
|
||||
- mid/smallmid/pool v2 は第2波の最適化対象とし、pf/first-touch/HugePage は v3 以降(最後の 5〜10% を詰めるテーマ)に回すのが妥当。
|
||||
- ドキュメント反映:
|
||||
- `docs/analysis/TINY_HEAP_V2_DESIGN.md` に「ChatGPT Pro からのフィードバックと v2 ロードマップ」セクションを追加し、
|
||||
- v1/v2 并列 Hot Box 構造(Front/Gate → TinyHeapBox v1 or TinyHotHeapBox v2 → 共通の Cold Box)
|
||||
- v1/v2 共通の Cold インタフェース(TinyColdIface)導入方針
|
||||
- C7-only → C5–C7 への段階的 rollout 戦略
|
||||
- v2 世代では Superslab/Segment/Tier/Guard/Remote の構造は変えず、v3 世代で SmallObjectHeap 全体を再構成する
|
||||
を明文化。
|
||||
- 今後のロードマップ(v2 世代の位置づけ):
|
||||
- v2 では Tiny front/route Box(済)+ TinyHotHeap v2 の Hot Box 再設計に集中し、Cold Box 側はほぼ据え置きとする。
|
||||
- mid/smallmid/pool v2 は構造スケッチと A/B ゲートまでに留め、本線は pool v1 のまま。
|
||||
- pf/first-touch/HugePage は研究用モード(Mode A/B)として設計・実装を持ちつつ、運用デフォルトには含めない。
|
||||
|
||||
### Phase 37: TinyHotHeap v2 C7 current_page ポリシー修正(スローパス多発の是正)
|
||||
- ベンチ結果(Release, PROFILE=C7_SAFE):
|
||||
- C7-only (ws=64, iters=20k): v2 OFF **40.09M ops/s** / v2 ON **25.57M ops/s**(`HEAP_STATS[7] fast=97 slow=32758` → ほぼ slow_prepare)。
|
||||
@ -99,6 +242,7 @@
|
||||
- C7_SAFE (C6 OFF, `PROFILE=C7_SAFE`): **40.96M ops/s**(cls7 fast=5691 / slow=1)。
|
||||
- C6+C7 SAFE (`HEAP_CLASSES=0xC0` / C6+7 HOT / meta_mode=1): **27.21M ops/s**(cls6 fast=1388 / slow=1366、cls7 fast=5664 / slow=19)。
|
||||
- 結論: C6 TinyHeap は mode0/1 いずれも C6-heavy/Mixed で大幅マイナス。C6 meta_mode=1 は slow_prepare が増え性能も悪化。C6 は引き続き bench/実験専用マスク(0x40/0xC0)とし、通常は LEGACY または C7_SAFE プロファイルを推奨。
|
||||
- 現状の扱い: C6 v2/TinyHeap は構造レベルでは通電しているが perf 未達のため「研究箱」に固定し、`HAKMEM_TINY_HOTHEAP_CLASSES=0x40/0xC0` は常に opt-in(実験時のみ ON)とする。本線の TinyHeap は C7 SAFE(v1/v2)のみ。
|
||||
|
||||
### Phase 20: C6 Hot front の箱追加(C7 対称の直線パス)
|
||||
- 新規ドキュメント: `docs/analysis/C6_HOTBOX_DESIGN.md` を追加し、C6 を TinyHeap でホット化する箱の目的と境界を定義(SAFE のみ、ULTRA なし)。C6 TinyHeap は当面 bench/実験扱いと明記。
|
||||
@ -600,6 +744,32 @@
|
||||
- v2 ON: **24.73M ops/s**
|
||||
- 所感: 回帰を避けるため標準は v1 を維持しつつ、どの変更が悪かったかをスイッチ単位でA/Bできるようにした。次は各スイッチON/OFFでの差分取り、必要なら v2 を研究箱のまま凍結。
|
||||
|
||||
### Phase69: PoolHotBox v2 初回 A/B(Cold IF まだダミー)
|
||||
- 状況: HotBox v2 を posix_memalign/free ベースの Cold IF で通電。Pool v2 は研究箱のまま(デフォルト OFF)。
|
||||
- ベンチ (Release, min=2048/max=8192, ws=400, iters=100k, C7_SAFE, Tiny v2 OFF):
|
||||
- v2 OFF: **28.70M ops/s**
|
||||
- v2 ON (classes=0x7F, Cold IF=posix_memalign): **2.15M ops/s**
|
||||
- Stats: `alloc_refill=1630` (cls0), `alloc_refill_fail=0`, `free_fb_v1=50437` → page_of/class 判定が合わず v1 free に大量フォールバック。
|
||||
- 次の一手: Cold IF を v1 pool/Superslab 経路に差し替え、free_fb_v1 の主因(page_of or class 判定)を潰した上で再A/B。研究箱のまま進め、標準は v1 を維持。
|
||||
|
||||
### Phase70: PoolHotBox v2 Cold IF 切替(進行中)
|
||||
- 変更: Cold IF を posix_memalign から v1 ベースの mmap/POOL_PAGE_SIZE + mid_desc_register に切り替え、retire も munmap に統一。page_of で class/base 範囲を厳密チェックし、ミスマッチ時は Fail-Fast(v2 内で v1 fallback しない)に変更。free_fb_v1 は front 側のみでカウントする前提に整理。
|
||||
- ビルド: `make bench_random_mixed_hakmem -j4` 成功(警告のみ)。
|
||||
- 次アクション: C6-heavy で v2 OFF/ON を再A/B(POOL_V2_ENABLED=1, CLASSES=C6ビット, POOL_V2_STATS=1)。free_fb_v1 が 0 近傍か、refill/fail が妥当かを確認して Phase69 の回帰を再評価する。
|
||||
|
||||
### Phase72: PoolHotBox v2 page_of O(1) 化と凍結判断
|
||||
- 実装・挙動整理:
|
||||
- PoolHotBox v2 の page_of を 64KiB アラインマスク+ページ先頭ヘッダに埋めた self ポインタで O(1) 化し、retire/初期化時にヘッダを必ずセット/クリアするように変更。ヘッダ領域を除いた容量から freelist を carve するよう capacity 計算も揃えた。
|
||||
- Cold IF は v1 pool/Superslab ベースの mmap + mid_desc_register / munmap に統一し、HotBox v2 からは geometry/token だけを受け取る構造に整理。
|
||||
- C6-heavy 短〜中尺(10k〜100k/ws=400, v2 ON, POOL_V2_STATS=1)では page_of_fail_x=0 / free_fb_v1=0 / alloc_refill 十数〜数十回で完走し、構造バグや大量 fallback は解消された。
|
||||
- 1M 長尺 C6-heavy での結果:
|
||||
- v2 OFF: ≈27〜30M ops/s で安定完走。
|
||||
- v2 ON: 120s タイムアウトで完走せず(ハング/極端な遅さ)。page_of_fail は短尺では 0 だが、長尺では v1 比で極端な回帰となる。
|
||||
- 結論と方針:
|
||||
- PoolHotBox v2 の箱構造と Cold IF は一通り通電し、研究用には十分な観測性が得られたものの、C6-heavy 長尺での性能回帰が大きく、現フェーズ(Tiny v2 + mid/pool v2)のスコープでは本線候補に持ち上げるのは難しい。
|
||||
- 本フェーズでは PoolHotBox v2 を **研究箱として凍結** し、標準構成は引き続き `HAKMEM_POOL_V2_ENABLED=0`(v1 pool)とする。
|
||||
- 将来の v3 テーマとして mid/smallmid/pool の Hot/Cold 再設計を行う際に、本フェーズの実装・perf/abort ログを出発点とする。***
|
||||
|
||||
### Phase Final: 現行デフォルトと研究箱の位置づけ
|
||||
- 標準構成: `HAKMEM_TINY_HEAP_PROFILE=C7_SAFE`, `HAKMEM_TINY_HOTHEAP_V2=0`, `HAKMEM_TINY_STATS_BOX=1`, `HAKMEM_TINY_STATS_BATCH=0`, Pool は `HAKMEM_POOL_V2_ENABLED=0`。HugePage/ヘッダ light/off はすべて OFF。
|
||||
- 研究箱:
|
||||
@ -608,5 +778,86 @@
|
||||
- HugePage/ヘッダ light/off: first-touch/bench 専用。デフォルトはすべて OFF。
|
||||
- 次フェーズの方向性(v3 テーマの入口):
|
||||
- TinyHeap v2 を C5–C7 統合 HotHeap として再設計(現行 v2 とは別ライン)。
|
||||
- first-touch/page-fault 系の本格対応(HugePage/ヘッダ light の昇格可否検証)。
|
||||
- mid/smallmid の pool/フロント最適化、または mid/large route のさらなるフラット化。
|
||||
- first-touch/page-fault 系の本格対応(HugePage/ヘッダ light の昇格可否検証)。
|
||||
- mid/smallmid の pool/フロント最適化、または mid/large route のさらなるフラット化。
|
||||
|
||||
### Phase65-c7-v3 short/long A/B(C7-only, Tiny v2/pool v2 OFF)
|
||||
- 短尺 (20k, ws=64): v3 OFF `41.26M` ops/s → v3 ON `57.55M` ops/s、`alloc_refill=49, fb_v1=0, page_of_fail=0`(HEAP_STATS slow 未取得だがクラッシュなし)。
|
||||
- 長尺 (1M, ws=400): v3 OFF `38.26M` ops/s → v3 ON `50.24M` ops/s、`alloc_refill=5077, fb_v1=0`、クラッシュ/アサートなし。
|
||||
|
||||
### Phase65-c7-v3 mixed sanity(16–1024B, ws=400, iters=1M, Tiny v2/pool v2 OFF)
|
||||
- v3 OFF `41.56M` ops/s → v3 ON `49.40M` ops/s、`alloc_refill=2446, fb_v1=0`、異常なし。
|
||||
- v3 は依然デフォルト OFF(HAKMEM_SMALL_HEAP_V3_ENABLED=0)。C7-only の研究/bench では `ENABLED=1, CLASSES=0x80` で opt-in 可能。
|
||||
|
||||
### Phase73: Tiny front v3 スナップショット化(Mixed 16–1024B, C7 v3 ON)
|
||||
- 追加: `HAKMEM_TINY_FRONT_V3_ENABLED`(デフォルトOFF)と `HAKMEM_TINY_FRONT_V3_STATS` ENV を追加し、front v3 有効時に
|
||||
- unified_cache_on
|
||||
- tiny_guard_on
|
||||
- header_mode
|
||||
を 1 回だけキャッシュする `TinyFrontV3Snapshot` を導入。ホットパスでは snapshot を読むだけにして guard/UC 判定の呼び出しを整理。
|
||||
- コード影響: malloc/free 両方が snapshot を読んで UC/guard 判定をキャッシュ経由で処理(front v3 OFF のときは従来通り)。
|
||||
- Mixed 16–1024B (ws=400, iters=1M, C7 v3 ON, Tiny/Pool v2 OFF) で A/B: HEAP_STATS slow=1 のまま挙動変化なし(perf は後続フラット化で狙う)。
|
||||
|
||||
### Phase74: Tiny front v3 size→class LUT 化(挙動維持のまま前段を軽量化)
|
||||
- ENV: `HAKMEM_TINY_FRONT_V3_LUT_ENABLED`(デフォルト OFF、front v3 ON 時だけ有効)。ON 時は Tiny 前段が size→class→route を LUT 1 ルックアップで取得し、従来の `hak_tiny_size_to_class` + route 読みを置き換える。
|
||||
- LUT は起動時に既存の size→class 変換と route スナップショットを写経して構築するため挙動は不変。LUT/Front v3 が無効なときは自動で旧経路にフォールバック。
|
||||
- A/B (Mixed 16–1024B, ws=400, iters=1M, C7 v3 ON, Tiny/Pool v2 OFF, front v3 ON):
|
||||
- LUT=0: 44.820M ops/s
|
||||
- LUT=1: 45.231M ops/s(約 +0.9%)
|
||||
- C7_PAGE_STATS: prepare_calls=2446(HEAP_STATS は TinyHeap v1 経路外のため未出力)
|
||||
- 次: size→class→route 前段のさらなるフラット化(Phase2-B 相当)で 5〜10% を狙う。
|
||||
|
||||
### Phase75: Tiny front v3 route fast path(LUT→1 switch)
|
||||
- ENV: `HAKMEM_TINY_FRONT_V3_ROUTE_FAST_ENABLED`(デフォルト OFF)。front v3 + LUT が ON のときだけ、LUT に写した route_kind を直接使い `tiny_route_for_class` 呼び出しを省略する。
|
||||
- 実装: malloc_tiny_fast の Tiny 前段で size→class→route を LUT 2 バイト load だけで決定し、直後の 1 switch で v3/v2/v1/legacy に分岐。route_fast=0 では Phase74 と同じ経路に自動フォールバック。
|
||||
- A/B (Mixed 16–1024B, ws=400, iters=1M, C7 v3 ON, Tiny/Pool v2 OFF, front v3 ON, LUT ON):
|
||||
- route_fast=0: 45.066M ops/s
|
||||
- route_fast=1: 44.821M ops/s(約 -0.5%)
|
||||
- C7_PAGE_STATS: prepare_calls=2446、回帰なし(HEAP_STATS は TinyHeap v1 経路外)。
|
||||
- 所感: 微小マイナスのためデフォルトは ROUTE_FAST=0 のまま。次は size→class 前段・header/guard 判定の整理など、別軸のフラット化を検討。
|
||||
|
||||
### Phase76: Tiny front v3 Header v3 (C7-only軽量ヘッダ)
|
||||
- ENV: `HAKMEM_TINY_HEADER_V3_ENABLED`(デフォルト0), `HAKMEM_TINY_HEADER_V3_SKIP_C7`(デフォルト0)。有効時は C7 v3 alloc だけ tiny_region_id_write_header を通さず 1byte を軽く書いて返す。
|
||||
- snapshot に header_v3_enabled/skip_c7 を追加(他クラスや v1/v2/pool には無影響、いつでも ENV で OFF)。
|
||||
- A/B (Mixed 16–1024B, ws=400, iters=1M, C7 v3 ON, front v3 ON, LUT ON, route_fast=0, Tiny/Pool v2 OFF):
|
||||
- header_v3=0: 44.29M ops/s, C7_PAGE_STATS prepare_calls=2446
|
||||
- header_v3=1 + SKIP_C7=1: 43.68M ops/s(約 -1.4%)、prepare_calls=2446、fallback_v1/page_of_fail=0
|
||||
- 所感: ヘッダ簡略だけでは perf 改善せず。free 側のヘッダ依存を下げる / header_light 再設計を別フェーズで検討。デフォルトは HEADER_V3=0 のまま。
|
||||
|
||||
### Phase58: TinyColdIface 導入と C7 v2 Cold 境界接続(WIP / v2 は引き続き研究箱)
|
||||
- 変更内容(コード側の状態メモ):
|
||||
- `core/box/tiny_cold_iface_v1.h` に `TinyColdIface` を追加し、v1 TinyHeap の `tiny_heap_prepare_page()` / `tiny_heap_page_becomes_empty()` を
|
||||
- `refill_page(cold_ctx, class_idx)`
|
||||
- `retire_page(cold_ctx, class_idx, page)`
|
||||
の 2 関数にラップ。Hot Box(v2 など)はこの IF 経由でのみ Superslab/Tier/Stats に触れる方針を具体化した。
|
||||
- `core/hakmem_tiny.c` の C7 v2 実装を更新し、`tiny_hotheap_v2_refill_slow()` / `tiny_hotheap_v2_page_retire_slow()` が `TinyColdIface` 経由で v1 TinyHeap に
|
||||
ページ借用・返却を行うよう変更。refill では:
|
||||
- `cold.refill_page(tiny_heap_ctx_for_thread(), 7)` で `tiny_heap_page_t` を 1 枚取得
|
||||
- v2 側の `tiny_hotheap_page_v2` ノードを確保し、`base/capacity/slab_idx/meta` などをコピー
|
||||
- v1 ページに freelist が無い場合でも、v2 が `used=0` にリセットして `tiny_hotheap_v2_build_freelist()` で自前 freelist を構築する
|
||||
- freelist がある場合は `lease_page->meta->freelist` を v2 側 freelist で更新
|
||||
- `current_page` に必ず freelist/capacity を持つページが入るように Fail-Fast チェックを追加
|
||||
- TLS 初期化 (`tiny_hotheap_v2_tls_get()`) を整理し、クラスごとに `storage_page` を `tiny_hotheap_v2_page_reset()` でゼロ化しつつ `stride=tiny_stride_for_class(i)` を事前設定。
|
||||
v2 側の page 構造は常に Hot Box 内で初期化される。
|
||||
- v2 統計を強化し、`cold_refill_fail` / `cold_retire_calls` など Cold IF 周辺のカウンタを `HAKMEM_TINY_HOTHEAP_V2_STATS=1` でダンプ。
|
||||
- 現状の挙動 / ベンチ:
|
||||
- C7-only (ws=64, iters=20k, `PROFILE=C7_SAFE`, v2 OFF) は依然として **≈42M ops/s**, `HEAP_STATS[7] fast=11015 slow=1` を維持(v1 C7_SAFE は安定)。
|
||||
- v2 ON では、Cold IF 経由の refill/carve までは進むものの、ウォームアップ直後の `tiny_hotheap_v2_try_pop()` 付近で SIGSEGV が再現し、ベンチ完走前に落ちる。
|
||||
- 既存ログでは `route_hits` / `alloc_calls` は増えるが、`cold_refill_fail` は 0 近傍まで減少しており、「refill が常に失敗する」状態は解消。
|
||||
- SEGV は v2 `current_page` / `lease_page` と v1 TinyHeap の `tiny_heap_page_pop()` / `mark_full` の相互作用に起因している可能性が高い。
|
||||
- 問題の整理(Box Theory 観点):
|
||||
- `tiny_hotheap_v2_try_pop()` はまだ v1 TinyHeap の `tiny_heap_page_pop()` / `tiny_heap_page_mark_full()` を直接呼び出し、
|
||||
- `v2page->lease_page` の `free_list/used` を v1 関数で更新しつつ
|
||||
- v2 側の `candidate->freelist/used` にもコピーする
|
||||
という二重管理になっている。
|
||||
- 本来の設計では「Hot Box (v2) は自分の `tiny_hotheap_page_v2.freelist` だけを pop/push し、Cold Box とは `TinyColdIface` の refill/retire 境界でのみ接続する」べきであり、
|
||||
v1 TinyHeap の page/pop ロジックに依存している現状は箱の境界が曖昧になっている。
|
||||
- その結果として、v1 側の meta/used/freelist と v2 側の freelist/capacity がずれた状態で deref され、SEGV や不整合の温床になっていると考えられる。
|
||||
- 次の一手(実装担当 AI 向け指示書の要点 / 詳細は `docs/design/TINY_HOTHEAP_V2_IMPLEMENTATION_GUIDE.md` に追記済み想定):
|
||||
1. `tiny_hotheap_v2_try_pop()` から v1 TinyHeap 依存を排除し、**v2 の freelist だけで pop する直線パス**に書き換える。
|
||||
- refill 時に `TinyColdIface.refill_page()` から得た `tiny_heap_page_t` の `base/capacity` だけを信頼し、`tiny_hotheap_page_v2` 内で freelist を完全に自前構築する。
|
||||
- Hot path では `lease_page` を「Cold に返すための token」として保持するだけにし、`tiny_heap_page_pop()` / `mark_full()` など v1 の API は呼ばない。
|
||||
2. `tiny_hotheap_v2_page_retire_slow()` では、page が空になったタイミングでのみ `TinyColdIface.retire_page(cold_ctx, class_idx, lease_page)` を呼ぶようにし、
|
||||
v2 内部のリスト (`current_page` / `partial_pages` / `full_pages`) から unlink したら Hot 側の state を全て破棄する。
|
||||
3. `TinyColdIface` を **「refill/retire だけの境界」**として明確化し、Hot Box から Cold Box への侵入(meta/used/freelist の直接操作)をこれ以上増やさない。
|
||||
4. C7-only で v2 ON/OFF を A/B しつつ、`cold_refill_fail` が 0 に張り付いていること、`alloc_fast` ≈ v1 の `fast` 件数に近づいていることを確認する(性能よりもまず安定性・境界の分離を優先)。
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
#define POOL_API_INC_H
|
||||
|
||||
#include "pagefault_telemetry_box.h" // Box PageFaultTelemetry (PF_BUCKET_MID)
|
||||
#include "box/pool_hotbox_v2_box.h"
|
||||
|
||||
// Pool v2 is experimental. Default OFF (use legacy v1 path).
|
||||
static inline int hak_pool_v2_enabled(void) {
|
||||
@ -35,6 +36,61 @@ static inline int hak_pool_v2_tls_fast_enabled(void) {
|
||||
return g;
|
||||
}
|
||||
|
||||
// Pool v1 flatten (hot path only) is experimental and opt-in.
|
||||
static inline int hak_pool_v1_flatten_enabled(void) {
|
||||
static int g = -1;
|
||||
if (__builtin_expect(g == -1, 0)) {
|
||||
const char* e = getenv("HAKMEM_POOL_V1_FLATTEN_ENABLED");
|
||||
g = (e && *e && *e != '0') ? 1 : 0;
|
||||
}
|
||||
return g;
|
||||
}
|
||||
|
||||
static inline int hak_pool_v1_flatten_stats_enabled(void) {
|
||||
static int g = -1;
|
||||
if (__builtin_expect(g == -1, 0)) {
|
||||
const char* e = getenv("HAKMEM_POOL_V1_FLATTEN_STATS");
|
||||
g = (e && *e && *e != '0') ? 1 : 0;
|
||||
}
|
||||
return g;
|
||||
}
|
||||
|
||||
typedef struct PoolV1FlattenStats {
|
||||
_Atomic uint64_t alloc_tls_hit;
|
||||
_Atomic uint64_t alloc_fallback_v1;
|
||||
_Atomic uint64_t free_tls_hit;
|
||||
_Atomic uint64_t free_fallback_v1;
|
||||
_Atomic uint64_t free_fb_page_null;
|
||||
_Atomic uint64_t free_fb_not_mine;
|
||||
_Atomic uint64_t free_fb_other;
|
||||
} PoolV1FlattenStats;
|
||||
|
||||
static PoolV1FlattenStats g_pool_v1_flat_stats = {0};
|
||||
|
||||
static inline void pool_v1_flat_stats_dump(void) {
|
||||
if (!hak_pool_v1_flatten_stats_enabled()) return;
|
||||
fprintf(stderr,
|
||||
"[POOL_V1_FLAT] alloc_tls_hit=%llu alloc_fb=%llu free_tls_hit=%llu free_fb=%llu page_null=%llu not_mine=%llu other=%llu\n",
|
||||
(unsigned long long)atomic_load_explicit(&g_pool_v1_flat_stats.alloc_tls_hit,
|
||||
memory_order_relaxed),
|
||||
(unsigned long long)atomic_load_explicit(&g_pool_v1_flat_stats.alloc_fallback_v1,
|
||||
memory_order_relaxed),
|
||||
(unsigned long long)atomic_load_explicit(&g_pool_v1_flat_stats.free_tls_hit,
|
||||
memory_order_relaxed),
|
||||
(unsigned long long)atomic_load_explicit(&g_pool_v1_flat_stats.free_fallback_v1,
|
||||
memory_order_relaxed),
|
||||
(unsigned long long)atomic_load_explicit(&g_pool_v1_flat_stats.free_fb_page_null,
|
||||
memory_order_relaxed),
|
||||
(unsigned long long)atomic_load_explicit(&g_pool_v1_flat_stats.free_fb_not_mine,
|
||||
memory_order_relaxed),
|
||||
(unsigned long long)atomic_load_explicit(&g_pool_v1_flat_stats.free_fb_other,
|
||||
memory_order_relaxed));
|
||||
}
|
||||
|
||||
__attribute__((destructor)) static void pool_v1_flatten_stats_destructor(void) {
|
||||
pool_v1_flat_stats_dump();
|
||||
}
|
||||
|
||||
// Thin helper to keep the hot path straight-line when converting a PoolBlock to
|
||||
// a user pointer. All sampling/stat updates remain here so the callers stay
|
||||
// small.
|
||||
@ -123,6 +179,13 @@ static inline void* hak_pool_try_alloc_v2_impl(size_t size, uintptr_t site_id) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Experimental PoolHotBox v2 (Hot path) — currently structure only.
|
||||
if (__builtin_expect(pool_hotbox_v2_class_enabled(class_idx), 0)) {
|
||||
void* p = pool_hotbox_v2_alloc((uint32_t)class_idx, size, site_id);
|
||||
if (p) return p;
|
||||
pool_hotbox_v2_record_alloc_fallback((uint32_t)class_idx);
|
||||
}
|
||||
|
||||
if (__builtin_expect(size >= 33000 && size <= 41000, 0)) {
|
||||
HAKMEM_LOG("[Pool] ACCEPTED: class_idx=%d, proceeding with allocation\n", class_idx);
|
||||
}
|
||||
@ -357,6 +420,14 @@ static inline void hak_pool_free_v2_impl(void* ptr, size_t size, uintptr_t site_
|
||||
}
|
||||
int class_idx = mid_by_desc ? (int)d_desc->class_idx : hak_pool_get_class_index(size);
|
||||
if (class_idx < 0) return;
|
||||
if (__builtin_expect(pool_hotbox_v2_class_enabled(class_idx), 0)) {
|
||||
pool_hotbox_v2_record_free_call((uint32_t)class_idx);
|
||||
PoolBlock* raw_block_for_v2 = (PoolBlock*)raw;
|
||||
if (pool_hotbox_v2_free((uint32_t)class_idx, raw_block_for_v2)) {
|
||||
return;
|
||||
}
|
||||
pool_hotbox_v2_record_free_fallback((uint32_t)class_idx);
|
||||
}
|
||||
PoolBlock* block = (PoolBlock*)raw;
|
||||
uint64_t owner_tid = 0;
|
||||
if (d_desc) owner_tid = d_desc->owner_tid;
|
||||
@ -768,6 +839,111 @@ static inline void hak_pool_free_v1_impl(void* ptr, size_t size, uintptr_t site_
|
||||
mid_page_inuse_dec_and_maybe_dn(raw);
|
||||
}
|
||||
|
||||
// --- v1 flatten (opt-in) ----------------------------------------------------
|
||||
|
||||
static inline void* hak_pool_try_alloc_v1_flat(size_t size, uintptr_t site_id) {
|
||||
if (!hak_pool_is_poolable(size)) return NULL;
|
||||
int class_idx = hak_pool_get_class_index(size);
|
||||
if (class_idx < 0) return NULL;
|
||||
|
||||
PoolTLSRing* ring = &g_tls_bin[class_idx].ring;
|
||||
if (g_tls_ring_enabled && ring->top > 0) {
|
||||
PoolBlock* tlsb = ring->items[--ring->top];
|
||||
// Adopt shared pages to this thread so free can stay on the fast path.
|
||||
mid_desc_adopt(tlsb, class_idx, (uint64_t)(uintptr_t)pthread_self());
|
||||
if (hak_pool_v1_flatten_stats_enabled()) {
|
||||
atomic_fetch_add_explicit(&g_pool_v1_flat_stats.alloc_tls_hit, 1, memory_order_relaxed);
|
||||
}
|
||||
return hak_pool_block_to_user(tlsb, class_idx, site_id);
|
||||
}
|
||||
|
||||
if (g_tls_bin[class_idx].lo_head) {
|
||||
PoolBlock* b = g_tls_bin[class_idx].lo_head;
|
||||
g_tls_bin[class_idx].lo_head = b->next;
|
||||
if (g_tls_bin[class_idx].lo_count) g_tls_bin[class_idx].lo_count--;
|
||||
mid_desc_adopt(b, class_idx, (uint64_t)(uintptr_t)pthread_self());
|
||||
if (hak_pool_v1_flatten_stats_enabled()) {
|
||||
atomic_fetch_add_explicit(&g_pool_v1_flat_stats.alloc_tls_hit, 1, memory_order_relaxed);
|
||||
}
|
||||
return hak_pool_block_to_user(b, class_idx, site_id);
|
||||
}
|
||||
|
||||
if (hak_pool_v1_flatten_stats_enabled()) {
|
||||
atomic_fetch_add_explicit(&g_pool_v1_flat_stats.alloc_fallback_v1, 1, memory_order_relaxed);
|
||||
}
|
||||
return hak_pool_try_alloc_v1_impl(size, site_id);
|
||||
}
|
||||
|
||||
static inline void hak_pool_free_v1_flat(void* ptr, size_t size, uintptr_t site_id) {
|
||||
if (!ptr) return;
|
||||
if (!hak_pool_is_poolable(size)) return;
|
||||
|
||||
void* raw = (char*)ptr - HEADER_SIZE;
|
||||
MidPageDesc* d_desc = mid_desc_lookup(ptr);
|
||||
if (!d_desc) {
|
||||
if (hak_pool_v1_flatten_stats_enabled()) {
|
||||
atomic_fetch_add_explicit(&g_pool_v1_flat_stats.free_fallback_v1, 1, memory_order_relaxed);
|
||||
atomic_fetch_add_explicit(&g_pool_v1_flat_stats.free_fb_page_null, 1, memory_order_relaxed);
|
||||
}
|
||||
hak_pool_free_v1_impl(ptr, size, site_id);
|
||||
return;
|
||||
}
|
||||
|
||||
int class_idx = (int)d_desc->class_idx;
|
||||
if (class_idx < 0 || class_idx >= POOL_NUM_CLASSES) {
|
||||
if (hak_pool_v1_flatten_stats_enabled()) {
|
||||
atomic_fetch_add_explicit(&g_pool_v1_flat_stats.free_fallback_v1, 1, memory_order_relaxed);
|
||||
atomic_fetch_add_explicit(&g_pool_v1_flat_stats.free_fb_other, 1, memory_order_relaxed);
|
||||
}
|
||||
hak_pool_free_v1_impl(ptr, size, site_id);
|
||||
return;
|
||||
}
|
||||
|
||||
const uint64_t owner_tid = d_desc->owner_tid;
|
||||
const uint64_t self_tid = (uint64_t)(uintptr_t)pthread_self();
|
||||
|
||||
if (g_pool.tls_free_enabled && owner_tid != 0 && owner_tid == self_tid) {
|
||||
PoolBlock* block = (PoolBlock*)raw;
|
||||
PoolTLSRing* ring = &g_tls_bin[class_idx].ring;
|
||||
if (g_tls_ring_enabled && ring->top < POOL_L2_RING_CAP) {
|
||||
ring->items[ring->top++] = block;
|
||||
} else {
|
||||
block->next = g_tls_bin[class_idx].lo_head;
|
||||
g_tls_bin[class_idx].lo_head = block;
|
||||
g_tls_bin[class_idx].lo_count++;
|
||||
if ((int)g_tls_bin[class_idx].lo_count > g_tls_lo_max) {
|
||||
size_t spill = g_tls_bin[class_idx].lo_count / 2;
|
||||
int shard = hak_pool_get_shard_index(site_id);
|
||||
while (spill-- && g_tls_bin[class_idx].lo_head) {
|
||||
PoolBlock* b = g_tls_bin[class_idx].lo_head;
|
||||
g_tls_bin[class_idx].lo_head = b->next;
|
||||
g_tls_bin[class_idx].lo_count--;
|
||||
uintptr_t old_head;
|
||||
do {
|
||||
old_head = atomic_load_explicit(&g_pool.remote_head[class_idx][shard], memory_order_acquire);
|
||||
b->next = (PoolBlock*)old_head;
|
||||
} while (!atomic_compare_exchange_weak_explicit(
|
||||
&g_pool.remote_head[class_idx][shard],
|
||||
&old_head, (uintptr_t)b,
|
||||
memory_order_release, memory_order_relaxed));
|
||||
atomic_fetch_add_explicit(&g_pool.remote_count[class_idx][shard], 1, memory_order_relaxed);
|
||||
}
|
||||
set_nonempty_bit(class_idx, shard);
|
||||
}
|
||||
}
|
||||
if (hak_pool_v1_flatten_stats_enabled()) {
|
||||
atomic_fetch_add_explicit(&g_pool_v1_flat_stats.free_tls_hit, 1, memory_order_relaxed);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (hak_pool_v1_flatten_stats_enabled()) {
|
||||
atomic_fetch_add_explicit(&g_pool_v1_flat_stats.free_fallback_v1, 1, memory_order_relaxed);
|
||||
atomic_fetch_add_explicit(&g_pool_v1_flat_stats.free_fb_not_mine, 1, memory_order_relaxed);
|
||||
}
|
||||
hak_pool_free_v1_impl(ptr, size, site_id);
|
||||
}
|
||||
|
||||
static inline int hak_pool_mid_lookup_v1_impl(void* ptr, size_t* out_size) {
|
||||
if (g_mf2_enabled) { MidPage* page = mf2_addr_to_page(ptr); if (page) { int c = (int)page->class_idx; if (c < 0 || c >= POOL_NUM_CLASSES) return 0; size_t sz = g_class_sizes[c]; if (sz == 0) return 0; if (out_size) *out_size = sz; return 1; } }
|
||||
MidPageDesc* d = mid_desc_lookup(ptr); if (!d) return 0; int c = (int)d->class_idx; if (c < 0 || c >= POOL_NUM_CLASSES) return 0; size_t sz = g_class_sizes[c]; if (sz == 0) return 0; if (out_size) *out_size = sz; return 1;
|
||||
@ -783,6 +959,9 @@ static inline int hak_pool_v2_route(void) { return hak_pool_v2_enabled(); }
|
||||
|
||||
void* hak_pool_try_alloc(size_t size, uintptr_t site_id) {
|
||||
if (!hak_pool_v2_route()) {
|
||||
if (hak_pool_v1_flatten_enabled()) {
|
||||
return hak_pool_try_alloc_v1_flat(size, site_id);
|
||||
}
|
||||
return hak_pool_try_alloc_v1_impl(size, site_id);
|
||||
}
|
||||
return hak_pool_try_alloc_v2_impl(size, site_id);
|
||||
@ -790,7 +969,11 @@ void* hak_pool_try_alloc(size_t size, uintptr_t site_id) {
|
||||
|
||||
void hak_pool_free(void* ptr, size_t size, uintptr_t site_id) {
|
||||
if (!hak_pool_v2_route()) {
|
||||
hak_pool_free_v1_impl(ptr, size, site_id);
|
||||
if (hak_pool_v1_flatten_enabled()) {
|
||||
hak_pool_free_v1_flat(ptr, size, site_id);
|
||||
} else {
|
||||
hak_pool_free_v1_impl(ptr, size, site_id);
|
||||
}
|
||||
return;
|
||||
}
|
||||
hak_pool_free_v2_impl(ptr, size, site_id);
|
||||
@ -798,6 +981,8 @@ void hak_pool_free(void* ptr, size_t size, uintptr_t site_id) {
|
||||
|
||||
void hak_pool_free_fast(void* ptr, uintptr_t site_id) {
|
||||
if (!hak_pool_v2_route()) {
|
||||
// fast path lacks size; keep existing v1 fast implementation even when
|
||||
// flatten is enabled to avoid behavior drift.
|
||||
hak_pool_free_fast_v1_impl(ptr, site_id);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -38,11 +38,15 @@ static void mid_desc_init_once(void) {
|
||||
|
||||
static void mid_desc_register(void* page, int class_idx, uint64_t owner_tid) {
|
||||
mid_desc_init_once();
|
||||
uint32_t h = mid_desc_hash(page);
|
||||
void* canonical_page = (void*)((uintptr_t)page & ~((uintptr_t)POOL_PAGE_SIZE - 1));
|
||||
uint32_t h = mid_desc_hash(canonical_page);
|
||||
pthread_mutex_lock(&g_mid_desc_mu[h]);
|
||||
MidPageDesc* d = (MidPageDesc*)hkm_libc_malloc(sizeof(MidPageDesc)); // P0 Fix: Use libc malloc
|
||||
if (d) {
|
||||
d->page = page; d->class_idx = (uint8_t)class_idx; d->owner_tid = owner_tid; d->next = g_mid_desc_head[h];
|
||||
d->page = canonical_page;
|
||||
d->class_idx = (uint8_t)class_idx;
|
||||
d->owner_tid = owner_tid;
|
||||
d->next = g_mid_desc_head[h];
|
||||
atomic_store(&d->in_use, 0);
|
||||
d->blocks_per_page = 0; // optional; not used for emptiness in P0
|
||||
atomic_store(&d->pending_dn, 0);
|
||||
@ -98,4 +102,3 @@ static inline void mid_page_inuse_dec_and_maybe_dn(void* raw) {
|
||||
}
|
||||
|
||||
#endif // POOL_MID_DESC_INC_H
|
||||
|
||||
|
||||
@ -44,3 +44,32 @@
|
||||
- v1 (POOL_V2_ENABLED=0): **27.40M ops/s**
|
||||
- v2 (POOL_V2_ENABLED=1): **24.73M ops/s**
|
||||
- 所感: v2 の変更が回帰要因と判明。標準は v1 に戻し、スイッチ単位の A/B でどの改変が悪いかを切り分ける方針。
|
||||
|
||||
## Phase80: Pool v1 flatten 初回 A/B(C6-heavy)
|
||||
|
||||
- スコープ: pool v1 の自スレッドホットパス(TLS ring/lo hit)を別 Box として直線化し、mid/smallmid(C6-heavy)での ops/s 向上を狙う。pool v2 は引き続き研究箱のまま触らない。
|
||||
- 実装: `core/hakmem_pool.c` に `hak_pool_try_alloc_v1_flat` / `hak_pool_free_v1_flat` を追加し、TLS ring/lo hit 時は最小限の分岐だけで pop/push する経路を用意。miss 時や cross-thread/slow は従来の `_v1_impl` にフォールバックする構造にし、ENV `HAKMEM_POOL_V1_FLATTEN_ENABLED`(デフォルト0)と `HAKMEM_POOL_V1_FLATTEN_STATS` で切替・統計を制御。
|
||||
- ベンチ条件: `./bench_mid_large_mt_hakmem 1 1000000 400 1`(Release)、`HAKMEM_BENCH_MIN_SIZE=257`, `MAX_SIZE=768`, `HAKMEM_TINY_HEAP_PROFILE=C7_SAFE`, `HAKMEM_TINY_C7_HOT=1`, `HAKMEM_TINY_HOTHEAP_V2=0`, `HAKMEM_SMALL_HEAP_V3_ENABLED=1`, `HAKMEM_SMALL_HEAP_V3_CLASSES=0x80`, `HAKMEM_POOL_V2_ENABLED=0`, `HAKMEM_POOL_V1_FLATTEN_STATS=1`。
|
||||
- A/B 結果:
|
||||
- flatten OFF (`POOL_V1_FLATTEN_ENABLED=0`): Throughput ≈ **23.12M ops/s**、`[POOL_V1_FLAT] alloc_tls_hit=0 alloc_fb=0 free_tls_hit=0 free_fb=0`。
|
||||
- flatten ON (`POOL_V1_FLATTEN_ENABLED=1`): Throughput ≈ **25.50M ops/s**(約 +10%)、`alloc_tls_hit=499,870 alloc_fb=230 free_tls_hit=460,450 free_fb=39,649`。
|
||||
- 所感:
|
||||
- Pool v1 の TLS fast path を分離・直線化するだけで、狙っていた +5〜10% の上限付近まで改善が出た。まだ free_fb がそこそこ残っており、page_of / 自スレ判定の精度を上げて free_fb を減らす余地がある。
|
||||
- デフォルト運用は安全側を維持するため `HAKMEM_POOL_V1_FLATTEN_ENABLED=0` のままとし、C6-heavy/mid ベンチや実験時にのみ flatten 経路を ON にする Box として扱う。
|
||||
|
||||
## Phase81: Pool v1 flatten Phase2(free_fb 理由分解)
|
||||
|
||||
- 変更: flatten stats に free fallback の内訳を追加(page_null / not_mine / other)。free で mid_desc が取れない場合は page_null、owner 不一致や TLS 無効による fallback は not_mine、それ以外は other としてカウント。
|
||||
- ベンチ条件: `./bench_mid_large_mt_hakmem 1 1000000 400 1`(Release)、`HAKMEM_TINY_HEAP_PROFILE=LEGACY`, `HAKMEM_TINY_HOTHEAP_V2=0`, `HAKMEM_POOL_V2_ENABLED=0`, `HAKMEM_POOL_V1_FLATTEN_ENABLED=1`, `HAKMEM_POOL_V1_FLATTEN_STATS=1`。
|
||||
- A/B:
|
||||
- flatten OFF: **23.68M ops/s**、stats すべて 0。
|
||||
- flatten ON : **25.90M ops/s**(約 +9.4%)、`alloc_tls_hit=499,870 alloc_fb=230 free_tls_hit=460,060 free_fb=40,039 page_null=40,039 not_mine=0 other=0`。
|
||||
- 所感: free fallback のほぼすべてが「mid_desc が取れず page=null」で発生している。owner mismatch はゼロ。今後は page_of/mid_desc 判定の精度を上げることで free_fb を減らす余地がある。
|
||||
|
||||
## Phase82: mid_desc マスク整合による free_fb 削減
|
||||
|
||||
- 変更: `mid_desc_register/lookup/adopt` が扱うページアドレスを `POOL_PAGE_SIZE` で正規化し、64KiB に未アラインな mmap でも mid_desc_lookup が一致するように修正。これにより false negative の page_null を減らす。
|
||||
- ベンチ(同条件、flatten ON, Release, tiny/pool v2 OFF, LEGACY tiny):
|
||||
- flatten OFF: **23.68M ops/s**(参考値・前フェーズと同じ)。
|
||||
- flatten ON : **26.70M ops/s**(約 +13% vs OFF)、`alloc_tls_hit=499,871 alloc_fb=229 free_tls_hit=489,147 free_fb=10,952 page_null=3,476 not_mine=7,476 other=0`。
|
||||
- 所感: page_null が大幅減(≈40k→≈3.4k)。not_mine が顕在化したため、次は owner 判定/自スレ判定の精度を軽く見直す余地がある。デフォルトは引き続き flatten OFF(安全側)で、bench/実験時のみ ON。
|
||||
|
||||
Reference in New Issue
Block a user