Phase v4-3.1: reuse C7 v4 pages and record prep calls

This commit is contained in:
Moe Charm (CI)
2025-12-10 17:58:42 +09:00
parent 31dd1e19d7
commit cbd33511eb
9 changed files with 551 additions and 5 deletions

View File

@ -997,3 +997,67 @@ export HAKMEM_POOL_ZERO_MODE=header
### Header Light v3.1C7 header 再書き込み抑制・ベンチ専用) ### Header Light v3.1C7 header 再書き込み抑制・ベンチ専用)
- ENV: `HAKMEM_TINY_C7_HEADER_DEDUP_ENABLED`(デフォルト 0, C7 v3 ON 時のみ効く)。直前と同一のヘッダなら store をスキップ、free 側の検証は不変。 - ENV: `HAKMEM_TINY_C7_HEADER_DEDUP_ENABLED`(デフォルト 0, C7 v3 ON 時のみ効く)。直前と同一のヘッダなら store をスキップ、free 側の検証は不変。
- A/B (Mixed 161024B, ws=400, iters=1M, MIXED_TINYV3_C7_SAFE): OFF 44.38M ops/s, ON 44.30M ops/s±イズ, 回帰なし)。安全な実験箱として残す(デフォルト OFF - A/B (Mixed 161024B, ws=400, iters=1M, MIXED_TINYV3_C7_SAFE): OFF 44.38M ops/s, ON 44.30M ops/s±イズ, 回帰なし)。安全な実験箱として残す(デフォルト OFF
### Phase v4-1: SmallObjectHotBox v4 設計相談ChatGPT Proと次フェーズ方針
- 背景: C7-only SmallObject v3 + Tiny front v3LUT + fast classifyまで積んだ状態でも、Mixed 161024B で mimalloc の 30〜40% 程度。Tiny v2 / Pool v2 / C6 v3 など v2 世代の実験箱は perf 的に NG が多く、small-object heap 全体をもう一段構造から見直す必要が見えてきた。
- 設計相談ChatGPT Proからの提案要約:
- small-object heap v416〜1024B〜2KiBの箱構造:
- HotBox_v4per-thread SmallHeapCtx / SmallClassHeap / SmallPageMeta: current/partial/full を持つ page-based freelist を全 small-object クラスで統一。
- ColdIface_v4: `refill_page` / `retire_page` / `remote_push` / `remote_drain` など少数の境界 API に集約し、内部で Superslab/Warm/Remote を呼ぶ薄いラッパ。
- SuperslabBox/RemoteBox: 既存の Superslab/WarmPool/Remote を Cold 側の Box として再利用Hot から直接は触らない)。
- PolicyBox/LearningBox: small-object 用の `SmallPolicySnapshot` を作り、route_kind/classごとの block_size などを A/B できるようにするHot は snapshot を読むだけ)。
- mimalloc に近づくための「大きい一手」:
1. per-thread small-object heap v4 をきちんと作るC7-only v3 の成功パターンを generalize
2. Segment/Page/Block レイアウトと pf 削減page 配置と WarmPool を v4 用に再チューニング)。
3. front/gate を small-object 用に一段直線化Tiny/front v3 は残しつつ small-object 用 route を V4 vs legacy/pool に収束)。
- v3 の扱い:
- C7-only SmallObject v3 / front v3 は「v4 の prototype」として構造だけ再利用し、HotBox_v4 は基本的に新規設計にする。
- v2 ラッパ系TinyHotHeap v2 など)はインターフェースアイデアだけ残し、実装は archive 的扱いに寄せる。
- 次の実装フェーズv4-1の方針:
- まずは HotBox_v4 / ColdIface_v4 の「型と入口」だけを追加する(挙動は変えない):
- `core/box/smallobject_hotbox_v4_box.h` に `SmallPageMeta` / `SmallClassHeap` / `SmallHeapCtx` の struct 定義と TLS 取得 API 宣言だけ追加。
- `core/box/smallobject_cold_iface_v4.h` に `SmallColdIface` と `refill_page/retire_page` 等のインターフェース宣言だけ用意(中身は後続フェーズ)。
- route 種類として `TINY_ROUTE_SMALLHEAP_V4` を enum に追加し、front v3 の switch に case を足すが、現時点では即 legacy/v3 へフォールバックする stub に留める。
- docs:
- `docs/analysis/SMALLOBJECT_V4_BOX_DESIGN.md` を新規追加し、上記の箱構造と Phase v4-1〜v4-4 のロードマップをまとめる。
- 挙動確認:
- コード追加後も `HAKMEM_PROFILE=MIXED_TINYV3_C7_SAFE` / `C6_HEAVY_LEGACY_POOLV1` の健康診断ランが segv/assert なし・スループットほぼ不変で通ることを確認するv4 はまだ stub のため perf には影響しない前提)。
- 実装メモ (2025-12-10):
- 追加: `core/box/smallobject_hotbox_v4_box.h`, `core/box/smallobject_cold_iface_v4.h`, `docs/analysis/SMALLOBJECT_V4_BOX_DESIGN.md`。`tiny_route_env_box.h` に `TINY_ROUTE_SMALLHEAP_V4` を追加し、`malloc_tiny_fast.h` の alloc/free switch に stub case を追加C7 は v3 経由、それ以外は v1 へフォールバック)。
- 健康診断ラン: Mixed 37.16M ops/sMIXED_TINYV3_C7_SAFE, ws=400, iters=1M、C6-heavy 27.62M ops/sC6_HEAVY_LEGACY_POOLV1, ws=400, iters=1M。segv/assert なし。今後の perf 伸びしろは別途追う。
### Phase v4-2: C7-only v4 route (v3 互換挙動、ENV で A/B)
- ENV:
- `HAKMEM_SMALL_HEAP_V4_ENABLED`(デフォルト 0, 研究箱)
- `HAKMEM_SMALL_HEAP_V4_CLASSES`bit7=0x80 を C7 用に使用。v4 と v3 を両方指定した場合は v4 を優先。
- v4 OFF従来 v3: `SMALL_HEAP_V3_ENABLED=1, V3_CLASSES=0x80, V4_ENABLED=0`
- v4 ONC7-only v4: `SMALL_HEAP_V3_ENABLED=0, V3_CLASSES=0, V4_ENABLED=1, V4_CLASSES=0x80`
- 変更点:
- `smallobject_hotbox_v4_env_box.h` を追加(デフォルト OFF
- `smallobject_hotbox_v4.c` を追加し、C7 ルートは v4 → いまは v3 実装に委譲する stub後続フェーズで独立実装予定。TLS ctx (`small_heap_ctx_v4_get`) は stub を返す。
- `tiny_route_env_box.h` で v4 を優先する route snapshot に更新。`malloc_tiny_fast.h` の alloc/free で `TINY_ROUTE_SMALLHEAP_V4` case を追加C7 以外は v1/v3 へフォールバック)。
- 健康診断ランv4 OFF のまま): Mixed 37.16M ops/s、C6-heavy 27.62M ops/ssegv/assert なし)。
### Phase v4-3: C7-only v4 freelist 実装Cold は Tiny v1 経由)
- 変更:
- `smallobject_hotbox_v4.c` に C7 専用の current/partial/full + freelist を実装。page_of はクラス内リスト検索。ColdIface v4 の refill/retire は Tiny v1 (`tiny_heap_prepare_page` / `tiny_heap_page_becomes_empty`) に接続。
- `smallobject_cold_iface_v4.h` を retire(class_idx 付き) に更新。
- `smallobject_hotbox_v4_box.h` に block_size/base を追加。
- route/LUT は v4 を優先するが、v4 が無効なら従来どおり v3/v1。
- A/B (Mixed 161024B, ws=400, iters=1M):
- v3: `V3_ENABLED=1 V3_CLASSES=0x80 V4_ENABLED=0` → 39.23M ops/s
- v4: `V3_ENABLED=0 V3_CLASSES=0 V4_ENABLED=1 V4_CLASSES=0x80` → 38.01M ops/s
- segv/assert なし。v4 は自前実装になったがまだ v3 よりわずかに遅い。次は C7 ページ管理の最適化/pf 改善を検討。
### Phase v4-3.1: C7 v4 の prepare 多発を抑制current/partial 再利用強化)
- 変更:
- `smallobject_hotbox_v4.c` で current/partial を捨てず保持する設計に変更。freelist が空でも current を NULL にせず、slow パスが partial を拾う / 本当に空のときだけ refill。free 側で current が無ければ戻ってきた page を掴み直し、partial_count を持って上限 2 に抑制。
- C7 alloc で `tiny_region_id_write_header` を呼ぶようにして v3 と整合を取った。
- `smallobject_hotbox_v4_box.h` に partial_count を追加。
- A/Bws=400, iters=1M, size=1024 固定, stats ON:
- v3: prepare_calls=5,077, Throughput=41,673,129 ops/s
- v4: prepare_calls=4,701以前 17,191 → 4.7k に減少), Throughput=42,130,607 ops/sv3 比 +1%
- Mixed 161024B (MIXED_TINYV3_C7_SAFE):
- v3 route: 40,661,560 ops/s
- v4 route: 40,010,302 ops/s-1.6% 以内、回帰なし)
- 所感: C7-only では v4 が逆転し、prepare 増加の問題は解消。Mixed も健康レンジに収まった。次は C7 v4 の pf/partial 再利用 or C6/C5 拡張を検討。

