Files
hakmem/docs/analysis/SMALLOBJECT_V7_DESIGN.md
Moe Charm (CI) d5aa3110c6 Phase v7-5b: C5+C6 multi-class expansion (+4.3% improvement)
- Add C5 (256B blocks) support alongside C6 (512B blocks)
- Same segment shared between C5/C6 (page_meta.class_idx distinguishes)
- SMALL_V7_CLASS_SUPPORTED() macro for class validation
- Extend small_v7_block_size() for C5 (switch statement)

A/B Result: C6-only v7 avg 7.64M ops/s → C5+C6 v7 avg 7.97M ops/s (+4.3%)
Criteria: C6 protected , C5 net positive , TLS bloat none 

ENV: HAKMEM_SMALL_HEAP_V7_CLASSES=0x60 (bit5+bit6)

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-12 05:11:02 +09:00

26 KiB
Raw Blame History

SmallObjectHeap v7 / HAKMEM v3 コア設計メモ2025-12-11

このドキュメントは、ULTRA + MID v3 + V6 世代の上に新しく載せる SmallObjectHeap v7= HAKMEM v3 small/mid コア) の設計方針をまとめたものです。 当面は設計・型スケルトンのみで、挙動は一切変更しません。


1. 位置づけと層構造Box Theory

1-1. 既存世代のまとめ

  • L0: ULTRA lanes現行
    • C4C7 ULTRA。C7 は 2MiB Segment + 64KiB Page + TLS freelistC7 ULTRA Box
    • Mixed / C7-only で十分な性能が出ており、FROZEN完成世代 とみなす。
  • L1: HotBox v2 世代
    • Tiny front v3 + TinyHeap v1小クラス
    • MID v3257768B の mid/smallmid を TLS heap で扱う)。
    • V6 C6-only headerless coreRegionId + Segment + TLS laneの研究箱。
  • L2: Segment / Superslab / Warm / Remote
  • L3: Policy/Learner + Stats + ENVACE/ELO/CAP 等)

この世代では、各帯に特化した箱ULTRA / MID v3 / V6を積み上げることで Mixed 161024B を ~30M → ~44M ops/s まで底上げしたが、 small〜mid を一体で見る「共通の SmallObject コア」は存在しない。

1-2. v7 世代の狙い

v7 は L1 に新しく追加する SmallObjectHotBox_v7 として設計する:

Front (size→class→route LUT)
  |
  +-- L0: ULTRA lanes (C4C7, FROZEN)
  |
  +-- L1: SmallObjectHotBox_v7      ← NEW small/mid コア
  |
  +-- L1': TinyHeap v1 / MID v3 / V6 (fallback/legacy)
  |
  +-- L2: SegmentBox_v7 / ColdIface_v7
  |
  +-- L3: PolicyBox_v7 / RegionIdBox / PageStatsBox

目的:

  • small例: 16〜1KiB or 16〜2KiBと mid の一部を 1 個の thread-local heap + segment で扱う土台を作る。
  • ULTRA 世代C4C7は L0 としてそのまま残すC7 ULTRA は独立 box
  • headerless/v6 の実験で得た「RegionId + Segment + TLS lane + PageStats」の物理層パターンをコア側に反映する。

2. 型設計SmallHeapCtx_v7 / SmallSegment_v7

v7 の基本構造は v6/V3 の経験を統合したものとする。

2-1. SmallPageMeta_v7

Hot path で頻繁に触るフィールドと Stats 用フィールドを分離する。

// Hot line: alloc/free で触るフィールド
typedef struct SmallPageMeta_v7 {
    // ---- hot fields (cache line 0 想定) ----
    void     *free_list;   // LIFO freelist: block -> next
    uint32_t  used;        // 現在の使用スロット数
    uint32_t  capacity;    // この page にある block スロット数

    uint16_t  class_idx;   // サイズクラス (C0..C?)
    uint16_t  flags;       // HOT/PARTIAL/FULL/REMOTE_PENDING 等
    uint16_t  page_idx;    // Segment 内 index (0..N-1)
    uint16_t  reserved0;   // アラインメント用

    struct SmallSegment_v7 *segment; // Segment への back pointer

    // ---- cold fields (Stats/Policy, cache line 1〜) ----
    uint64_t alloc_count;        // 累積 alloc 数
    uint64_t free_count;         // 累積 free 数
    uint64_t remote_free_count;  // 累積 remote free 数

    uint16_t live_current;       // 現在の live
    uint16_t peak_live;          // lifetime 最大 live
    uint16_t remote_burst_max;   // 一度の drain で吸い上げた remote の最大
    uint16_t reserved1;

    uint32_t epoch_first_alloc;  // coarse epoch (L3 用)
    uint32_t epoch_last_free;    // 同上
} SmallPageMeta_v7;

