Files
hakmem/docs/analysis/SMALLOBJECT_HOTBOX_V3_DESIGN.md
Moe Charm (CI) 1a8652a91a Phase TLS-UNIFY-3: C6 intrusive freelist implementation (完成)
Implement C6 ULTRA intrusive LIFO freelist with ENV gating:
- Single-linked LIFO using next pointer at USER+1 offset
- tiny_next_store/tiny_next_load for pointer access (single source of truth)
- Segment learning via ss_fast_lookup (per-class seg_base/seg_end)
- ENV gate: HAKMEM_TINY_C6_ULTRA_INTRUSIVE_FL (default OFF)
- Counters: c6_ifl_push/pop/fallback in FREE_PATH_STATS

Files:
- core/box/tiny_ultra_tls_box.h: Added c6_head field for intrusive LIFO
- core/box/tiny_ultra_tls_box.c: Pop/push with intrusive branching (case 6)
- core/box/tiny_c6_ultra_intrusive_env_box.h: ENV gate (new)
- core/box/tiny_c6_intrusive_freelist_box.h: L1 pure LIFO (new)
- core/tiny_debug_ring.h: C6_IFL events
- core/box/free_path_stats_box.h/c: c6_ifl_* counters

A/B Test Results (1M iterations, ws=200, 257-512B):
- ENV_OFF (array): 56.6 Mop/s avg
- ENV_ON (intrusive): 57.6 Mop/s avg (+1.8%, within noise)
- Counters verified: c6_ifl_push=265890, c6_ifl_pop=265815, fallback=0

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

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2025-12-12 16:26:42 +09:00

23 KiB
Raw Blame History

SmallObject HotBox v3 Design (Tiny + mid/smallmid 統合案)

目的と背景

  • 現状の性能スナップショット:
    • Mixed 161024B (1t, ws=400, iters=1M, PROFILE=C7_SAFE, Tiny v2 OFF):
      • HAKMEM ≈ 4050M ops/s
      • mimalloc ≈ 110120M ops/s
      • system ≈ 90M ops/s
    • mid/smallmid (bench_mid_large_mt, 1t, ws=400, iters=1M):
      • HAKMEM ≈ 2829M ops/s
      • mimalloc ≈ 54M ops/s
      • system ≈ 15M ops/s
  • これまでの Tiny/Pool v2 の試行から見えたこと:
    • C7 v2 (TinyHotHeap v2) は C7-only / Mixed 長尺では v1 C7_SAFE と同等以上まで到達したが、C6/C5 への水平展開や pool v2 は現フェーズでは perf 未達で凍結。
    • pool v2 は page_of O(1) 化Cold IF=v1 pool/Superslab まで実装したが、C6-heavy 長尺で極端な遅さが発生し、現行設計のまま押し上げるのは難しい。
  • 結論:
    • mimalloc に 7〜8割で迫るには、「Tiny (16〜1KiB) と mid/smallmid の両方」を一体の SmallObject HotBox として設計し直す必要がある。
    • Superslab/Segment/Tier/Guard/Remote といった Cold/Safety 層は Box として残しつつ、SmallObject 側の Hot Box を 1 枚に集約する方向で v3 を設計する。

進捗サマリ (Phase A/B 通電)

  • ENV: HAKMEM_SMALL_HEAP_V3_ENABLED / HAKMEM_SMALL_HEAP_V3_CLASSES を追加(デフォルトは C7-only ON: ENABLED=1, CLASSES=0x80 相当)。
  • 型/IF: core/box/smallobject_hotbox_v3_box.h に so_page/class/ctx と stats を定義。TLS 初期化でクラス別 stride/max_partial をセット。
  • Cold IF: smallobject_cold_iface_v1.h で C7 専用の v1 Tiny ラッパを実装。refill で tiny_heap_prepare_page(7) を借り、retire で tiny_heap_page_becomes_empty に返す。
  • Hot: core/smallobject_hotbox_v3.c で so_alloc/so_free を実装current/partial freelist を v3 で管理、refill 失敗は v1 fallback。ページ内 freelist carve は v3 側で実施。
  • Route: tiny_route_env_box.hTINY_ROUTE_SMALL_HEAP_V3 を追加。クラスビットが立っているときだけ route snapshot で v3 に振り分け。
  • Front: malloc/free で v3 route を試し、失敗時は v2/v1/legacy に落とす直線パス。デフォルトは OFF なので挙動は従来通り。