View File

@ -427,7 +427,7 @@ test-box-refactor: box-refactor
./larson_hakmem 10 8 128 1024 1 12345 4 ./larson_hakmem 10 8 128 1024 1 12345 4
# Phase 4: Tiny Pool benchmarks (properly linked with hakmem) # Phase 4: Tiny Pool benchmarks (properly linked with hakmem)
TINY_BENCH_OBJS_BASE = hakmem.o hakmem_config.o hakmem_tiny_config.o hakmem_ucb1.o hakmem_bigcache.o hakmem_pool.o hakmem_l25_pool.o hakmem_site_rules.o hakmem_tiny.o core/box/ss_allocation_box.o superslab_stats.o superslab_cache.o superslab_ace.o superslab_slab.o superslab_backend.o core/superslab_head_stub.o hakmem_smallmid.o core/box/superslab_expansion_box.o core/box/integrity_box.o core/box/mailbox_box.o core/box/front_gate_box.o core/box/front_gate_classifier.o core/box/free_publish_box.o core/box/capacity_box.o core/box/carve_push_box.o core/box/prewarm_box.o core/box/ss_hot_prewarm_box.o core/box/front_metrics_box.o core/box/bench_fast_box.o core/box/ss_addr_map_box.o core/box/slab_recycling_box.o core/box/pagefault_telemetry_box.o core/box/tiny_sizeclass_hist_box.o core/box/tiny_env_box.o core/box/tiny_route_box.o core/box/tiny_page_box.o core/box/tiny_class_policy_box.o core/box/tiny_class_stats_box.o core/box/tiny_policy_learner_box.o core/box/ss_budget_box.o core/box/tiny_mem_stats_box.o core/box/c7_meta_used_counter_box.o core/box/wrapper_env_box.o core/box/madvise_guard_box.o core/box/libm_reloc_guard_box.o core/box/ptr_trace_box.o core/box/link_missing_stubs.o core/box/super_reg_box.o core/box/shared_pool_box.o core/box/remote_side_box.o core/page_arena.o core/front/tiny_unified_cache.o tiny_sticky.o tiny_remote.o tiny_publish.o tiny_debug_ring.o hakmem_tiny_magazine.o hakmem_tiny_stats.o hakmem_tiny_sfc.o hakmem_tiny_query.o hakmem_tiny_rss.o hakmem_tiny_registry.o hakmem_tiny_remote_target.o hakmem_tiny_bg_spill.o tiny_adaptive_sizing.o hakmem_super_registry.o hakmem_shared_pool.o hakmem_shared_pool_acquire.o hakmem_shared_pool_release.o hakmem_elo.o hakmem_batch.o hakmem_p2.o hakmem_sizeclass_dist.o hakmem_evo.o hakmem_debug.o hakmem_sys.o hakmem_whale.o hakmem_policy.o hakmem_ace.o hakmem_ace_stats.o hakmem_prof.o hakmem_learner.o hakmem_size_hist.o hakmem_learn_log.o hakmem_syscall.o hakmem_ace_metrics.o hakmem_ace_ucb1.o hakmem_ace_controller.o tiny_fastcache.o core/tiny_alloc_fast_push.o core/link_stubs.o core/tiny_failfast.o core/tiny_destructors.o core/smallobject_hotbox_v3.o TINY_BENCH_OBJS_BASE = hakmem.o hakmem_config.o hakmem_tiny_config.o hakmem_ucb1.o hakmem_bigcache.o hakmem_pool.o hakmem_l25_pool.o hakmem_site_rules.o hakmem_tiny.o core/box/ss_allocation_box.o superslab_stats.o superslab_cache.o superslab_ace.o superslab_slab.o superslab_backend.o core/superslab_head_stub.o hakmem_smallmid.o core/box/superslab_expansion_box.o core/box/integrity_box.o core/box/mailbox_box.o core/box/front_gate_box.o core/box/front_gate_classifier.o core/box/free_publish_box.o core/box/capacity_box.o core/box/carve_push_box.o core/box/prewarm_box.o core/box/ss_hot_prewarm_box.o core/box/front_metrics_box.o core/box/bench_fast_box.o core/box/ss_addr_map_box.o core/box/slab_recycling_box.o core/box/pagefault_telemetry_box.o core/box/tiny_sizeclass_hist_box.o core/box/tiny_env_box.o core/box/tiny_route_box.o core/box/tiny_page_box.o core/box/tiny_class_policy_box.o core/box/tiny_class_stats_box.o core/box/tiny_policy_learner_box.o core/box/ss_budget_box.o core/box/tiny_mem_stats_box.o core/box/c7_meta_used_counter_box.o core/box/wrapper_env_box.o core/box/madvise_guard_box.o core/box/libm_reloc_guard_box.o core/box/ptr_trace_box.o core/box/link_missing_stubs.o core/box/super_reg_box.o core/box/shared_pool_box.o core/box/remote_side_box.o core/page_arena.o core/front/tiny_unified_cache.o tiny_sticky.o tiny_remote.o tiny_publish.o tiny_debug_ring.o hakmem_tiny_magazine.o hakmem_tiny_stats.o hakmem_tiny_sfc.o hakmem_tiny_query.o hakmem_tiny_rss.o hakmem_tiny_registry.o hakmem_tiny_remote_target.o hakmem_tiny_bg_spill.o tiny_adaptive_sizing.o hakmem_super_registry.o hakmem_shared_pool.o hakmem_shared_pool_acquire.o hakmem_shared_pool_release.o hakmem_elo.o hakmem_batch.o hakmem_p2.o hakmem_sizeclass_dist.o hakmem_evo.o hakmem_debug.o hakmem_sys.o hakmem_whale.o hakmem_policy.o hakmem_ace.o hakmem_ace_stats.o hakmem_prof.o hakmem_learner.o hakmem_size_hist.o hakmem_learn_log.o hakmem_syscall.o hakmem_ace_metrics.o hakmem_ace_ucb1.o hakmem_ace_controller.o tiny_fastcache.o core/tiny_alloc_fast_push.o core/link_stubs.o core/tiny_failfast.o core/tiny_destructors.o core/smallobject_hotbox_v3.o core/smallobject_hotbox_v4.o
TINY_BENCH_OBJS = $(TINY_BENCH_OBJS_BASE) TINY_BENCH_OBJS = $(TINY_BENCH_OBJS_BASE)
ifeq ($(POOL_TLS_PHASE1),1) ifeq ($(POOL_TLS_PHASE1),1)
TINY_BENCH_OBJS += pool_tls.o pool_refill.o core/pool_tls_arena.o pool_tls_registry.o pool_tls_remote.o TINY_BENCH_OBJS += pool_tls.o pool_refill.o core/pool_tls_arena.o pool_tls_registry.o pool_tls_remote.o