設計ポイント:

  • Hot path (alloc/free) では free_list / used / capacity / class_idx だけを触る。
  • Stats/Learner は L2 retire 時に cold fields を SmallPageStatsV7 にまとめて L3 に渡す。
  • segment を持たせて退役時の SegmentBox 更新を簡単にする(必要であれば将来削る)。

2-2. SmallClassHeap_v7 / SmallHeapCtx_v7

各クラスの現在/部分/満杯ページと、小さな TLS magazine を持つ。

typedef struct SmallClassHeap_v7 {
    SmallPageMeta_v7 *current;       // いま alloc に使っている page
    SmallPageMeta_v7 *partial_head;  // まだ空きのあるページ
    SmallPageMeta_v7 *full_head;     // FULL 判定のページCold 側に寄せる用)

    void    *local_freelist;         // オプション: mini-ULTRA (class ローカル TLS)
    uint16_t local_freelist_count;
    uint16_t local_freelist_cap;

    uint16_t class_idx;
    uint16_t flags;                  // class 側のポリシーフラグ (ULTRA禁止など)
} SmallClassHeap_v7;

#define HAK_SMALL_NUM_CLASSES_V7  /* 例: 16〜24 */

typedef struct SmallHeapCtx_v7 {
    SmallClassHeap_v7 cls[HAK_SMALL_NUM_CLASSES_V7];
} SmallHeapCtx_v7;

設計ポイント:

  • v7 第1版では local_freelist は無効 (cap=0) にしておき、必要なクラスだけ Learner で有効化してもよい。
  • クラス数は最初は「small側16〜1KiB or 2KiBをカバーする程度」に抑え、 mid を扱う MidHeapCtx_v7 は別箱とする(後述)。

2-3. SmallSegment_v7

ULTRA の 2MiB/64KiB パターンをベースに、small v7 用 SegmentBox を定義する。

#define SMALL_SEGMENT_SIZE_V7   (2u * 1024u * 1024u)  // 2MiB
#define SMALL_PAGE_SIZE_V7      (64u * 1024u)         // 64KiB
#define SMALL_PAGES_PER_SEG_V7  (SMALL_SEGMENT_SIZE_V7 / SMALL_PAGE_SIZE_V7)

typedef struct SmallSegment_v7 {
    uintptr_t   base;           // 実データ領域の先頭アドレス
    uint32_t    num_pages;      // 実際に使うページ数
    uint32_t    owner_tid;      // 所有スレッド id

    uint32_t    flags;          // SEGMENT_IN_USE / RETIRED 等
    uint32_t    region_kind;    // REGION_SMALL_V7 / REGION_ULTRA / REGION_POOL 等
    uint32_t    segment_idx;    // RegionIdBox 上の index
    uint32_t    free_page_head; // free page stack head
    uint32_t    free_page_count;

    SmallPageMeta_v7 page_meta[SMALL_PAGES_PER_SEG_V7];
} SmallSegment_v7;

設計ポイント:

  • ptr & ~(SEG_SIZE-1) で Segment に直行し、
    (ptr - base) >> PAGE_SHIFTpage_idx を求めて page_meta[page_idx] に行ける O(1) 構造。
  • small/mid/pool で Segment geometry を変えたい場合も、API は共通で持てるSegmentBox_v7 small / mid 用の 2 種類も可)。

3. RegionIdBox / header / class 判定方針v7 世代)

3-1. header の扱い

v6 C6-only headerless 実験から:

  • header 完全削除は「Region lookup コスト」で相殺されがちで、劇的な改善には繋がらないケースが多い。
  • ただし RegionId + Segment + page_meta.class_idx のパターンは
    ptr→segment→page_meta→class を O(1) にする物理層として非常に有用。