Phase S1: C6 v3 研究箱C7 を壊さずにベンチ限定で解禁)

  • Gate: HAKMEM_SMALL_HEAP_V3_ENABLED/CLASSES の bit7=C7デフォルト ON=0x80、bit6=C6research-only、デフォルト OFF。C6 を叩くときは HAKMEM_TINY_C6_HOT=1 を併用して tiny front を確実に通す。
  • Cold IF: smallobject_cold_iface_v1.h を C6 にも適用し、tiny_heap_prepare_page/page_becomes_empty を C7 と同じ形で使う。v3 stats に page_of_fail を追加し、free 側の page_of ミスを計測。
  • Bench (Release, Tiny/Pool v2 OFF, ws=400, iters=1M):
    • C6-heavy A/B: MIN_SIZE=257 MAX_SIZE=768CLASSES=0x80C6 v147.71M ops/sCLASSES=0x40C6 v3, stats ON36.77M ops/scls6 route_hits=266,930 alloc_refill=5 fb_v1=0 page_of_fail=0。v3 は約 -23%。
    • Mixed 161024B: CLASSES=0x80C7-only47.45M ops/sCLASSES=0xC0C6+C7 v3, stats ON44.45M ops/scls6 route_hits=137,307 alloc_refill=1 fb_v1=0 page_of_fail=0 / cls7 alloc_refill=2,446)。約 -6%。
  • 運用方針: 標準プロファイルは HAKMEM_SMALL_HEAP_V3_CLASSES=0x80C7-only v3に確定。C6 v3 は bench/研究のみ明示 opt-in とし、C6-heavy/Mixed の本線には乗せない。性能が盛り返すまで研究箱据え置き。
  • C6-heavy を v1 固定で走らせる推奨プリセット(研究と混同しないための明示例):
    HAKMEM_BENCH_MIN_SIZE=257
    HAKMEM_BENCH_MAX_SIZE=768
    HAKMEM_TINY_HEAP_PROFILE=C7_SAFE
    HAKMEM_TINY_C6_HOT=1
    HAKMEM_SMALL_HEAP_V3_ENABLED=1
    HAKMEM_SMALL_HEAP_V3_CLASSES=0x80   # C7-only v3
    

設計ゴール (SmallObjectHotBox v3)

  • 対象サイズ帯:
    • 16〜約 2KiB 程度の「SmallObject」領域既存 Tiny C0〜C7 + mid/smallmid の一部)。
    • それより大きいサイズは現行 mid/large ルートpool v1 / direct mmapを継続使用。
  • Box 構造:
    • Hot Box: SmallObjectHotBox (per-thread)
      • 責務: size→class→page→block のみを扱う。ページ内 freelist pop/push、current/partial 管理。
      • Tiny v2 / pool v2 で学んだ「page_of O(1)、current/partial/retire ポリシー」を統合。
    • Cold Box: Superslab/Segment/Tier/Guard/Remote
      • 責務: Superslab/Segment の割当て・解放、Tier/HOT/DRAINING/FREE 管理、Remote Queue、Guard/Budget。
      • Hot からは refill/retire の 2 箇所でのみ触れる。
    • Policy Box: SmallObjectPolicySnapshot
      • 責務: クラスごとの有効/無効、max_partial_pages、warm_cap などのポリシーを _Atomic スナップショットで保持。
      • Hot は snapshot を読むだけ。Learner/ENV は snapshot の更新のみ。
    • Stats Box / Learning Box:
      • 責務: page/event 単位の delta を受け取り、Cold 側で集計・観測・学習を行う。
      • Hot は「page refill/retire 時」「alloc/free のカウンタ更新」以外では触らない。
  • 境界:
    • Hot → Cold は 2 箇所に集約:
      • so_refill_page(cold_ctx, class_idx) … SmallObjectHotBox が page を 1 枚借りる。
      • so_page_retire(cold_ctx, class_idx, page) … empty page を Cold 側に返却。
    • 現行 TinyColdIface / PoolColdIface の経験を活かし、SmallObject 用 Cold IF を 1 枚設計する。

