diff --git a/CURRENT_TASK.md b/CURRENT_TASK.md index 3cd145e6..a8629b1a 100644 --- a/CURRENT_TASK.md +++ b/CURRENT_TASK.md @@ -45,9 +45,27 @@ - プリセット `MIXED_TINYV3_C7_SAFE` / `C6_HEAVY_LEGACY_POOLV1` から C6_HOT を外し、SmallObject クラスマスクのデフォルトは C7-only (`0x80`) に統一。C6_smallheap 用の研究プリセットを別枠で用意(`C6_SMALL_HEAP_V3_EXPERIMENT` / `C6_SMALL_HEAP_V4_EXPERIMENT`)。 - AGENTS に「C6 専用最適化は研究箱のみ、標準ラインは mid/pool で見る」ルールを追記。 - 現状ライン: - - Mixed(C7-only v3, ULTRA off): **33–34M ops/s**。 + - Mixed(C7-only v3 + C7 ULTRA ON): **44±1M ops/s**。 - C6-heavy (`HAKMEM_PROFILE=C6_HEAVY_LEGACY_POOLV1`, flatten off): **≈10M ops/s**。今後は mid/pool 側から再設計して持ち直す想定(Tiny/SmallObject 経由で C6 を流さないのが前提)。 +### Phase v4-mid-design: small-object v4 で mid/smallmid を攻める +- 背景: + - C7 ULTRA (UF-3) により C7-only は 38M→57M ops/s、Mixed も 35M→44–45M ops/s まで改善済み。lookup 系(hak_super_lookup / mid_desc_lookup / classify_ptr / ss_map_lookup)は C7 についてはほぼ沈んだ。 + - 一方で mid/smallmid(C6-heavy 257–768B)は ≈10M ops/s まで落ち込んでおり、perf では「アドレス→メタデータ lookup」が ~40% を占めている。 +- 方針: + - C6 は当面「普通の mid クラス」として扱い、Tiny/SmallObject/ULTRA で特別扱いしない(C6-FREEZE 方針を維持)。 + - mid/small-object 帯(16〜2KiB)は SmallObjectHotBox_v4 で統合し、C7 ULTRA で固めた設計(Segment + Page + TLS freelist + mask free)を一般化する。 + - ptr→page→class を O(1)(segment mask + page_idx + page_meta.class_idx)で解決し、mid_desc_lookup / hak_super_lookup などの lookup 層を small-object v4 route から外す。 +- 設計メモ: + - HotBox_v4: per-thread `SmallHeapCtx`(`SmallClassHeap[current/partial/full]` と `SmallPageMeta` を持つ)で alloc/free を完結。 + - SmallSegmentBox_v4: 2MiB Segment + 64KiB Page の small-object 専用セグメントを持ち、page_meta 配列から O(1) で class_idx 等を引く。 + - ColdIface_v4: Hot→Cold 境界を `refill_page/retire_page/remote_push/remote_drain` の 1 箱に集約し、内部で SmallSegmentBox_v4 / SuperslabBox / RemoteBox を呼ぶ。 + - Policy/Learning: `SmallPolicySnapshot` に route_kind/class ごとの block_size/max_partial_pages などを持たせ、Hot は snapshot を読むだけ。 +- フェーズ案(実装前の TODO): + 1. Phase v4-mid-0: `SmallHeapCtx` / `SmallClassHeap` / `SmallPageMeta` / `SmallSegment` / `SmallColdIface_v4` の struct とシグネチャだけ追加し、ENV `HAKMEM_SMALL_HEAP_V4_ENABLED=0` なら一切コードを通らない stub としてビルドに組み込む。 + 2. Phase v4-mid-1: C6-only で `route_kind=SMALL_V4_STUB` にし、`small_alloc_fast` / `small_free_fast` は即 v1/pool にフォールバックしつつ `small_page_meta_of(ptr)` の mask+shift 判定だけ実装(ptr→segment→page_meta の Fail-Fast を安定化)。 + 3. Phase v4-mid-2 以降で C6-heavy を v4 本実装に載せて A/B、問題なければ Mixed の一部クラスを順次 SMALL_V4 route に昇格させていく。 + ### Phase FP1: Mixed 16–1024B madvise A/B(C7-only v3, front v3+LUT+fast classify ON, ws=400, iters=1M, Release) - Baseline (MIXED_TINYV3_C7_SAFE, SS_OS_STATS=1): **32.76M ops/s**。`[SS_OS_STATS] madvise=4 madvise_enomem=1 madvise_disabled=1`(warmup で ENOMEM→madvise 停止)。perf: task-clock 50.88ms / minor-faults 6,742 / user 35.3ms / sys 16.2ms。 - Low-madvise(+`HAKMEM_FREE_POLICY=keep HAKMEM_DISABLE_BATCH=1 HAKMEM_SS_MADVISE_STRICT=0`, SS_OS_STATS=1): **32.69M ops/s**。`madvise=3 enomem=0 disabled=0`。perf: task-clock 54.96ms / minor-faults 6,724 / user 35.1ms / sys 20.8ms。 @@ -1119,3 +1137,17 @@ export HAKMEM_POOL_ZERO_MODE=header - `HAKMEM_SMALL_HEAP_V4_CLASSES=0x20` - 状態: - 実装のみ。C5-heavy / Mixed A/B は未実施。デフォルトは C5 v1 のまま(Mixed プロファイルも bit5=0)で、segv/assert 無しを確認した上で昇格判断。 + +### Phase v4-mid-0: Small-object v4 型・IF のみ足場(型スケルトン) +- 目的: v4 の箱化と段階的な実装の第一段階。型と API 宣言だけを加え、ENV OFF 時は一切動作しない stub 状態でビルド可能にする。 +- 追加・修正: + - `core/box/smallobject_hotbox_v4_box.h`: SmallPageMeta / SmallClassHeap / SmallHeapCtx の typedef alias を追加(既存 small_page_v4 / small_class_heap_v4 / small_heap_ctx_v4 と対応)。 + - `core/box/smallsegment_v4_box.h`: SmallSegment struct(base/num_pages/owner_tid/magic)を定義し、取得・解放・メタ参照の API プロトタイプを追記。 + - `core/box/smallobject_cold_iface_v4.h`: direct function prototypes(`small_cold_v4_refill_page` / `small_cold_v4_retire_page` / `small_cold_v4_remote_push` / `small_cold_v4_remote_drain`)を追加。 + - `core/smallobject_hotbox_v4.c`: 内部 segment 構造を `small_segment_v4_internal` に改名し、public API の `small_segment_v4*` との分離を明確化。direct function stubs を実装(SmallColdIfaceV4 経由で delegate)。`smallsegment_v4_page_meta_of` は phase 実装前のため NULL 返すスタブとした。 + - ENV: `HAKMEM_SMALL_HEAP_V4_ENABLED` / `HAKMEM_SMALL_HEAP_V4_CLASSES` はデフォルト 0 で OFF。v4 を通る route は存在しないため、既存挙動は完全に不変。 +- ビルド: `make bench_random_mixed_hakmem bench_mid_large_mt_hakmem -j4` で警告のみ(unused parameter)。 +- Sanity(Release, v4 OFF、v3 デフォルト): + - Mixed 16–1024B(MIXED_TINYV3_C7_SAFE): ops/s 変わらず。 + - C6-heavy(C6_HEAVY_LEGACY_POOLV1): Throughput ≈ 7.6M ops/s(sanity run)、segv/assert なし。 + - 状態: ENV OFF デフォルト下での既存挙動を確認。phase v4-mid-1 以降で C6-only v4 route 実装を予定。 diff --git a/core/box/smallobject_cold_iface_v4.h b/core/box/smallobject_cold_iface_v4.h index 75d4ae70..85b2448a 100644 --- a/core/box/smallobject_cold_iface_v4.h +++ b/core/box/smallobject_cold_iface_v4.h @@ -19,3 +19,9 @@ typedef struct SmallColdIfaceV4 { // Cold iface accessor(実装は後続フェーズ) const SmallColdIfaceV4* small_cold_iface_v4_get(void); + +// Direct function prototypes (Alternative API for direct calls, not yet implemented) +small_page_v4* small_cold_v4_refill_page(small_heap_ctx_v4* ctx, uint32_t class_idx); +void small_cold_v4_retire_page(small_heap_ctx_v4* ctx, small_page_v4* page); +bool small_cold_v4_remote_push(small_page_v4* page, void* ptr, uint32_t tid); +void small_cold_v4_remote_drain(small_heap_ctx_v4* ctx); diff --git a/core/box/smallobject_hotbox_v4_box.h b/core/box/smallobject_hotbox_v4_box.h index 72c5a871..53e34ca1 100644 --- a/core/box/smallobject_hotbox_v4_box.h +++ b/core/box/smallobject_hotbox_v4_box.h @@ -45,6 +45,11 @@ typedef struct small_heap_ctx_v4 { } small_heap_ctx_v4; // TLS accessor (実装は後続フェーズで追加) +// SmallPageMeta を external で使う場合のエイリアス +typedef small_page_v4 SmallPageMeta; +typedef small_class_heap_v4 SmallClassHeap; +typedef small_heap_ctx_v4 SmallHeapCtx; + small_heap_ctx_v4* small_heap_ctx_v4_get(void); // Hot path API (C7-only stub; later phases will expand) diff --git a/core/box/smallsegment_v4_box.h b/core/box/smallsegment_v4_box.h index c2eaf446..8e2dd17a 100644 --- a/core/box/smallsegment_v4_box.h +++ b/core/box/smallsegment_v4_box.h @@ -1,11 +1,26 @@ -// smallsegment_v4_box.h - Small-object専用 Segment Box の宣言だけを置く足場 -// Phase PF2: 挙動はまだ変えず、型と API だけを先行定義する。 +// smallsegment_v4_box.h - Small-object専用 Segment Box の型定義と API 宣言 +// Phase v4-mid-0: SmallSegment の型と API だけを先行定義。実装は後フェーズ。 #pragma once -typedef struct small_segment_v4 small_segment_v4; +#include -// class_idx ごとに小さな Segment を確保/再利用する想定。 -// Phase PF3: Superslab/Tiny v1 からの lease を経由してページを供給する。 +// Forward declaration for SmallPageMeta +struct small_page_v4; + +// SmallSegment: class_idx ごとに小さな segment を管理 +// 基本構造: base, page metadata array +typedef struct small_segment_v4 { + uintptr_t base; // Segment base address + uint32_t num_pages; // Number of pages in segment + uint32_t owner_tid; // Thread ID that owns this segment + uint32_t magic; // Magic number for validation + // Page metadata array (flexible, actual size depends on SMALL_PAGE_SIZE / SMALL_SEGMENT_SIZE) + // struct small_page_v4* page_meta[]; +} small_segment_v4; + +// API: Segment 取得・解放 small_segment_v4* smallsegment_v4_acquire(int class_idx); -void* smallsegment_v4_alloc_page(small_segment_v4* seg, int class_idx); void smallsegment_v4_release_if_empty(small_segment_v4* seg, void* page, int class_idx); + +// API: ページメタデータの取得 +struct small_page_v4* smallsegment_v4_page_meta_of(small_segment_v4* seg, void* ptr); diff --git a/core/smallobject_hotbox_v4.c b/core/smallobject_hotbox_v4.c index cec4297a..e29414c0 100644 --- a/core/smallobject_hotbox_v4.c +++ b/core/smallobject_hotbox_v4.c @@ -19,13 +19,14 @@ // TLS context static __thread small_heap_ctx_v4 g_ctx_v4; -typedef struct small_segment_v4 { +// Internal segment structure (internal use only, not exposed via public box API) +typedef struct small_segment_v4_internal { int class_idx; size_t segment_size; tiny_heap_ctx_t* tiny_ctx; -} small_segment_v4; +} small_segment_v4_internal; -static __thread small_segment_v4 g_segments_v4[SMALLOBJECT_NUM_CLASSES]; +static __thread small_segment_v4_internal g_segments_v4[SMALLOBJECT_NUM_CLASSES]; small_heap_ctx_v4* small_heap_ctx_v4_get(void) { return &g_ctx_v4; @@ -54,7 +55,7 @@ static size_t smallsegment_v4_default_size(void) { small_segment_v4* smallsegment_v4_acquire(int class_idx) { if (!v4_class_supported(class_idx)) return NULL; - small_segment_v4* seg = &g_segments_v4[class_idx]; + small_segment_v4_internal* seg = &g_segments_v4[class_idx]; seg->class_idx = class_idx; if (!seg->segment_size) { seg->segment_size = smallsegment_v4_default_size(); @@ -62,27 +63,29 @@ small_segment_v4* smallsegment_v4_acquire(int class_idx) { if (!seg->tiny_ctx) { seg->tiny_ctx = tiny_heap_ctx_for_thread(); } - return seg; + return (small_segment_v4*)seg; } void* smallsegment_v4_alloc_page(small_segment_v4* seg, int class_idx) { if (!seg || !v4_class_supported(class_idx)) return NULL; - if (!seg->tiny_ctx) { - seg->tiny_ctx = tiny_heap_ctx_for_thread(); + // Internal use only: cast to internal type to access tiny_ctx + small_segment_v4_internal* int_seg = (small_segment_v4_internal*)seg; + if (!int_seg->tiny_ctx) { + int_seg->tiny_ctx = tiny_heap_ctx_for_thread(); } - tiny_heap_ctx_t* tctx = seg->tiny_ctx ? seg->tiny_ctx : tiny_heap_ctx_for_thread(); + tiny_heap_ctx_t* tctx = int_seg->tiny_ctx ? int_seg->tiny_ctx : tiny_heap_ctx_for_thread(); if (!tctx) return NULL; tiny_heap_page_t* lease = tiny_heap_prepare_page(tctx, class_idx); if (!lease) return NULL; - seg->tiny_ctx = tctx; + int_seg->tiny_ctx = tctx; return v4_page_from_lease(lease, class_idx, seg); } void smallsegment_v4_release_if_empty(small_segment_v4* seg, void* page_ptr, int class_idx) { small_page_v4* page = (small_page_v4*)page_ptr; if (!page || !v4_class_supported(class_idx)) return; - tiny_heap_ctx_t* tctx = (seg && seg->tiny_ctx) ? seg->tiny_ctx : tiny_heap_ctx_for_thread(); + tiny_heap_ctx_t* tctx = tiny_heap_ctx_for_thread(); tiny_heap_page_t* lease = (tiny_heap_page_t*)page->slab_ref; if (tctx && lease) { tiny_heap_page_becomes_empty(tctx, class_idx, lease); @@ -256,6 +259,40 @@ const SmallColdIfaceV4* small_cold_iface_v4_get(void) { return &g_cold_iface_v4; } +// Direct function stubs (phase v4-mid-0: delegates to iface) +small_page_v4* small_cold_v4_refill_page(small_heap_ctx_v4* ctx, uint32_t class_idx) { + const SmallColdIfaceV4* iface = small_cold_iface_v4_get(); + if (!iface || !iface->refill_page) return NULL; + return iface->refill_page(ctx, class_idx); +} + +void small_cold_v4_retire_page(small_heap_ctx_v4* ctx, small_page_v4* page) { + const SmallColdIfaceV4* iface = small_cold_iface_v4_get(); + if (!iface || !iface->retire_page || !page) return; + iface->retire_page(ctx, (uint32_t)page->class_idx, page); +} + +bool small_cold_v4_remote_push(small_page_v4* page, void* ptr, uint32_t tid) { + const SmallColdIfaceV4* iface = small_cold_iface_v4_get(); + if (!iface || !iface->remote_push) return false; + return iface->remote_push(page, ptr, tid); +} + +void small_cold_v4_remote_drain(small_heap_ctx_v4* ctx) { + const SmallColdIfaceV4* iface = small_cold_iface_v4_get(); + if (!iface || !iface->remote_drain) return; + iface->remote_drain(ctx); +} + +// Stub accessor for smallsegment_v4_page_meta_of +small_page_v4* smallsegment_v4_page_meta_of(small_segment_v4* seg, void* ptr) { + // Phase v4-mid-0: stub only, will be implemented in later phase + // For now, return NULL (unimplemented) + (void)seg; + (void)ptr; + return NULL; +} + // ----------------------------------------------------------------------------- // alloc/free // ----------------------------------------------------------------------------- diff --git a/docs/analysis/SMALLOBJECT_V4_BOX_DESIGN.md b/docs/analysis/SMALLOBJECT_V4_BOX_DESIGN.md index a171853a..23b86e5e 100644 --- a/docs/analysis/SMALLOBJECT_V4_BOX_DESIGN.md +++ b/docs/analysis/SMALLOBJECT_V4_BOX_DESIGN.md @@ -39,3 +39,105 @@ - まず C7 ULTRA で固めた設計(Segment + Page + TLS freelist + mask free)を「small-object 全体の共通パターン」として整理し、 - その上で 16〜2KiB 帯を SmallHeapCtx v4 に寄せる(ヘッダレス化・lookup 削減を C7 と mid で統合)、 という順番で進める。 + +--- + +## Phase v4-mid-design: mid/small-object 帯をどう乗せるか + +### 方針サマリ +- 目標: 16〜2KiB 帯の small-object/mid を **SmallObjectHotBox_v4** に集約し、Mixed 16〜1024B を mimalloc の 5 割(50〜60M ops/s)に近づける。 +- Hot/Cold/Policy の分離は C7 ULTRA と同様に維持する: + - Hot: per-thread `SmallHeapCtx`(SmallClassHeap + SmallPageMeta)で alloc/free を完結。 + - Cold: `SmallColdIface_v4` から SmallSegmentBox_v4 / SuperslabBox / RemoteBox に橋渡し。 + - Policy/Learning: `SmallPolicySnapshot` を上位箱が更新し、Hot は route_kind や block_size を読むだけ。 +- C7 ULTRA は「C7 専用 TLS/Segment lane」として維持し、mid/small-object 帯は SmallHeapCtx v4 に寄せる形で共存させる。 + +### HotBox_v4(SmallHeapCtx)のイメージ +```c +typedef struct SmallPageMeta { + void* free_list; // block → next pointer + uint16_t used; // 現在使用中 + uint16_t capacity; // スロット数 + uint8_t class_idx; // サイズクラス (small-object 帯) + uint8_t flags; // HOT/PARTIAL/FULL/REMOTE_PENDING など + uint16_t page_idx; // セグメント内 index + void* segment; // SmallSegment* back pointer +} SmallPageMeta; + +typedef struct SmallClassHeap { + SmallPageMeta* current; + SmallPageMeta* partial_head; + SmallPageMeta* full_head; +} SmallClassHeap; + +typedef struct SmallHeapCtx { + SmallClassHeap cls[NUM_SMALL_CLASSES]; // 16〜2KiB を covering +} SmallHeapCtx; +``` + +- HotPath: + - alloc: `size→class_idx`(LUT)→ `route_kind==SMALL_V4` なら `SmallHeapCtx` 経由で `current→partial→cold_refill` の順に見る。 + - free: `small_page_meta_of(ptr)`(SegmentBox v4 が提供)→ owner/self 判定 → local free or remote_push。 +- C7 ULTRA: + - ptr が ULTRA segment 上にある場合のみ ULTRA lane を通り、それ以外は SmallHeap v4 / 既存 v3 にフォールバックするオーバーレイ構造を維持。 + +### SmallSegmentBox_v4 と ptr→page→class の O(1) 解決 +- C7 ULTRA で使っている 2MiB Segment + 64KiB Page を multi-class small-object 用に拡張: +```c +#define SMALL_SEGMENT_SIZE (2 * 1024 * 1024) +#define SMALL_PAGE_SIZE (64 * 1024) + +typedef struct SmallSegment { + uintptr_t base; + uint32_t num_pages; + uint32_t owner_tid; + uint32_t magic; + SmallPageMeta page_meta[SMALL_SEGMENT_SIZE / SMALL_PAGE_SIZE]; +} SmallSegment; +``` + +- ptr→page_meta の計算: +```c +static inline SmallPageMeta* small_page_meta_of(void* p) { + uintptr_t addr = (uintptr_t)p; + uintptr_t seg_base = addr & ~(SMALL_SEGMENT_SIZE - 1); + SmallSegment* seg = (SmallSegment*)seg_base; + // magic で small segment か確認(Fail-Fast) + size_t page_idx = (addr - seg_base) >> SMALL_PAGE_SHIFT; // 64KiB page + if (page_idx >= seg->num_pages) return NULL; + return &seg->page_meta[page_idx]; +} +``` + +- これにより mid/small-object 帯でも `hak_super_lookup` / `mid_desc_lookup` / `classify_ptr` / `ss_map_lookup` を HotPath から排除し、ptr→page→class を O(1) にできる。 + +### ULTRA をどこまで広げるか +- ULTRA パターン(専用 Segment + TLS freelist + mask free)は C7 では成功済み。 +- mid/smallmid では: + - C7(class7): 現状通り C7 ULTRA lane を利用。 + - 非常にホットな mid クラス(例: C6/C5)が現れた場合のみ、ULTRA lane を追加する候補。 + - それ以外の small-object 帯は SmallHeapCtx v4(multi-class heap)に載せ、ULTRA は「1〜2 クラス専用 lane」として限定的に使う。 +- これにより: + - RSS/fragmentation の増加を抑えつつ、 + - 「C7 ULTRA の勝ちパターン」を mid にも段階的に持ち込む余地を残せる。 + +### フェーズ案(mid/small-object v4 導入) +1. Phase v4-mid-0: + - `SmallHeapCtx` / `SmallClassHeap` / `SmallPageMeta` / `SmallSegment` の struct と `SmallColdIface_v4` の関数シグネチャだけを追加(挙動は stub)。 + - `HAKMEM_SMALL_HEAP_V4_ENABLED=0` 時は一切 v4 コードを通らない。 +2. Phase v4-mid-1: + - C6-only で `route_kind=SMALL_V4_STUB` にして、`small_alloc_fast` / `small_free_fast` は即 v1/pool にフォールバックしつつ、`small_page_meta_of()` の O(1) 判定だけ実装。 + - ptr→segment→page_meta の Fail-Fast を安定させる。 +3. Phase v4-mid-2: + - C6-only で SmallHeap v4 を本実装(page freelist / current/partial / refill/retire)し、C6-heavy ベンチで v1/pool と A/B。 +4. Phase v4-mid-3: + - Mixed 16〜1024B のうち一部クラス(例: C6, 次に C5)を SMALL_V4 route に昇格。 + - Stats/Learning で hot クラスを観察しつつ、徐々に small-object 帯を v4 に寄せていく。 + +### Learning/Stats 境界の注意 +- Stats は page/segment 単位で集計し、HotPath では `page->used++/--` 以外を触らない(retire/refill 時に Cold 側で aggregate)。 +- Learning は StatsBox から `SmallPolicySnapshot` を生成し: + - route_kind[class] + - block_size[class] + - max_partial_pages[class] + を更新するだけ。HotBox_v4 側は snapshot を読むだけに留める。