v7 では次の方針とする:

  1. ヘッダは 薄く残すFail-Fastlegacy/pool bridgeデバッグ用
  2. small/mid の fast path では、できるだけ header を触らない。
    • C7 ULTRA / 一部 hot クラス(将来の C6 ULTRA lane など)は完全 headerless も許可。
    • SmallHeapCtx_v7 の core は「carve/refill 時に 1 回だけ書く」程度に抑える。
  3. free 時の class 判定は:
    • front/gate の hintsize→class / header + RegionIdBox + page_meta.class_idx を併用。
    • v7 small pathでは、最終的には page_meta.class_idx を真とし、hint は OBSERVE 検証用とする。

3-2. RegionIdBox API

small/mid/pool 共通の ptr 分類箱として RegionIdBox_v7 を定義する:

typedef enum {
    REGION_UNKNOWN = 0,
    REGION_SMALL_V7,
    REGION_ULTRA,
    REGION_MID_V7,
    REGION_POOL_V3,
    REGION_LARGE,
} region_kind_t;

typedef struct RegionLookupResult_v7 {
    region_kind_t kind;
    union {
        struct {
            SmallSegment_v7 *segment;
            uint16_t         page_idx;
        } small_v7;
        struct {
            void *segment;  // C7 ULTRA / mid v7 / pool v3 等
        } other;
    } u;
} RegionLookupResult_v7;

static inline RegionLookupResult_v7
region_id_lookup_v7(void *ptr);

small v7 free pathfast path:

RegionLookupResult_v7 lk = region_id_lookup_v7(ptr);
if (likely(lk.kind == REGION_SMALL_V7)) {
    SmallPageMeta_v7* page =
        &lk.u.small_v7.segment->page_meta[lk.u.small_v7.page_idx];
    uint8_t class_idx = page->class_idx;
    small_heap_free_fast_v7(ctx, page, class_idx, ptr);
} else {
    // ULTRA / MID / POOL / LEGACY へ bridge
}

移行モード:

  • Phase OBSERVE: header-based class と page_meta.class_idx を比較して log/assert。挙動はまだ v2 世代のまま。
  • Phase FROZEN: small v7 管理ページでは header を見ず、RegionId + page_meta.class_idx だけで動かす。

4. mid/pool との関係SmallHeapCtx_v7 vs MidHeapCtx_v7

v7 世代では small と mid を同じ物理層RegionIdBox + SegmentBox + PageStatsBoxに乗せつつ、 HotBox は別箱に分けるのが現実的:

RegionIdBox / SegmentBox_v7 / PageStatsBox
    |
    +--> SmallHeapCtx_v7  (small: 16〜1KiB or 2KiB)
    |
    +--> MidHeapCtx_v7    (mid: 2〜16KiB or 2〜32KiB)
    |
    +--> PoolCtx_v3/v7    (さらに大きい / 特殊用途)

方針:

  • 共通化するもの:
    • RegionIdBoxptr→region_kind + segment/page_idx
    • Segment geometry APIsmall 用と mid 用に派生しても良い)
    • PageStats 基本構造class_idx / alloc/free/remote / live/peak など)
  • 専用箱にするもの:
    • SmallHeapCtx_v7small 特有の TLS/prefetch/クラス配置)
    • MidHeapCtx_v7mid 特有の page サイズ・クラス分割・remote ポリシー)
    • PoolCtx_v3/v7巨大オブジェクト特殊用途

橋渡し:

  • RegionIdBox で kind != REGION_SMALL_V7 を検出したときのみ、 mid/pool の bridge_boxsmall_mid_bridge_free() 等)に渡す。
  • small core / mid core / pool core は互いを直接呼ばず、「bridge 1 箇所」で繋ぐ。

5. フェーズ分割v7-0 / v7-1 / v7-2 の指針)

いきなり small/mid 全体を v7 にするのではなく、C6-only small 帯から段階的に導入する。

Phase v7-0: 型とインフラだけ追加(挙動一切変更なし)

目的:

  • struct と設計 doc だけ追加し、ビルドと Box 理論上の位置づけを固める。

タスク:

  • SmallPageMeta_v7 / SmallClassHeap_v7 / SmallHeapCtx_v7 / SmallSegment_v7 struct をヘッダに追加。
  • RegionIdBox に REGION_SMALL_V7RegionLookupResult_v7 を追加(実装はまだダミーで OK
  • ENV:
    • HAKMEM_SMALL_HEAP_V7_ENABLED=0
    • HAKMEM_SMALL_HEAP_V7_CLASSES=0x0
    • front/gate は v7 に一切 route しない。

Phase v7-1: C6-only v7 stubroute だけ v7 に向ける)

目的:

  • front/gate・ENV・RegionIdBox の配線が壊れていないか確認する。

