Files
hakmem/docs/analysis/SMALLOBJECT_V7_DESIGN.md
Moe Charm (CI) ea905b2ccb docs: HAKMEM v2 generation summary and Phase v7-4 completion
- Add HAKMEM_V2_GENERATION_SUMMARY.md: comprehensive overview of v2 generation
- Update CURRENT_TASK.md: 'v2 generation complete' section
- Update SMALLOBJECT_V7_DESIGN.md: Phase v7-4 completion notes + v7-5 candidates

v2 generation freeze: ULTRA (FROZEN) / MID_v3 (stable) / v7 (research, code freeze)
Next: HakORune / JoinIR priority, HAKMEM resumes at v7-5 (multi-class expansion)

Layer structure (L0-L3) established, Box Theory implementation patterns confirmed.
Design documents serve as maps for future v7 second chapter.

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

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

17 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 が地図として機能
  • v7-4 時点の設計メモ (本セクション) が読み返せる
  • HakORune / JoinIR が一段落or 並行可能に)
  • v7 research box は冷凍庫に保存完了