データ構造案

Page メタ (so_page_t)

typedef struct so_page_t {
    void*     freelist;    // ページ内 block 単位の LIFO freelist
    uint32_t  used;        // 使用中 block 数
    uint32_t  capacity;    // ページ内 block 総数
    uint16_t  class_idx;   // SmallObject クラス ID
    uint16_t  flags;       // HOT/PARTIAL/FULL などの軽量フラグ
    uint32_t  block_size;  // 1 block のバイト数
    void*     base;        // ページ base アドレス
    void*     slab_ref;    // Superslab/Segment 側 token (Cold Box 用)
    struct so_page_t* next;
} so_page_t;

ポイント:

  • Tiny v2 / pool v2 のページ構造を統一し、「SmallObject 全体で同じ page 型」を使う。
  • page_of は POOL_PAGE_SIZE や Superslab サイズに合わせた mask + header で O(1) を前提とするpool v2 で得た知見)。

クラス状態 (so_class_t)

typedef struct so_class_t {
    so_page_t* current;         // ホットページ
    so_page_t* partial;         // 空きありページのリスト
    uint16_t   max_partial;     // partial に保持する上限枚数
    uint16_t   partial_count;   // 現在の partial 枚数
    uint32_t   block_size;      // クラスの block サイズ
} so_class_t;
  • Tiny C7 Safe / TinyHotHeap v2 / pool v2 の current/partial/retire ポリシーを統合。
  • full リストは v3 初期段階では不要(必要になったら追加)。

TLS コンテキスト (so_ctx_t)

typedef struct so_ctx_t {
    so_class_t cls[SMALLOBJECT_NUM_CLASSES];
} so_ctx_t;
  • TLS (__thread) で per-thread の SmallObject コンテキストを保持。
  • 初期化時にクラスごとの block_size / max_partial / ポリシー値をセットする。

Cold IF (SmallObjectColdIface) のイメージ

typedef struct SmallObjectColdIface {
    so_page_t* (*refill_page)(void* cold_ctx, uint32_t class_idx);
    void       (*retire_page)(void* cold_ctx, uint32_t class_idx, so_page_t* page);
} SmallObjectColdIface;
  • refill_page:
    • Cold Box が Superslab/Segment から SmallObject 用ページを 1 枚切り出し、
      • base / block_size / capacity / slab_ref を設定した so_page_t を返す。
    • v3 では so_page_t 自体を Cold 側で確保する案と、Hot 側 node を再利用する案のどちらかを選べるようにしておく。
  • retire_page:
    • used==0 のページを Cold Box に返却し、Tier/Guard/Remote の扱いは Cold 側に委譲。
  • 現行の TinyColdIface / pool Cold IF をラップする形で、SmallObject 用 Cold IF を段階的に導入する。

Hot パス設計alloc/free

alloc (Hot パス)