タスク:

  • TINY_ROUTE_SMALL_HEAP_V7 を route kind に追加。
  • プロファイル: C6-only v7 stub モード(CLASSES=0x40 など)を追加。
  • small_heap_alloc_fast_v7_stub(size, ci) / small_heap_free_fast_v7_stub(ptr, ci) を実装し、 中身は即座に v2 世代MID v3 / V6 / pool v1にフォールバックするだけにする。
  • RegionIdBox は OBSERVE モードで region_id_lookup_v7(ptr) を呼んで統計取得のみ(挙動不変)。

Phase v7-2: C6-only v7 本実装small帯だけ

目的:

  • C6-only の alloc/free を SmallHeapCtx_v7 + SmallSegment_v7 で本当に回し、
    C6-heavy / Mixed で v3/V6/v2 本線と比較する。

タスク:

  • SegmentBox_v7 と ColdIface_v7 を実装し、C6 pages の refill/retire を Segment v7 経由にする。
  • small_heap_alloc_fast_v7(size, ci) / small_heap_free_fast_v7(ptr, ci) を実装:
    • alloc: current→partial→cold_refill の順で page/freelist を消費。
    • free: RegionIdBox で small_v7 page を特定し、page_meta.free_list に push必要時 retire
  • プロファイル:
    • C6-only v7 ON他クラスは ULTRA + MID v3 + V6 のまま)。
  • ベンチ:
    • C6-heavy / Mixed で v7 vs MID v3 vs V6 vs v2 本線を測り、
      C6-only v7 の価値を評価(十分なら次の v7 拡張フェーズへ)。

6. まとめ

  • v2 世代ULTRA + MID v3 + V6 C6-onlyは、Box Theory に沿ってかなりやり切った世代とみなし、ここで一度締める。
  • v3 世代SmallObjectHeap v7は、「small〜mid を 1 個の SmallHeapCtx + Segment + RegionIdBox で扱う」第2章として設計する。
  • まずは C6-only small 帯から v7 を導入し、ULTRA/MID v3 を壊さない形で徐々に適用範囲を広げていく。

Phase v7-3: C6 TLS Segment Fast Path + Page Metadata Cache

目的:

  • Phase v7-2 で明らかになった RegionIdBox binary search のオーバーヘッド(-7% vs legacyを削減する。
  • "ほとんどの" C6 free パスで RegionIdBox lookup を避ける設計に改善。

主な最適化:

  1. TLS segment fast hint:

    • SmallHeapCtx_v7 に tls_seg_base / tls_seg_end / tls_seg を追加。
    • free 初期段階で if (addr >= tls_seg_base && addr < tls_seg_end) をチェックして、 ほとんどの C6 free は RegionIdBox を呼ばずに page_idx を計算できるように。
  2. same-page page_meta TLS cache:

    • SmallHeapCtx_v7 に last_page_base / last_page_end / last_page_meta を追加。
    • 前回と同じページ内の free は page_meta 検索をスキップ1-2% 改善期待)。
  3. RegionIdBox は TLS 範囲外のみ:

    • RegionIdBox は「TLS segment が知らない alloc」POOL / LEGACY / ULTRA 由来)の分類専用に限定。
    • C6-only v7 の hot path では RegionIdBox を通さない。
  4. C6-only 維持:

    • Phase v7-3 でも C6 のみに絞る。
    • C5/C4 への拡張は、v7 の性能が legacy に追いついた後に検討。

期待結果:

  • TLS fast path で RegionIdBox overhead 大部分回避 → -7% から ±0〜+α への改善を目指す。
  • SegmentBox_v7 / ColdIface_v7 の API は不変(内部最適化のみ)。

7. Phase v7-4: Policy Box (L3 層の明確化)

Policy Box の役割

SmallPolicyV7 Box を L3 に配置し、「どのクラスをどの層に送るか」を一元管理:

typedef struct SmallPolicyV7 {
    SmallRouteKind route_kind[8];  // C0-C7
} SmallPolicyV7;

const SmallPolicyV7* small_policy_v7_snapshot(void);

フロントの責務

フロントは Policy Snapshot を読んで route を選ぶだけ:

const SmallPolicyV7* policy = small_policy_v7_snapshot();
SmallRouteKind route = policy->route_kind[class_idx];