View File

@ -0,0 +1,21 @@
// smallobject_cold_iface_v4.h - SmallObject HotHeap v4 Cold Interface (境界 API のみ)
//
// 役割:
// - HotBox_v4 と Superslab/Warm/Remote を繋ぐ関数ポインタの箱を定義する。
// - 実装は後続フェーズで追加し、いまは型と宣言だけを置く。
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include "smallobject_hotbox_v4_box.h"
typedef struct SmallColdIfaceV4 {
small_page_v4* (*refill_page)(small_heap_ctx_v4*, uint32_t class_idx);
void (*retire_page)(small_heap_ctx_v4*, uint32_t class_idx, small_page_v4* page);
bool (*remote_push)(small_page_v4* page, void* ptr, uint32_t self_tid);
void (*remote_drain)(small_heap_ctx_v4*);
} SmallColdIfaceV4;
// Cold iface accessor実装は後続フェーズ
const SmallColdIfaceV4* small_cold_iface_v4_get(void);

View File

@ -0,0 +1,48 @@
// smallobject_hotbox_v4_box.h - SmallObject HotHeap v4 (型スケルトン)
//
// 役割:
// - v4 のページ / クラス / TLS コンテキスト型と API 宣言だけを定義する箱。
// - 挙動はまだ v3/v1 のまま。alloc/free 本体は後続フェーズで実装する。
#pragma once
#include <stdint.h>
#include <stddef.h>
#include "tiny_geometry_box.h"
#ifndef SMALLOBJECT_NUM_CLASSES
#define SMALLOBJECT_NUM_CLASSES TINY_NUM_CLASSES
#endif
// Page metadata for v4 HotBox
typedef struct small_page_v4 {
void* freelist;
uint16_t used;
uint16_t capacity;
uint8_t class_idx;
uint8_t flags;
uint32_t block_size;
uint8_t* base;
void* slab_ref; // Superslab / lease token (box境界で扱う)
struct small_page_v4* next;
} small_page_v4;
// Per-class heap state (current / partial / full lists)
typedef struct small_class_heap_v4 {
small_page_v4* current;
small_page_v4* partial_head;
small_page_v4* full_head;
uint32_t partial_count;
} small_class_heap_v4;
// TLS heap context (per-thread)
typedef struct small_heap_ctx_v4 {
small_class_heap_v4 cls[SMALLOBJECT_NUM_CLASSES];
} small_heap_ctx_v4;
// TLS accessor (実装は後続フェーズで追加)
small_heap_ctx_v4* small_heap_ctx_v4_get(void);
// Hot path API (C7-only stub; later phases will expand)
void* small_heap_alloc_fast_v4(small_heap_ctx_v4* ctx, int class_idx);
void small_heap_free_fast_v4(small_heap_ctx_v4* ctx, int class_idx, void* ptr);