void* so_alloc_fast(so_ctx_t* ctx, uint32_t ci) {
    so_class_t* hc = &ctx->cls[ci];
    so_page_t* p = hc->current;

    if (likely(p && p->freelist && p->used < p->capacity)) {
        void* blk = p->freelist;
        p->freelist = *(void**)blk;
        p->used++;
        return blk;
    }

    if (hc->partial) {
        p = hc->partial;
        hc->partial = p->next;
        p->next = NULL;
        hc->current = p;
        if (p->freelist && p->used < p->capacity) {
            void* blk = p->freelist;
            p->freelist = *(void**)blk;
            p->used++;
            return blk;
        }
    }

    return NULL; // slow_refill へ
}
  • Slow パス (so_alloc_refill_slow) では:
    • SmallObjectColdIface.refill_page() で Cold Box からページを 1 枚借りる。
    • so_page_t に geometry を設定し、ページ内 freelist を Hot Box 側で carve。
    • hc->current にセットしてから so_alloc_fast で pop。

free (Hot パス)

void so_free_fast(so_ctx_t* ctx, uint32_t ci, void* ptr) {
    so_class_t* hc = &ctx->cls[ci];
    so_page_t* p = so_page_of(ptr); // O(1) page_of

    *(void**)ptr = p->freelist;
    p->freelist = ptr;
    p->used--;

    if (p->used == 0) {
        if (hc->partial_count < hc->max_partial) {
            p->next = hc->partial;
            hc->partial = p;
            hc->partial_count++;
        } else {
            so_page_retire_slow(ctx, ci, p); // Cold IF 経由
        }
        if (hc->current == p) hc->current = NULL;
    } else {
        if (!hc->current) hc->current = p;
    }
}
  • Tiny v2 / pool v2 で使った「空ページ温存 or retire」のポリシーを、クラス別 max_partial で制御する。
  • page_of は pool v2 と同様に O(1) で実装し、Fail-Fast ではなく統計+前段 fallback で診断できるようにする。

Front/Gate/Route の統合方針

  • size→class→route の LUT は既存 TinyRoute Box を流用しつつ、「SmallObjectHotBox v3 対応 route」を追加する。
  • 例:
    • ROUTE_SMALL_HEAP_V3 … SmallObjectHotBox v3。
    • ROUTE_TINY_V1 / ROUTE_POOL_V1 / ROUTE_LEGACY … 現行のまま。
  • PolicySnapshot で:
enum SmallObjectHeapVersion {
    SO_HEAP_V1 = 0,
    SO_HEAP_V3 = 1,
};

typedef struct SmallObjectPolicySnapshot {
    uint8_t heap_version[SMALLOBJECT_NUM_CLASSES]; // V1/V3
    uint8_t enabled_mask[SMALLOBJECT_NUM_CLASSES]; // クラスごとの ON/OFF
    uint16_t max_partial[SMALLOBJECT_NUM_CLASSES];
} SmallObjectPolicySnapshot;
  • Front からは:
    • class_idx = size_to_smallobject_class(size);
    • route = g_smallobject_route[class_idx];
    • switch (route) { SMALL_HEAP_V3 / TINY_V1 / POOL_V1 / LEGACY } という 1 LUT + 1 switch で決定。C7 v2 / Tiny v1 / pool v1 など既存経路もここで選べるようにする。

段階的 rollout 戦略

  1. Phase A: 設計・骨格導入bench/実験専用)

    • SmallObjectHotBox 型・SmallObjectColdIface・PolicySnapshot を導入。
    • まずは Tiny C7-only を SmallObjectHotBox v3 経由に差し替え(現行 C7 v2 を v3 枠に移すイメージ)。
    • ENV (HAKMEM_SMALL_HEAP_V3_ENABLED, HAKMEM_SMALL_HEAP_V3_CLASSES) で C7-only を v3 に切り替え可能に。
  2. Phase B: C6/C5 など Tiny クラスを v3 に拡張

    • C6-heavy / C5-heavy ベンチで C6/C5 を v3 に載せ、v1 vs v3 の perf A/B を取得。
    • Mixed 161024B で C7-only v3 vs C6+C7 v3 を比較。
    • ここまでは pool v1 をそのまま使い、SmallObjectHotBox v3 側で Tiny 相当をまとめて扱う。
  3. Phase C: mid/smallmid pool を SmallObject に寄せる

    • mid/smallmid サイズクラスを SmallObjectHotBox v3 のクラスとして増やし、pool v1 経路の一部を v3 に移管する。
    • Cold IF は Superslab/Segment 共通のまま、SmallObject クラスの範囲だけ v3 で扱う。
  4. Phase D: v1/v2/v3 の役割を整理

    • v1 TinyHeap / pool v1 は完全な fallback/研究箱とし、標準は SmallObjectHotBox v3 をメインにする。
    • v2 系TinyHotHeap v2 / pool v2は v3 開発の "失敗を記録した箱" として docs/analysis に残す。