switch (route) {
    case SMALL_ROUTE_ULTRA:   // L0
    case SMALL_ROUTE_V7:      // L1
    case SMALL_ROUTE_MID_V3:  // L1'
    case SMALL_ROUTE_LEGACY:  // L1'
}

ENV の一元化

ENV 変数は Policy init で一度だけ読む:

  • HAKMEM_TINY_C7_ULTRA_ENABLED
  • HAKMEM_SMALL_HEAP_V7_ENABLED + HAKMEM_SMALL_HEAP_V7_CLASSES
  • HAKMEM_MID_V3_ENABLED + HAKMEM_MID_V3_CLASSES

優先順位: ULTRA > v7 > MID_v3 > LEGACY (固定)

将来的にはクラスごとの柔軟な優先順位設定や、Learner 連携による動的ルート選択も可能。

段階移行

Phase v7-4 では v7 関連のみ Policy box 経由に変更。 ULTRA/MID_v3/LEGACY は既存の tiny_route_env_box.h を併用(後で統合予定)。


8. v7 第2章への設計メモPhase v7-4 完了時点)

現状C6-only 研究箱)

  • 性能: 56.3M ops/s (Phase v7-3, -4.3% overhead)
  • 設計完了: SmallHeapCtx / Segment / ColdIface / RegionIdBox 統一
  • Policy統合: 完了route 一元化)

-4.3% Overhead の内訳(仮説)

要因 推定 対策
Page metadata 間接参照 ~2% Multi-class で分摊
Extra validation ~1% Branch優化
RegionIdBox fallback ~1% TLS cache強化

Multi-class 拡張時の検討項目

  1. Segment 設計:

    • Option A: Class ごとに独立 segment (SmallSegment_v7_C6, v7_C5, ...)
    • Option B: 複数 class を 1 segment 内で共存
    • Decision point: TLS hint の複数 segment 対応
  2. TLS Context 拡張:

    typedef struct SmallHeapCtx_v7_multi {
        SmallClassHeap_v7 cls[8];
        // Multi-class TLS hints
        struct {
            uintptr_t seg_base;
            uintptr_t seg_end;
            SmallSegment_v7* seg;
        } tls_seg[5];  // C3-C7
    } SmallHeapCtx_v7_multi;
    
  3. Overhead 分摊の期待値:

    • C6-only: -4.3% (current)
    • C5+C6: -2% (overhead 薄まる)
    • C4+C5+C6: -1% (さらに薄まる)

Learner 連携Phase v7-5 候補)

概要: SmallPageStatsV7 から実行時最適 route を学習

// Policy Box update interface
void small_policy_v7_update_from_learner(
    const SmallLearnerStats* stats,
    SmallPolicyV7* policy_out
);

学習要件:

  • Alloc/free count, peak_live, lifetime_ms
  • v7 vs MID_v3 の速度比較
  • Learner の信頼度 threshold

HeaderLess 統一(将来検討)

v7-5 以降でも header を削除できるかの検証:

  • v6 headerless: page->class_idx で header-free を実装済み
  • v7 適用: free 時に page_meta から class_idx を取得
  • Benefit: 1 byte 削減 per allocationmicro, 但し alloc density up

次世代開始チェックリスト

  • HAKMEM_V2_GENERATION_SUMMARY.md と本ドキュメントから、v2→v7 の層構造と責務が一貫して読み取れること
  • v7-4 時点の設計メモ(本セクション)が「どこを伸ばすか/どこは ULTRA/MID に任せるか」の地図として機能していること
  • v7 small/mid コアを C6 以外にも広げる際のクラス割り当てC2〜C7HAKMEM_V2_GENERATION_SUMMARY.md と齟齬なく定義されていること

9. HAKMEM v3mimalloc に追いつく世代)のターゲット像

この節は、「いま以降の HAKMEM 開発は何を目指すか」をブレないようにするためのメモです。

9-1. 性能ターゲットMixed 161024B

  • 評価プロファイル:
    • HAKMEM_PROFILE=MIXED_TINYV3_C7_SAFE
    • ws=400, iters=1,000,000, size range 161024B
  • 比較対象:
    • mimalloc: 現状 ~ 110120M ops/s
    • HAKMEM v2 世代: ~ 4045M ops/sULTRA + MID v3 ベース)
  • v3/v7 世代の目標:
    • 短期: 50M ops/s 台mimalloc の ~0.5×
    • 中期: 60M ops/s 近辺0.5〜0.6×)を安定して狙える設計

9-2. クラス別の役割v3 世代の前提)