View File

@ -0,0 +1,45 @@
// smallobject_hotbox_v4_env_box.h - ENV gate for SmallObject HotHeap v4
// デフォルト: OFF研究用に明示 opt-in
#pragma once
#include <stdint.h>
#include <stdlib.h>
#include "../hakmem_tiny_config.h"
static inline int small_heap_v4_enabled(void) {
static int g_enable = -1;
if (__builtin_expect(g_enable == -1, 0)) {
const char* e = getenv("HAKMEM_SMALL_HEAP_V4_ENABLED");
if (e && *e) {
g_enable = (*e != '0') ? 1 : 0;
} else {
// v4 は研究箱。明示しない限り OFF
g_enable = 0;
}
}
return g_enable;
}
static inline int small_heap_v4_class_enabled(uint8_t class_idx) {
static int g_parsed = 0;
static unsigned g_mask = 0;
if (__builtin_expect(!g_parsed, 0)) {
const char* e = getenv("HAKMEM_SMALL_HEAP_V4_CLASSES");
if (e && *e) {
unsigned v = (unsigned)strtoul(e, NULL, 0);
g_mask = v & 0xFFu;
} else {
// デフォルトは全クラス OFF
g_mask = 0;
}
g_parsed = 1;
}
if (!small_heap_v4_enabled()) return 0;
if (class_idx >= TINY_NUM_CLASSES) return 0;
return (g_mask & (1u << class_idx)) != 0;
}
static inline int small_heap_v4_c7_enabled(void) {
return small_heap_v4_class_enabled(7);
}

View File

