Phase v4-mid-0: Small-object v4 型・IF 足場(箱化モジュール化)

- SmallHeapCtx/SmallPageMeta/SmallClassHeap typedef alias 追加
- SmallSegment struct (base/num_pages/owner_tid/magic) を smallsegment_v4_box.h に定義
- SmallColdIface_v4 direct function prototypes (refill/retire/remote_push/drain)
- smallobject_hotbox_v4.c の internal/public API 分離(small_segment_v4_internal)
- direct function stubs 実装(SmallColdIfaceV4 delegate 形式)
- ENV OFF デフォルト(ENABLED=0/CLASSES=0)で既存挙動 100% 不変
- ビルド成功・sanity 確認(mixed/C6-heavy、segv/assert なし)
- CURRENT_TASK.md に Phase v4-mid-0 記録

🤖 Generated with Claude Code

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
Moe Charm (CI)
2025-12-10 23:23:07 +09:00
parent 2a13478dc7
commit 52c65da783
6 changed files with 214 additions and 17 deletions

View File

@ -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 で見る」ルールを追記。
- 現状ライン:
- MixedC7-only v3, ULTRA off: **3334M ops/s**
- MixedC7-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→4445M ops/s まで改善済み。lookup 系hak_super_lookup / mid_desc_lookup / classify_ptr / ss_map_lookupは C7 についてはほぼ沈んだ。
- 一方で mid/smallmidC6-heavy 257768Bは ≈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 161024B madvise A/BC7-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 structbase/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
- SanityRelease, v4 OFF、v3 デフォルト):
- Mixed 161024BMIXED_TINYV3_C7_SAFE: ops/s 変わらず。
- C6-heavyC6_HEAVY_LEGACY_POOLV1: Throughput ≈ 7.6M ops/ssanity run、segv/assert なし。
- 状態: ENV OFF デフォルト下での既存挙動を確認。phase v4-mid-1 以降で C6-only v4 route 実装を予定。

View File

@ -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);

View File

@ -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)

View File

@ -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 <stdint.h>
// 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);

View File

@ -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
// -----------------------------------------------------------------------------

View File

@ -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_v4SmallHeapCtxのイメージ
```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 では:
- C7class7: 現状通り C7 ULTRA lane を利用。
- 非常にホットな mid クラス(例: C6/C5が現れた場合のみ、ULTRA lane を追加する候補。
- それ以外の small-object 帯は SmallHeapCtx v4multi-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 を読むだけに留める。