非ゴール(この設計フェーズでやらないこと)

  • Superslab/Segment/Tier/Guard/Remote のフル再設計Segment サイズや Tier ポリシー変更など)は v3 後半〜v4 テーマとする。
  • first-touch/pf/HugePage/NUMA 最適化は SmallObjectHotBox v3 の上に乗る別箱として扱い、この設計では触らない。
  • 学習層 (ACE/ELO) の仕様変更は行わず、PolicySnapshot の更新だけを学習側が持ち、Hot パスは snapshot を読むだけにする。

まとめ

  • SmallObjectHotBox v3 は、TinyHotHeap v2 / pool v2 で得た知見を統合し、「SmallObject 全体を 1 枚の Hot Box」として扱う設計。
  • Hot Box と Cold Box の境界を 2 箇所refill/retireに絞り、Policy/Stats/Learning を別箱に押し出すことで、Box Theory に沿った形で mimalloc に近い構造を目指す。
  • 実装は docs/design/SMALLOBJECT_HOTBOX_V3_IMPLEMENTATION_GUIDE.md に従って段階的に行い、常に v1/v2 への rollback path を維持する。

Phase65 簡易ベンチメモC7-only v3, Tiny/Pool v2 OFF

  • 短尺 20k/ws=64: v3 OFF 41.26M ops/s → v3 ON 57.55M ops/salloc_refill=49, fallback_v1=0, page_of_fail=0
  • 長尺 1M/ws=400: v3 OFF 38.26M ops/s → v3 ON 50.24M ops/salloc_refill=5077, fallback_v1=0
  • Mixed 161024B 1M/ws=400: v3 OFF 41.56M ops/s → v3 ON 49.40M ops/salloc_refill=2446, fallback_v1=0
  • デフォルトは C7-only ON (HAKMEM_SMALL_HEAP_V3_ENABLED 未指定 / HAKMEM_SMALL_HEAP_V3_CLASSES 未指定で class7 のみ v3)。明示的に ENABLED=0 または CLASSES から bit7 を外すことで v1 経路に戻せる。

Phase65-HEAP_STATS 追加観測C7-only v3 A/B, Tiny/Pool v2 OFF

  • 短尺 20k/ws=64:
    • v3 OFF: 40.91M ops/s, HEAP_STATS[7] fast=11015 slow=1
    • v3 ON: 56.43M ops/s, v3 stats alloc_refill=49 fb_v1=0 page_of_fail=0(短尺ウォームアップ由来の refill。HEAP_STATS は Tiny v1 経路のみ出力。
  • 長尺 1M/ws=400:
    • v3 OFF: 38.29M ops/s, HEAP_STATS[7] fast=550099 slow=1
    • v3 ON: 50.25M ops/s, v3 stats alloc_refill=5077 fb_v1=0 page_of_fail=0
  • Mixed 161024B 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 を ONENABLED=1, CLASSES デフォルト=0x80としつつ、混乱を避けるため HAKMEM_SMALL_HEAP_V3_ENABLED=0 / クラスマスクでいつでも v1 に戻せるようにしている。

Phase SO-BACKEND-OPT-1: v3 Backend ボトルネック分析(計画)

現状認識PERF-ULTRA-REBASE-4 時点)

v3 backend の perf 内訳 (Mixed 16-1024B, iters=1M, ws=400):