@ -10,12 +10,14 @@
#include "tiny_heap_env_box.h" #include "tiny_heap_env_box.h"
#include "smallobject_hotbox_v3_env_box.h" #include "smallobject_hotbox_v3_env_box.h"
#include "smallobject_hotbox_v4_env_box.h"
typedef enum { typedef enum {
TINY_ROUTE_LEGACY = 0, TINY_ROUTE_LEGACY = 0,
TINY_ROUTE_HEAP = 1, // TinyHeap v1 TINY_ROUTE_HEAP = 1, // TinyHeap v1
TINY_ROUTE_HOTHEAP_V2 = 2, // TinyHotHeap v2 TINY_ROUTE_HOTHEAP_V2 = 2, // TinyHotHeap v2
TINY_ROUTE_SMALL_HEAP_V3 = 3, // SmallObject HotHeap v3 (C7-first,研究箱) TINY_ROUTE_SMALL_HEAP_V3 = 3, // SmallObject HotHeap v3 (C7-first,研究箱)
TINY_ROUTE_SMALL_HEAP_V4 = 4, // SmallObject HotHeap v4 (stub, route未使用)
} tiny_route_kind_t; } tiny_route_kind_t;
extern tiny_route_kind_t g_tiny_route_class[TINY_NUM_CLASSES]; extern tiny_route_kind_t g_tiny_route_class[TINY_NUM_CLASSES];
@ -23,7 +25,9 @@ extern int g_tiny_route_snapshot_done;
static inline void tiny_route_snapshot_init(void) { static inline void tiny_route_snapshot_init(void) {
for (int i = 0; i < TINY_NUM_CLASSES; i++) { for (int i = 0; i < TINY_NUM_CLASSES; i++) {
if (small_heap_v3_class_enabled((uint8_t)i)) { if (small_heap_v4_class_enabled((uint8_t)i)) {
g_tiny_route_class[i] = TINY_ROUTE_SMALL_HEAP_V4;
} else if (small_heap_v3_class_enabled((uint8_t)i)) {
g_tiny_route_class[i] = TINY_ROUTE_SMALL_HEAP_V3; g_tiny_route_class[i] = TINY_ROUTE_SMALL_HEAP_V3;
} else if (tiny_hotheap_v2_class_enabled((uint8_t)i)) { } else if (tiny_hotheap_v2_class_enabled((uint8_t)i)) {
g_tiny_route_class[i] = TINY_ROUTE_HOTHEAP_V2; g_tiny_route_class[i] = TINY_ROUTE_HOTHEAP_V2;
@ -47,7 +51,10 @@ static inline tiny_route_kind_t tiny_route_for_class(uint8_t ci) {
} }
static inline int tiny_route_is_heap_kind(tiny_route_kind_t route) { static inline int tiny_route_is_heap_kind(tiny_route_kind_t route) {
return route == TINY_ROUTE_HEAP || route == TINY_ROUTE_HOTHEAP_V2 || route == TINY_ROUTE_SMALL_HEAP_V3; return route == TINY_ROUTE_HEAP ||
route == TINY_ROUTE_HOTHEAP_V2 ||
route == TINY_ROUTE_SMALL_HEAP_V3 ||
route == TINY_ROUTE_SMALL_HEAP_V4;
} }
// C7 front が TinyHeap を使うかRoute snapshot 経由で判定) // C7 front が TinyHeap を使うかRoute snapshot 経由で判定)

View File

@ -41,6 +41,7 @@
#include "../box/tiny_heap_box.h" // TinyHeap 汎用 Box #include "../box/tiny_heap_box.h" // TinyHeap 汎用 Box
#include "../box/tiny_hotheap_v2_box.h" // TinyHotHeap v2 (Phase31 A/B) #include "../box/tiny_hotheap_v2_box.h" // TinyHotHeap v2 (Phase31 A/B)
#include "../box/smallobject_hotbox_v3_box.h" // SmallObject HotHeap v3 skeleton #include "../box/smallobject_hotbox_v3_box.h" // SmallObject HotHeap v3 skeleton
#include "../box/smallobject_hotbox_v4_box.h" // SmallObject HotHeap v4 (C7 stub)
#include "../box/tiny_front_v3_env_box.h" // Tiny front v3 snapshot gate #include "../box/tiny_front_v3_env_box.h" // Tiny front v3 snapshot gate
#include "../box/tiny_heap_env_box.h" // ENV gate for TinyHeap front (A/B) #include "../box/tiny_heap_env_box.h" // ENV gate for TinyHeap front (A/B)
#include "../box/tiny_route_env_box.h" // Route snapshot (Heap vs Legacy) #include "../box/tiny_route_env_box.h" // Route snapshot (Heap vs Legacy)
@ -132,7 +133,8 @@ static inline void* malloc_tiny_fast(size_t size) {
route_trusted = false; route_trusted = false;
} else if (!route_trusted && } else if (!route_trusted &&
route != TINY_ROUTE_LEGACY && route != TINY_ROUTE_HEAP && route != TINY_ROUTE_LEGACY && route != TINY_ROUTE_HEAP &&
route != TINY_ROUTE_HOTHEAP_V2 && route != TINY_ROUTE_SMALL_HEAP_V3) { route != TINY_ROUTE_HOTHEAP_V2 && route != TINY_ROUTE_SMALL_HEAP_V3 &&
route != TINY_ROUTE_SMALL_HEAP_V4) {
route = tiny_route_for_class((uint8_t)class_idx); route = tiny_route_for_class((uint8_t)class_idx);
} }
@ -148,6 +150,15 @@ static inline void* malloc_tiny_fast(size_t size) {
// fallthrough to v2/v1 // fallthrough to v2/v1
__attribute__((fallthrough)); __attribute__((fallthrough));
} }
case TINY_ROUTE_SMALL_HEAP_V4: {
void* v4p = small_heap_alloc_fast_v4(small_heap_ctx_v4_get(), class_idx);
if (TINY_HOT_LIKELY(v4p != NULL)) {
return v4p;
}
so_v3_record_alloc_fallback((uint8_t)class_idx);
// fallthrough to v2/v1
__attribute__((fallthrough));
}
case TINY_ROUTE_HOTHEAP_V2: { case TINY_ROUTE_HOTHEAP_V2: {
void* v2p = tiny_hotheap_v2_alloc((uint8_t)class_idx); void* v2p = tiny_hotheap_v2_alloc((uint8_t)class_idx);
if (TINY_HOT_LIKELY(v2p != NULL)) { if (TINY_HOT_LIKELY(v2p != NULL)) {
@ -307,6 +318,12 @@ static inline int free_tiny_fast(void* ptr) {
// Same-thread + TinyHeap route → route-based free // Same-thread + TinyHeap route → route-based free
if (__builtin_expect(use_tiny_heap, 0)) { if (__builtin_expect(use_tiny_heap, 0)) {
switch (route) { switch (route) {
case TINY_ROUTE_SMALL_HEAP_V4:
if (class_idx == 7) {
small_heap_free_fast_v4(small_heap_ctx_v4_get(), class_idx, base);
return 1;
}
__attribute__((fallthrough));
case TINY_ROUTE_SMALL_HEAP_V3: case TINY_ROUTE_SMALL_HEAP_V3:
so_free((uint32_t)class_idx, base); so_free((uint32_t)class_idx, base);
return 1; return 1;
@ -332,7 +349,7 @@ static inline int free_tiny_fast(void* ptr) {
// fallback: lookup failed but TinyHeap front is ON → use generic TinyHeap free // fallback: lookup failed but TinyHeap front is ON → use generic TinyHeap free
if (route == TINY_ROUTE_HOTHEAP_V2) { if (route == TINY_ROUTE_HOTHEAP_V2) {
tiny_hotheap_v2_record_free_fallback((uint8_t)class_idx); tiny_hotheap_v2_record_free_fallback((uint8_t)class_idx);
} else if (route == TINY_ROUTE_SMALL_HEAP_V3) { } else if (route == TINY_ROUTE_SMALL_HEAP_V3 || route == TINY_ROUTE_SMALL_HEAP_V4) {
so_v3_record_free_fallback((uint8_t)class_idx); so_v3_record_free_fallback((uint8_t)class_idx);
} }
tiny_heap_free_class_fast(tiny_heap_ctx_for_thread(), class_idx, ptr); tiny_heap_free_class_fast(tiny_heap_ctx_for_thread(), class_idx, ptr);

View File

@ -0,0 +1,304 @@
// smallobject_hotbox_v4.c - SmallObject HotHeap v4 (C7-only real path)
//
// Phase v4-3: C7 クラスについては v4 独自の freelist/current/partial で完結させる。
#include <stdlib.h>
#include <string.h>
#include "box/smallobject_hotbox_v4_box.h"
#include "box/smallobject_hotbox_v4_env_box.h"
#include "box/smallobject_cold_iface_v4.h"
#include "box/smallobject_hotbox_v3_env_box.h"
#include "box/tiny_heap_box.h"
#include "box/tiny_cold_iface_v1.h"
#include "box/tiny_geometry_box.h"
#include "tiny_region_id.h"
// TLS context
static __thread small_heap_ctx_v4 g_ctx_v4;
#define V4_MAX_PARTIAL_PAGES 2
small_heap_ctx_v4* small_heap_ctx_v4_get(void) {
return &g_ctx_v4;
}
// -----------------------------------------------------------------------------
// helpers
// -----------------------------------------------------------------------------
static inline void v4_page_push_partial(small_class_heap_v4* h, small_page_v4* page) {
if (!h || !page) return;
page->next = h->partial_head;
h->partial_head = page;
h->partial_count++;
}
static inline small_page_v4* v4_page_pop_partial(small_class_heap_v4* h) {
if (!h) return NULL;
small_page_v4* p = h->partial_head;
if (p) {
h->partial_head = p->next;
p->next = NULL;
if (h->partial_count > 0) {
h->partial_count--;
}
}
return p;
}
static inline void v4_page_push_full(small_class_heap_v4* h, small_page_v4* page) {
if (!h || !page) return;
page->next = h->full_head;
h->full_head = page;
}
static inline int v4_ptr_in_page(const small_page_v4* page, const uint8_t* ptr) {
if (!page || !ptr) return 0;
uint8_t* base = page->base;
size_t span = (size_t)page->block_size * (size_t)page->capacity;
if (ptr < base || ptr >= base + span) return 0;
size_t off = (size_t)(ptr - base);
return (off % page->block_size) == 0;
}
static inline void* v4_build_freelist(uint8_t* base, uint16_t capacity, size_t stride) {
void* head = NULL;
for (int i = capacity - 1; i >= 0; i--) {
uint8_t* blk = base + ((size_t)i * stride);
void* next = head;
head = blk;
memcpy(blk, &next, sizeof(void*));
}
return head;
}
typedef enum {
V4_LOC_NONE = 0,
V4_LOC_CURRENT,
V4_LOC_PARTIAL,
V4_LOC_FULL,
} v4_loc_t;
static small_page_v4* v4_find_page(small_class_heap_v4* h, const uint8_t* ptr, v4_loc_t* loc, small_page_v4** prev_out) {
if (loc) *loc = V4_LOC_NONE;
if (prev_out) *prev_out = NULL;
if (!h || !ptr) return NULL;
if (h->current && v4_ptr_in_page(h->current, ptr)) {
if (loc) *loc = V4_LOC_CURRENT;
return h->current;
}
small_page_v4* prev = NULL;
for (small_page_v4* p = h->partial_head; p; prev = p, p = p->next) {
if (v4_ptr_in_page(p, ptr)) {
if (loc) *loc = V4_LOC_PARTIAL;
if (prev_out) *prev_out = prev;
return p;
}
}
prev = NULL;
for (small_page_v4* p = h->full_head; p; prev = p, p = p->next) {
if (v4_ptr_in_page(p, ptr)) {
if (loc) *loc = V4_LOC_FULL;
if (prev_out) *prev_out = prev;
return p;
}
}
return NULL;
}
// -----------------------------------------------------------------------------
// Cold iface (C7-only, Tiny v1 経由)
// -----------------------------------------------------------------------------
static small_page_v4* cold_refill_page_v4(small_heap_ctx_v4* hot_ctx, uint32_t class_idx) {
if (__builtin_expect(class_idx != 7, 0)) return NULL;
(void)hot_ctx;
tiny_heap_ctx_t* tctx = tiny_heap_ctx_for_thread();
if (!tctx) return NULL;
tiny_heap_page_t* lease = tiny_heap_prepare_page(tctx, (int)class_idx);
if (!lease) return NULL;
small_page_v4* page = (small_page_v4*)malloc(sizeof(small_page_v4));
if (!page) {
return NULL;
}
memset(page, 0, sizeof(*page));
page->class_idx = (uint8_t)class_idx;
page->capacity = lease->capacity;
page->used = 0;
page->block_size = (uint32_t)tiny_stride_for_class((int)class_idx);
page->base = lease->base;
page->slab_ref = lease;
page->freelist = v4_build_freelist(lease->base, lease->capacity, page->block_size);
if (!page->freelist) {
free(page);
return NULL;
}
page->next = NULL;
page->flags = 0;
return page;
}
static void cold_retire_page_v4(small_heap_ctx_v4* hot_ctx, uint32_t class_idx, small_page_v4* page) {
(void)hot_ctx;
if (!page) return;
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, (int)class_idx, lease);
}
free(page);
}
static const SmallColdIfaceV4 g_cold_iface_v4 = {
.refill_page = cold_refill_page_v4,
.retire_page = cold_retire_page_v4,
.remote_push = NULL,
.remote_drain = NULL,
};
const SmallColdIfaceV4* small_cold_iface_v4_get(void) {
return &g_cold_iface_v4;
}
// -----------------------------------------------------------------------------
// alloc/free
// -----------------------------------------------------------------------------
static small_page_v4* small_alloc_slow_v4(small_heap_ctx_v4* ctx, int class_idx) {
small_class_heap_v4* h = &ctx->cls[class_idx];
small_page_v4* cur = h->current;
if (cur && cur->freelist) {
return cur; // usable current
}
if (cur && !cur->freelist) {
// current を full list に残しておき、free で戻す
v4_page_push_full(h, cur);
h->current = NULL;
}
// partial から 1 ページだけ復帰
small_page_v4* from_partial = v4_page_pop_partial(h);
if (from_partial) {
h->current = from_partial;
return from_partial;
}
const SmallColdIfaceV4* cold = small_cold_iface_v4_get();
if (!cold || !cold->refill_page) return NULL;
small_page_v4* page = cold->refill_page(ctx, (uint32_t)class_idx);
if (!page) return NULL;
h->current = page;
return page;
}
void* small_heap_alloc_fast_v4(small_heap_ctx_v4* ctx, int class_idx) {
if (__builtin_expect(class_idx != 7, 0)) {
return NULL; // C7 専用
}
if (!small_heap_v4_c7_enabled()) return NULL;
small_class_heap_v4* h = &ctx->cls[class_idx];
small_page_v4* page = h->current;
if (!page || !page->freelist) {
page = small_alloc_slow_v4(ctx, class_idx);
}
if (!page || !page->freelist) {
return NULL;
}
void* blk = page->freelist;
void* next = NULL;
memcpy(&next, blk, sizeof(void*));
page->freelist = next;
page->used++;
return tiny_region_id_write_header(blk, class_idx);
}
static void v4_unlink_from_list(small_class_heap_v4* h, v4_loc_t loc, small_page_v4* prev, small_page_v4* page) {
if (!h || !page) return;
switch (loc) {
case V4_LOC_CURRENT:
h->current = NULL;
break;
case V4_LOC_PARTIAL:
if (prev) prev->next = page->next;
else h->partial_head = page->next;
if (h->partial_count > 0) {
h->partial_count--;
}
break;
case V4_LOC_FULL:
if (prev) prev->next = page->next;
else h->full_head = page->next;
break;
default:
break;
}
page->next = NULL;
}
void small_heap_free_fast_v4(small_heap_ctx_v4* ctx, int class_idx, void* ptr) {
if (__builtin_expect(class_idx != 7, 0)) {
return;
}
if (!small_heap_v4_c7_enabled()) return;
if (!ptr) return;
small_class_heap_v4* h = &ctx->cls[class_idx];
small_page_v4* prev = NULL;
v4_loc_t loc = V4_LOC_NONE;
small_page_v4* page = v4_find_page(h, (const uint8_t*)ptr, &loc, &prev);
if (!page) return;
// freelist push
void* head = page->freelist;
memcpy(ptr, &head, sizeof(void*));
page->freelist = ptr;
if (page->used > 0) {
page->used--;
}
if (page->used == 0) {
const SmallColdIfaceV4* cold = small_cold_iface_v4_get();
if (loc != V4_LOC_CURRENT) {
v4_unlink_from_list(h, loc, prev, page);
}
if (!h->current) {
h->current = page;
page->next = NULL;
return;
}
if (h->current == page) {
page->next = NULL;
return;
}
if (h->partial_count < V4_MAX_PARTIAL_PAGES) {
v4_page_push_partial(h, page);
return;
}
if (cold && cold->retire_page) {
cold->retire_page(ctx, (uint32_t)class_idx, page);
} else {
free(page);
}
return;
}
if (!h->current) {
// このページを current に据える
if (loc != V4_LOC_CURRENT) {
v4_unlink_from_list(h, loc, prev, page);
}
h->current = page;
page->next = NULL;
} else if (loc == V4_LOC_FULL && page->freelist) {
// full → partial に戻す
v4_unlink_from_list(h, loc, prev, page);
v4_page_push_partial(h, page);
}
}

View File

@ -0,0 +1,40 @@
# SmallObject HotBox v4 Box Design (Phase v4-1 スケルトン)
## Overview
- 目的: 16〜1024B〜2KiB の small-object を統合する v4 の箱を用意し、v3 の成功パターンC7-onlyを一般化する足場を作る。
- 中期目標: mimalloc の 70〜80% に迫ること。現状は C7-only v3 + front v3 + fast classify で Mixed 161024B がまだ 30〜40% 程度。
- 位置付け: v3 = prototype、v2 = archiveインターフェース参考のみ。v4 は構造を整理しつつホット/コールド境界を明確にする。
## Box 構造
- **HotBox_v4**: per-thread `SmallHeapCtx``SmallClassHeap[current/partial/full]``SmallPageMeta` を持つ。ホットパスはここに閉じ込める。
- **ColdIface_v4**: `refill_page` / `retire_page` / `remote_push` / `remote_drain` を 1 箱に集約し、内部で Superslab / Warm / Remote を呼ぶ薄いラッパ。
- **SuperslabBox / RemoteBox**: 既存の Superslab/WarmPool/Remote を Cold 側の箱として再利用Hot から直接触らない)。
- **PolicyBox / LearningBox**: small-object 用の `SmallPolicySnapshot`block_size/route_kind 等を上位で更新し、Hot は snapshot を読むだけにする。
## Phase ロードマップ
- **v4-1**: 型と入口だけ追加。挙動は v3/v1 のままで、コンパイルが通る足場を用意。
- **v4-2**: C7-only を v4 に寄せ、v3 互換の挙動で動かすENV ゲート付き、v4 が優先)。
- **v4-3**: C7-only を v4 自前の freelist/current/partial で動かすCold は Tiny v1 経由。v3 はベンチ用に残し ENV で A/B。
- **v4-3.1 (今回)**: C7 v4 で current/partial 再利用を強化し、prepare_calls を v3 並みに抑制。C7-only ベンチで v4 が v3 比 +1% 程度まで回復。
- **v4-4**: C5〜C7 を含む全 small-object クラスを v4 に段階移行。route LUT から v4 を返せるようにする。
- **v4-5**: Segment/Page/Block レイアウトと pf 削減、WarmPool チューニングを v4 用に調整。
## 現行 v3/v2 の扱い
- v3: C7-only front v3 の prototype として構造だけ再利用する。性能・安定のベースライン。
- v2: archive として残し、インターフェース案のみ参照。ホットパスには混ぜない。
## 次ステップの入口
- `core/box/smallobject_hotbox_v4_box.h`: v4 のページ/クラス/TLS 型と TLS アクセサ宣言。
- `core/box/smallobject_cold_iface_v4.h`: ColdIface の関数ポインタ箱C7 専用 refill/retire を v1 Tiny に繋ぐ)。
- `core/box/tiny_route_env_box.h`: `TINY_ROUTE_SMALLHEAP_V4` を追加し、ENV `HAKMEM_SMALL_HEAP_V4_ENABLED` / `HAKMEM_SMALL_HEAP_V4_CLASSES` から C7 を v4 route に載せられる(未指定なら OFF
- `core/front/malloc_tiny_fast.h`: route switch に v4 の case を足し、C7 v4 が ON のときは v4 経路(現在は C7 自前 freelist, それ以外は v1/v3 へフォールバック、OFF 時は従来の v3/v1。
## A/B と運用
- Phase v4-3.1 時点の健康診断:
- C7-only A/B (ws=400, iters=1M, size=1024 固定):
- v3: 41.67M ops/s, prepare_calls=5,077
- v4: 42.13M ops/s, prepare_calls=4,701current/partial 再利用で 3.4x→約1.0x に改善)
- Mixed 161024B (MIXED_TINYV3_C7_SAFE, ws=400, iters=1M):
- v3 route: 40.66M ops/s
- v4 route: 40.01M ops/s-1.6% 以内、回帰なし)
- どちらも segv/assert なし。C7 v4 の prepare 増加は解消済み。Mixed ではまだ v3 がわずかに優勢だが許容範囲。