docs/analysis/HAKMEM_V2_GENERATION_SUMMARY.md の整理を踏まえ、v3 世代では次の前提で進める:

  • C7 (7691024B):
    • L0 C7 ULTRA が本命。v7 small/mid コアからは除外し、ULTRA Box を前提に最適化を続ける。
  • C6 (513768B):
    • v7 SmallHeapCtx の本命クラス。
    • C6-only v7 を最初のターゲットとして、legacy/MID/v6 を相手に A/B しながらコアの形を固める。
  • C5 (257512B):
    • 現状は MID v3 + ULTRA でそこそこ速く、本線としては安定。
    • v7 への移行は C6 v7 が legacy/MID を上回るめどが立ってから、段階的に検討する。
  • C4 (129256B):
    • C4 ULTRA寄生型が効いている帯。v7 に乗せるかは慎重に検討。
    • 当面は ULTRA を前提とし、v7 small コアには無理に載せない。
  • C3C0 (≤64B):
    • Tiny legacy/Tiny front v3 のまま。当座は v7 で触らない。

この前提により、「どのクラスを v7 small/mid コアで本気で攻めるか」「どこは ULTRA/MID/legacy に任せるか」が明確になる。

9-3. v7 コアで必ず守るルール

  1. Front は route するだけ
    • size → class_idx → SmallPolicyV7.route_kind[class_idx] → switch 以外の仕事ENV 読み・Region lookup・Segment 操作)は Front に置かない。
  2. HotBox は物理層に直接触らない
    • SmallObjectHotBox_v7 は SmallSegment_v7 / RegionIdBox / PageStatsBox へのアクセスを ColdIface/API 経由に限定し、HotPath 内で ENV や Learner を直接参照しない。
  3. Learning は snapshot 経由のみ
    • L3 Policy/Learner は Stats を読んで SmallPolicyV7 や per-class パラメータtls_cap, partial_limit 等)を更新する。
    • L1/L0 は「いまの snapshot 値」を読むだけで、学習や ENV を意識しない。

9-4. v3 世代での「やってよい/やらない」境界

  • やってよい(本筋):
    • v7 small/mid コア(特に C6の alloc/free/segment/RegionId 経路の設計・実装・A/B。
    • PolicyBox を使った route_kind の再配線ULTRA/MID/v7/LEGACY のクラスごとの切り替え)。
    • RegionIdBox/Segment/PageStats の「共通物理層」としての整理と、small/mid/pool での再利用設計。
  • やらない(この世代では控える):
    • ULTRA 世代の大改造C7 ULTRA の構造変更など)。
    • MID v3 のリフトアップを目的とした過度な再実装v7/mid コア側で吸収する)。
    • header を全クラス・全経路から完全に消し去るような大手術(これは v8 以降のテーマとして温存)。

この節を「v3 / v7 世代で何を目指すか」の固定観念として扱い、今後の設計・実装・A/B で迷子になったときの基準とする。


10. Phase v7-5a: C6 v7 極限最適化 (Hot Path Stats Removal)

10-1. 目的

Phase v7-3 時点の -4.3% overhead を ±0% 以下に改善し、v7 を本線として使える状態にする。

10-2. v7-5a vs v7-5b の選択

Option 内容 リスク 期待効果
v7-5a C6 micro-optimization (stats/header) -4.3% → ±0%
v7-5b Multi-class expansion (C5/C4) overhead 分摊

採用: v7-5a を優先。低リスクで効果を確認してから v7-5b に進む。

10-3. v7-5a の最適化内容

実施した最適化

  1. Per-page stats 削除 (hot path から):

    • alloc_count++ / free_count++ / live_current++/-- を hot path から除去
    • Stats は retire 時に cold path で収集
  2. Global atomic stats の ENV-gating:

    • HAKMEM_V7_HOT_STATS=1 で有効化デフォルトOFF
    • Hot path での atomic counter を完全除去可能に
  3. Header-at-carve-time 最適化 → 断念:

    • 試行: 「refill 時にヘッダを書き、alloc 時には書かない」
    • 失敗理由: intrusive LIFO freelist が block[0..7] に next pointer を格納するため、 carve 時にヘッダを書いても alloc pop 前に上書きされる
    • 対応: ヘッダは alloc 時に書く方式を維持
// freelist 構造の制約
// block[0]: header byte (1B)
// block[0..7]: freelist next pointer (8B)
// → 重複するため、carve 時にヘッダを書いても freelist が上書きする