関数 self% カテゴリ
so_alloc_fast 2.46% v3 alloc hot path
so_free 2.47% v3 free hot path
so_alloc 1.21% v3 alloc slow path
合計 ~5.14% v3 backend 全体

参考: 全体の主要ボトルネック:

  • free dispatcher: 25.48%
  • malloc dispatcher: 21.13%
  • C7 ULTRA alloc: 7.66%
  • C7 ULTRA free: 3.50%
  • C7 ULTRA refill/page_of: 1.78%
  • v3 backend: ~5.14% ← 次のターゲット

v3 の責務と設計上の位置づけ

v3 が担当するクラス:

  • C7: ULTRA との役割分担ULTRA cold path / fallback
  • C2-C6: v1 が遅い理由で v3 を選ぶケースMixed に含まれる)
  • mid/smallmid: 将来的に pool v1 から v3 へ移行予定

v3 が呼ばれるパターン:

  1. C7 ULTRA miss: C7 ULTRA が TLS segment から外れた ptr → v3 free の「class lookup + page_of」で検証
  2. C2-C6 fast path: alloc/free が v3 route で current/partial から pop/push
  3. slow path: page refill / retire / remote push など Cold IF 経由

Phase SO-BACKEND-OPT-1 の目的

v3 backend の「何が重いのか」を細分化する:

  • alloc 側: freelist carve / memset / class_idx 判定 / metadata access のうち、どれが hot か
  • free 側: header write / magic check / page_of lookup / remote 判定のうち、どれが hot か
  • クラス別分布: C4/C5 が vs C6/C7、どちらが多く呼ばれるか
  • slow path 率: page_refill / remote / v1 fallback が何回発生しているか

実装方針

HAKMEM_SO_V3_STATS=1 で有効化する stats 構造体:

struct SmallObjectStatsV3 {
    uint64_t total_alloc;            // 総 alloc call 数
    uint64_t total_free;             // 総 free call 数
    uint64_t alloc_by_class[8];      // C0-C7 毎の alloc
    uint64_t free_by_class[8];       // C0-C7 毎の free
    uint64_t alloc_refill;           // slow path (page refill)
    uint64_t alloc_current_hit;      // fast path: current から pop
    uint64_t alloc_partial_hit;      // fast path: partial から pop
    uint64_t free_current;           // fast path: current に push
    uint64_t free_partial;           // fast path: partial に push
    uint64_t free_retire;            // slow path: page retire
    uint64_t free_remote;            // slow path: remote free
    uint64_t page_of_fail;           // free 時の page lookup 失敗diagnostics
};

埋め込み箇所:

  • so_alloc_fast(): fast hit/miss で分岐計測
  • so_free_fast(): page locate / retire / remote で分岐計測
  • so_alloc_slow_refill(): refill call count

Phase SO-BACKEND-OPT-1 計測結果(実施完了)

C7-only (1024B, 1M iter, ws=400) — C7 ULTRA 無効化:

[SMALL_HEAP_V3_STATS] cls=7 route_hits=550100 alloc_calls=550100
  alloc_refill=5045 alloc_fb_v1=0 free_calls=399828 free_fb_v1=0 page_of_fail=0
  [ALLOC_DETAIL] alloc_current_hit=550095 alloc_partial_hit=5
  [FREE_DETAIL] free_current=0 free_partial=1 free_retire=349
Throughput: 42.4M ops/s (baseline 62.9M with ULTRA)

Mixed 161024B (1M iter, ws=400) — C7 ULTRA 無効化:

[SMALL_HEAP_V3_STATS] cls=7 route_hits=275089 alloc_calls=275089
  alloc_refill=2340 alloc_fb_v1=0 free_calls=204753 free_fb_v1=0 page_of_fail=0
  [ALLOC_DETAIL] alloc_current_hit=275089 alloc_partial_hit=0
  [FREE_DETAIL] free_current=0 free_partial=0 free_retire=142
Throughput: 35.9M ops/s (baseline 43.4M with ULTRA)

計測分析

Alloc パス:

メトリクス C7-only Mixed 評価
current_hit率 99.99% 100% 優秀page locality 最適)
partial_hit率 0.001% 0% 正常current が供給)
refill率 0.9% 0.85% 許容page churn 低)
alloc_fallback_v1 0% 0% v3 は robust

Free パス:

メトリクス C7-only Mixed 評価
retire率 0.09% 0.07% 低いpage churn 低)
free_fallback_v1 0% 0% v3 安定
page_of_fail 0/399828 0/204753 完璧no corner case
free_current 0 0 正常 (empty page以外は partial/retire)

推察される所見

  1. Alloc は最適化済み: current_hit ≈100% で、ほぼ毎回 TLS から poppage locality 完璧)
  2. Refill は低コスト: 0.9% 程度の頻度で Cold IF にfallthroughページ管理が効率的
  3. Free は堅牢: page_of_fail = 0 で全 ptr が正確に所属ページを特定O(1) lookup または十分な線形探索に成功)
  4. 設計的な限界: free_current=0 は normal patternempty page を温存するから)

次フェーズの決定ポイント

計測結果に基づいて以下から選択:

シナリオ 判定 対策
alloc_refill が大きい(>10% 判定: NO0.9% -
alloc_current_hit が小さい(<50% 判定: NO99.99% -
free_retire が大きい(>3% 判定: NO0.09% -
page_of_fail > 0 判定: NO0 -

結論: v3 backend の alloc/free hot path は既に最適化済み。so_alloc/so_free の内部コストheader write, memcpy, 分岐等)が 5% overhead の主因。

Phase SO-BACKEND-OPT-2 候補(ドキュメント段階、未実装)

v3 backend の so_alloc_fast/so_free_fast パスの「内部最適化」に進む場合:

  1. Header write 削減 (alloc path)

    • 現在: alloc 毎に tiny_region_id_write_header() を呼び出し
    • target: carve 時の一括初期化light mode
    • expected: 1-2% 削減
  2. Freelist carve 最適化 (alloc slow path)

    • 現在: refill 時に so_build_freelist() で手動 carve
    • target: pre-carved freelist を Cold IF から返却
    • expected: <1% 削減refill 0.9% × 削減率)
  3. Memcpy 削減 (free fast path)

    • 現在: *(void**)ptr = page->freelist; ← 8 byte store
    • target: inline assembly or atomic で削減
    • expected: 0.5-1% 削減
  4. 分岐削減 (alloc/free両路)

    • 現在: skip_header_c7, page->used==0, partial_count check 等
    • target: hot path を完全直線化unlikely() で cold path segregate
    • expected: 0.5-1% 削減

推奨: Phase SO-BACKEND-OPT-2 は実装前に perf profile (cycles:u) で so_alloc_fast/so_free_fast を詳細計測することを推奨。


Phase v11b-1: Free Path Micro-Optimization (2025-12-12)

変更内容

perf profile で free_tiny_fast() のシリアル ULTRA チェック (C7→C6→C5→C4) が 11.73% overhead を占めていることを発見。malloc_tiny_fast() と同様のパターンを適用:

  1. C7 ULTRA early-exit: Policy snapshot 前に C7 判定(最頻出パスを最短化)
  2. Single switch: route_kind[class_idx] で一発分岐jump table 生成)
  3. Dead code 削除: 未使用の v4 チェック、重複 v7 チェックを除去

結果

Workload v11a-5 v11b-1 改善
Mixed 16-1024B 45.4M ops/s 50.7M ops/s +11.7%
C6-heavy 49.1M ops/s 52.0M ops/s +5.9%
C6-heavy + MID v3.5 53.1M ops/s 53.6M ops/s +0.9%

教訓

  • alloc パス最適化 (v11a-5) と同じパターンが free パスにも有効
  • シリアル if-else チェーン → switch (jump table) で大幅改善
  • フロント層の分岐コストは backend より大きい(今回 +11.7% vs 想定 +1-2%