10-4. A/B ベンチマーク結果

ベンチ条件:

  • Range: 257-768B (C6-heavy)
  • Benchmark: bench_mid_large_mt_hakmem
  • 5 iterations each
v7 OFF (MID v3) v7 ON (v7-5a)
9,370,124 9,251,082
9,698,882 8,782,087
8,862,704 9,331,487
9,108,786 9,502,876
9,250,129 9,490,638
Avg: 9.26M Avg: 9.27M

結果: +0.15% (±0%, noise margin 内)

10-5. 評価

指標 Before (v7-3) After (v7-5a) 目標
Overhead -4.3% ±0% legacy ±2%
Stats impact 常時 ON ENV-gated -
Header overhead あり あり (維持) -

Phase v7-5a 達成: hot path から per-page stats を除去し、overhead を ±0% まで改善。

10-6. 次のステップ候補

  1. v7-5b (Multi-class): C5/C4 へ v7 を拡張し、overhead をさらに分摊
  2. Headerless mode: header 完全削除を再検討freelist 設計の変更が必要)
  3. Learner 連携: Stats から動的 route 選択

11. Phase v7-5b: C5+C6 Multi-class Expansion

11-1. スコープ

Target: C5 限定C4 は ULTRA に残す)

理由:

  • v4/v6 multi-class で TLS context 肥大 → L1 cache thrash の経験あり
  • C5 の legacy share が大きいDecision Matrix: 53%)ので先に検証

設計方針:

C6: いまの TLS lane のまま(本命クラス)
C5: TLS lane なし or local_freelist 極小(数個)
C4: ULTRA に残す(触らない)

11-2. 実装構造

Segment 共有モデル

// C5 と C6 は同じ 2MiB segment を共有
// page_meta[].class_idx で class を区別
SmallSegment_v7:
  page_meta[0]: class_idx=6 (C6 page)
  page_meta[1]: class_idx=5 (C5 page)
  page_meta[2]: class_idx=6 (C6 page)
  ...

Block Size 拡張

// smallsegment_v7_box.h
#define SMALL_V7_C5_CLASS_IDX   5
#define SMALL_V7_C5_BLOCK_SIZE  256

static inline size_t small_v7_block_size(uint32_t class_idx) {
    switch (class_idx) {
        case 5: return 256;   // C5
        case 6: return 512;   // C6
        default: return 0;    // Unsupported
    }
}

HotBox 拡張 (minimal)

// C5/C6 両対応TLS 構造は変更なし)
static inline void* small_heap_alloc_fast_v7(size_t size, uint8_t class_idx) {
    // v7-5b: C5 or C6
    if (unlikely(class_idx != SMALL_V7_C6_CLASS_IDX &&
                 class_idx != SMALL_V7_C5_CLASS_IDX)) {
        return NULL;
    }
    // ... 以下同じ
}

11-3. A/B 判断基準

ベンチ 条件 判定
C6-heavy C6-only v7 vs C5+C6 v7 C6 性能が落ちていないこと
Mixed 16-1024B 同上 C5 legacy コスト削減がトータルでプラス

採用ライン: 「C6 を守りつつ C5 がトータルでプラスなら採用」

11-4. ENV/Profile

# C5+C6 v7 実験プロファイル
HAKMEM_SMALL_HEAP_V7_ENABLED=1
HAKMEM_SMALL_HEAP_V7_CLASSES=0x60  # bit5(C5) + bit6(C6)

11-5. A/B ベンチマーク結果

ベンチ条件:

  • Range: 257-768B (C5+C6 mixed)
  • Benchmark: bench_mid_large_mt_hakmem
  • 5 iterations each
Config Run 1 Run 2 Run 3 Run 4 Run 5 Avg
C6-only v7 8.04M 6.66M 7.26M 8.14M 8.11M 7.64M
C5+C6 v7 8.35M 8.34M 7.87M 7.81M 7.49M 7.97M

結果: +4.3% improvement

11-6. 評価

基準 結果 判定
C6 性能維持 C6-only と同等以上 PASS
C5 net positive +4.3% 改善 PASS
TLS bloat なしsegment 共有) PASS

Phase v7-5b 達成: C5 を v7 に載せて、C6 を守りつつトータル +4.3% 改善。


Document Updated: 2025-12-12 Phase v7-5a Status: COMPLETE Phase v7-5b Status: COMPLETE