From 406835feb3bf4f3439a1aa29acda71fdb80f87c9 Mon Sep 17 00:00:00 2001 From: "Moe Charm (CI)" Date: Thu, 11 Dec 2025 23:07:26 +0900 Subject: [PATCH] =?UTF-8?q?Phase=20V6-HDR-0:=20C6-only=20headerless=20core?= =?UTF-8?q?=20=E8=A8=AD=E8=A8=88=E7=A2=BA=E5=AE=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - CURRENT_TASK.md: V6-HDR-0 セクション追加(4層 Box Theory) - SMALLOBJECT_CORE_V6_DESIGN.md: V6-HDR-0 設計方針追加 - REGIONID_V6_DESIGN.md: RegionIdBox 設計書新規作成 - smallobject_core_v6_box.h: SmallTlsLaneV6 型+TLS API 追加 - smallobject_core_v6.c: OBSERVE モード追加 - region_id_v6_box.h: RegionIdBox 型スケルトン - page_stats_v6_box.h: PageStatsV6 箱スケルトン - AGENTS.md: v6 研究箱ルールセクション追加 サニティベンチ: Mixed 42.1M, C6-heavy 25.0M(挙動不変確認) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- AGENTS.md | 47 +++++ CURRENT_TASK.md | 72 ++++++++ core/box/page_stats_v6_box.h | 98 ++++++++++ core/box/region_id_v6_box.h | 147 +++++++++++++++ core/box/smallobject_core_v6_box.h | 193 ++++++++++++++++++-- core/smallobject_core_v6.c | 38 +++- docs/analysis/REGIONID_V6_DESIGN.md | Bin 0 -> 5690 bytes docs/analysis/SMALLOBJECT_CORE_V6_DESIGN.md | 52 +++++- 8 files changed, 624 insertions(+), 23 deletions(-) create mode 100644 core/box/page_stats_v6_box.h create mode 100644 core/box/region_id_v6_box.h create mode 100644 docs/analysis/REGIONID_V6_DESIGN.md diff --git a/AGENTS.md b/AGENTS.md index 12dbf5c0..6b33fa74 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -263,3 +263,50 @@ Do / Don’t(壊れやすいパターンの禁止) 今後の大きい変更は **Headerless 系(v6 out-of-band header)/ mid/pool v3 新設計** といった独立ラインでのみ検討。 Tiny/ULTRA 層本体への追加最適化は Small patch level のみ(Phase SO-BACKEND-OPT-2 等の Header write 削減)を想定。 詳細は `docs/analysis/PERF_EXEC_SUMMARY_ULTRA_PHASE_20251211.md` 参照。 + +--- + +## v6 研究箱ルール(Phase V6-HDR-0 以降) + +### 基本方針 + +v6 は **C6-only headerless core** の研究ラインとして、Tiny/ULTRA 完成世代とは**独立して**運用する。 + +### 固定事項 + +1. **C7 ULTRA は触らない** + - TinyC7UltraBox / C7UltraSegmentBox は frozen。v6 から C7 を呼び出さない。 + - C7 の最適化が必要な場合は ULTRA 側で対応(v6 スコープ外)。 + +2. **v6 は C6-only** + - `HAKMEM_SMALL_CORE_V6_ENABLED=1` + `HAKMEM_SMALL_CORE_V6_CLASSES=0x40` (C6 のみ) + - C5/C4 への拡張は研究完了後に検討(Phase V6-HDR-1 以降) + +3. **Headerless 設計** + - alloc 時に header byte を書かない(`SMALL_V6_HEADERLESS=1`) + - free 時は RegionIdBox で ptr 分類 → page_meta へ直接アクセス + +4. **デフォルト OFF** + - `HAKMEM_SMALL_CORE_V6_ENABLED=0` がデフォルト + - 本線 Mixed / C6-heavy は v6 OFF で計測(回帰しない) + +### ENV 一覧 + +| ENV | Default | 説明 | +|-----|---------|------| +| `HAKMEM_SMALL_CORE_V6_ENABLED` | 0 | v6 core 有効化 | +| `HAKMEM_SMALL_CORE_V6_CLASSES` | 0x40 | 有効クラス (0x40=C6) | +| `HAKMEM_SMALL_V6_OBSERVE` | 0 | free 入口のログ出力 | +| `HAKMEM_PAGE_STATS_V6_ENABLED` | 0 | page lifetime stats | +| `HAKMEM_REGION_ID_V6_OBSERVE` | 0 | RegionIdBox lookup ログ | + +### 4層 Box Theory + +``` +L0: ULTRA lanes (C7 frozen) +L1: TLS Box (SmallTlsLaneV6 / SmallHeapCtxV6) +L2: Segment / ColdIface (SmallSegmentV6 / ColdIfaceV6) +L3: Policy / RegionIdBox / PageStatsV6 +``` + +詳細は `docs/analysis/SMALLOBJECT_CORE_V6_DESIGN.md` 参照。 diff --git a/CURRENT_TASK.md b/CURRENT_TASK.md index 196c341d..b013ff13 100644 --- a/CURRENT_TASK.md +++ b/CURRENT_TASK.md @@ -31,6 +31,78 @@ --- +## Phase V6-HDR-0: C6-only headerless core 設計確定(進行中) + +### 目的 + +Tiny/ULTRA 完成を受け、C6-only で **headerless 設計** を実証する最小コア(v6)を構築する。 +C7 ULTRA は既に完成・凍結されており、v6 は C6 専用の研究ラインとして独立させる。 + +### 4層 Box Theory(設計原則) + +``` +┌──────────────────────────────────────────────────────────────┐ +│ L0: ULTRA lanes (TinyC7UltraBox 等) │ +│ - C7 ULTRA は frozen / v6 とは独立 │ +│ - v6 ULTRA(将来)は C6-only で別途設計 │ +├──────────────────────────────────────────────────────────────┤ +│ L1: TLS Box (SmallTlsLaneV6 / SmallHeapCtxV6) │ +│ - per-class TLS freelist + current page ptr │ +│ - 責務: fast alloc/free(header 書き込みなし) │ +├──────────────────────────────────────────────────────────────┤ +│ L2: Segment / ColdIface (SmallSegmentV6 / ColdIfaceV6) │ +│ - page_meta[], segment base/end 管理 │ +│ - refill / retire の page lifecycle 管理 │ +├──────────────────────────────────────────────────────────────┤ +│ L3: Policy / RegionIdBox / Stats │ +│ - RegionIdBox: ptr→(region_kind, region_id, page_meta) │ +│ - PageStatsV6: page lifetime summary(占有率、retire 頻度)│ +│ - Policy: GC / compaction 決定(将来) │ +└──────────────────────────────────────────────────────────────┘ +``` + +### 設計ポイント + +1. **C7 ULTRA は独立 frozen 箱** + - TinyC7UltraBox / C7UltraSegmentBox はそのまま維持 + - v6 は C7 に触らない(C6-only) + +2. **v6 は C6-only small core(headerless 研究)** + - alloc 時に header byte を書かない(out-of-band metadata) + - free 時は RegionIdBox で ptr 分類 → page_meta へ直接アクセス + +3. **ptr 分類は RegionIdBox に集約** + - 従来: classify_ptr / hak_super_lookup / ss_fast_lookup など分散 + - v6: `region_id_lookup_v6(ptr)` で (region_kind, region_id, page_meta*) を返す + - region_kind: SMALL_V6 / POOL / LARGE / UNKNOWN + +4. **Stats/Learning は page lifetime summary のみを L3 に渡す** + - L1/L2 で個別 block の stats は取らない + - page retire 時に summary (total_allocs, avg_lifetime_ns) を L3 へ push + +### 実装タスク(本フェーズ) + +| No | タスク | 状態 | +|----|--------|------| +| 1-1 | CURRENT_TASK.md 整理(本セクション追加)| ✅ | +| 1-2 | SMALLOBJECT_CORE_V6_DESIGN.md 新規作成 | pending | +| 1-3 | REGIONID_V6_DESIGN.md 新規作成 | pending | +| 2-1 | SmallTlsLaneV6 / SmallHeapCtxV6 型スケルトン | pending | +| 2-2 | v6 TLS API (small_v6_tls_alloc/free) | pending | +| 3-1 | RegionIdBox 型と lookup API スケルトン | pending | +| 3-2 | OBSERVE モード(v6 free 入口にログ)| pending | +| 4-1 | PageStatsV6 箱(未接続)| pending | +| 5-1 | AGENTS.md に v6 研究箱ルール追記 | pending | +| 5-2 | サニティベンチ(Mixed / C6-heavy)| pending | + +### ENV(予定) + +- `HAKMEM_SMALL_CORE_V6_ENABLED=0` (default OFF) +- `HAKMEM_REGION_ID_V6_OBSERVE=0` (default OFF, ログ出力用) +- `HAKMEM_PAGE_STATS_V6_ENABLED=0` (default OFF) + +--- + --- ### 1. ベースライン(1 thread, ws=400, iters=1M, seed=1) diff --git a/core/box/page_stats_v6_box.h b/core/box/page_stats_v6_box.h new file mode 100644 index 00000000..84711ed4 --- /dev/null +++ b/core/box/page_stats_v6_box.h @@ -0,0 +1,98 @@ +// page_stats_v6_box.h - Page Lifetime Stats for v6 (V6-HDR-0) +// +// Purpose: Collect page lifetime summary stats +// - L2 (ColdIface) pushes stats on page retire +// - L3 (Policy) aggregates for decision making +// +// Note: This box is NOT connected to hot path (no per-block stats) + +#ifndef HAKMEM_PAGE_STATS_V6_BOX_H +#define HAKMEM_PAGE_STATS_V6_BOX_H + +#include +#include + +// ============================================================================ +// Page Lifetime Summary (pushed from L2 on retire) +// ============================================================================ + +/// Summary of a single page's lifetime (passed to L3 on retire) +typedef struct PageLifetimeSummaryV6 { + uint8_t class_idx; // Size class (C4=4, C5=5, C6=6) + uint32_t total_allocs; // Total allocs from this page + uint32_t total_frees; // Total frees to this page + uint32_t remote_frees; // Cross-thread frees + uint64_t lifetime_ns; // Time from carve to retire (nanoseconds) + uint64_t peak_used; // Max used during lifetime +} PageLifetimeSummaryV6; + +// ============================================================================ +// Aggregated Stats (L3 accumulator) +// ============================================================================ + +/// Aggregated stats per class (atomics for thread safety) +typedef struct PageStatsClassV6 { + _Atomic uint64_t pages_created; // Total pages created + _Atomic uint64_t pages_retired; // Total pages retired + _Atomic uint64_t total_allocs; // Sum of allocs across all pages + _Atomic uint64_t total_frees; // Sum of frees across all pages + _Atomic uint64_t remote_frees; // Sum of remote frees + _Atomic uint64_t sum_lifetime_ns; // Sum of lifetimes (for avg) + _Atomic uint64_t max_lifetime_ns; // Max lifetime seen + _Atomic uint64_t sum_peak_used; // Sum of peak_used (for avg utilization) +} PageStatsClassV6; + +/// Global stats box +typedef struct PageStatsV6 { + PageStatsClassV6 by_class[8]; // Stats per class (indexed by class_idx) + _Atomic uint64_t total_pages; // Total pages across all classes + _Atomic uint64_t total_retires; // Total retires across all classes +} PageStatsV6; + +// ============================================================================ +// API +// ============================================================================ + +/// Get global PageStatsV6 instance +PageStatsV6* page_stats_v6_get(void); + +/// Record a page retire event (called by L2 ColdIface) +/// @param summary: page lifetime summary +void page_stats_v6_record_retire(const PageLifetimeSummaryV6* summary); + +/// Record a page creation event (called by L2 ColdIface) +/// @param class_idx: size class +void page_stats_v6_record_create(uint8_t class_idx); + +/// Dump stats to stderr (for debug) +void page_stats_v6_dump(void); + +/// Reset all stats to zero +void page_stats_v6_reset(void); + +// ============================================================================ +// ENV Control +// ============================================================================ + +/// ENV: HAKMEM_PAGE_STATS_V6_ENABLED (default: 0) +/// When enabled, L2 calls page_stats_v6_record_*() on create/retire + +/// Check if page stats collection is enabled +int page_stats_v6_enabled(void); + +// ============================================================================ +// Computed Metrics +// ============================================================================ + +/// Get average page lifetime for a class (returns 0 if no data) +uint64_t page_stats_v6_avg_lifetime_ns(uint8_t class_idx); + +/// Get average peak utilization for a class (returns 0 if no data) +/// @return: percentage (0-100) +uint32_t page_stats_v6_avg_utilization(uint8_t class_idx); + +/// Get remote free ratio for a class (returns 0 if no data) +/// @return: percentage (0-100) +uint32_t page_stats_v6_remote_free_ratio(uint8_t class_idx); + +#endif // HAKMEM_PAGE_STATS_V6_BOX_H diff --git a/core/box/region_id_v6_box.h b/core/box/region_id_v6_box.h new file mode 100644 index 00000000..15ce4d52 --- /dev/null +++ b/core/box/region_id_v6_box.h @@ -0,0 +1,147 @@ +// region_id_v6_box.h - RegionIdBox for ptr->region lookup (V6-HDR-0) +// +// Purpose: Centralized ptr classification for headerless design +// - Replaces scattered classify_ptr / hak_super_lookup / ss_fast_lookup +// - Provides O(1) or O(log n) lookup from ptr to region info + +#ifndef HAKMEM_REGION_ID_V6_BOX_H +#define HAKMEM_REGION_ID_V6_BOX_H + +#include +#include +#include + +// ============================================================================ +// Region Kind Enum +// ============================================================================ + +typedef enum { + REGION_KIND_UNKNOWN = 0, // Unknown / not registered + REGION_KIND_SMALL_V6, // SmallObject v6 segment + REGION_KIND_C7_ULTRA, // C7 ULTRA segment (frozen) + REGION_KIND_POOL_V1, // Pool v1 region + REGION_KIND_LARGE, // Large mmap allocation + REGION_KIND_TINY_LEGACY, // Legacy tiny heap + REGION_KIND_MAX +} region_kind_t; + +// ============================================================================ +// Lookup Result Structure +// ============================================================================ + +/// Result of region_id_lookup_v6() +typedef struct RegionLookupV6 { + region_kind_t kind; // Region kind + uint32_t region_id; // Registry ID (unique per registration) + void* page_meta; // Kind-specific metadata (nullable) + // - SMALL_V6: SmallPageMetaV6* + // - C7_ULTRA: UltraSegment* + // - POOL_V1: pool_desc* + // - others: NULL +} RegionLookupV6; + +// ============================================================================ +// RegionIdBox Opaque Structure +// ============================================================================ + +typedef struct RegionIdBox RegionIdBox; + +// ============================================================================ +// Global Instance +// ============================================================================ + +/// Get the global RegionIdBox instance +RegionIdBox* region_id_box_get(void); + +// ============================================================================ +// Lookup API (Hot Path) +// ============================================================================ + +/// Lookup region info for a pointer +/// @param ptr: pointer to lookup (USER pointer) +/// @return: RegionLookupV6 with kind, region_id, and page_meta +/// @note: Returns REGION_KIND_UNKNOWN if not found +RegionLookupV6 region_id_lookup_v6(void* ptr); + +/// Fast lookup with TLS cache +/// @param ptr: pointer to lookup +/// @return: same as region_id_lookup_v6, but uses TLS cache +RegionLookupV6 region_id_lookup_cached_v6(void* ptr); + +// ============================================================================ +// Registration API (Cold Path) +// ============================================================================ + +/// Register a region with the box +/// @param base: region base address +/// @param size: region size in bytes +/// @param kind: region kind +/// @param metadata: kind-specific metadata (stored as page_meta in lookup result) +/// @return: unique region_id (0 on failure) +uint32_t region_id_register_v6(void* base, size_t size, region_kind_t kind, void* metadata); + +/// Unregister a region +/// @param region_id: ID returned from register +void region_id_unregister_v6(uint32_t region_id); + +// ============================================================================ +// Utility API +// ============================================================================ + +/// Check if region_id is valid +bool region_id_is_valid_v6(uint32_t region_id); + +/// Get region kind as string (for debug) +const char* region_kind_to_string(region_kind_t kind); + +/// Dump all registered regions (debug) +void region_id_box_dump(void); + +// ============================================================================ +// OBSERVE Mode (Debug) +// ============================================================================ + +/// ENV: HAKMEM_REGION_ID_V6_OBSERVE (default: 0) +/// When enabled, logs lookup/register/unregister operations + +/// Check if observe mode is enabled +bool region_id_observe_enabled(void); + +/// Log a lookup operation (called internally when observe=1) +void region_id_observe_lookup(void* ptr, const RegionLookupV6* result); + +/// Log a registration (called internally when observe=1) +void region_id_observe_register(void* base, size_t size, region_kind_t kind, uint32_t id); + +/// Log an unregistration (called internally when observe=1) +void region_id_observe_unregister(uint32_t id); + +// ============================================================================ +// Inline Fast Path (TLS Cache) +// ============================================================================ + +/// TLS cache for fast lookup (single entry) +typedef struct RegionIdTlsCache { + uintptr_t last_base; // Cached region base + uintptr_t last_end; // Cached region end + RegionLookupV6 last_result; // Cached result +} RegionIdTlsCache; + +/// Get TLS cache for current thread +RegionIdTlsCache* region_id_tls_cache_get(void); + +/// Inline fast lookup with TLS cache hit +static inline RegionLookupV6 region_id_lookup_fast_v6(void* ptr) { + RegionIdTlsCache* cache = region_id_tls_cache_get(); + uintptr_t addr = (uintptr_t)ptr; + + // TLS cache hit? + if (addr >= cache->last_base && addr < cache->last_end) { + return cache->last_result; + } + + // Cache miss -> slow path + return region_id_lookup_v6(ptr); +} + +#endif // HAKMEM_REGION_ID_V6_BOX_H diff --git a/core/box/smallobject_core_v6_box.h b/core/box/smallobject_core_v6_box.h index b84176b5..730eceb1 100644 --- a/core/box/smallobject_core_v6_box.h +++ b/core/box/smallobject_core_v6_box.h @@ -1,6 +1,9 @@ -// smallobject_core_v6_box.h - SmallObject Core v6 型定義(Phase v6-3) +// smallobject_core_v6_box.h - SmallObject Core v6 型定義 // -// v6-3: C6-only CORE with TLS ownership check + batch header writes + TLS batching +// Phase V6-HDR-0: C6-only headerless core design +// - SmallTlsLaneV6: per-class TLS freelist +// - SmallHeapCtxV6: per-thread context with lanes +// - Headerless: no header byte write on alloc #ifndef HAKMEM_SMALLOBJECT_CORE_V6_BOX_H #define HAKMEM_SMALLOBJECT_CORE_V6_BOX_H @@ -8,41 +11,107 @@ #include #include -// TLS freelist capacity +// ============================================================================ +// Constants +// ============================================================================ + +// TLS freelist capacity per lane #define SMALL_V6_TLS_CAP 32 -// Header byte format (from tiny_region_id.h pattern) -#define SMALL_V6_HEADER_MAGIC 0xA0 -#define SMALL_V6_HEADER_CLASS_MASK 0x0F +// Number of classes supported by v6 (C4, C5, C6) +#define NUM_SMALL_CLASSES_V6 3 +#define SMALL_V6_CLASS_C4 0 +#define SMALL_V6_CLASS_C5 1 +#define SMALL_V6_CLASS_C6 2 -// Pointer conversion macros (BASE vs USER pointer) +// Class index mapping (from global class index to v6 lane index) +#define SMALL_V6_LANE_FROM_CLASS(ci) ((ci) - 4) // C4=4->0, C5=5->1, C6=6->2 +#define SMALL_V6_CLASS_FROM_LANE(li) ((li) + 4) // 0->C4=4, 1->C5=5, 2->C6=6 + +// ============================================================================ +// Headerless Mode (V6-HDR-0) +// ============================================================================ + +// ENV: HAKMEM_SMALL_CORE_V6_HEADERLESS (default: 0 for backward compat) +// When headerless=1: BASE == USER (no offset) +// When headerless=0: BASE = USER - 1 (legacy 1-byte header) + +#ifndef SMALL_V6_HEADERLESS +#define SMALL_V6_HEADERLESS 0 // Default: legacy mode with header +#endif + +#if SMALL_V6_HEADERLESS +// Headerless mode: BASE == USER +#define SMALL_V6_BASE_FROM_USER(ptr) (ptr) +#define SMALL_V6_USER_FROM_BASE(ptr) (ptr) +#else +// Legacy mode: 1-byte header offset #define SMALL_V6_BASE_FROM_USER(ptr) ((uint8_t*)(ptr) - 1) #define SMALL_V6_USER_FROM_BASE(ptr) ((uint8_t*)(ptr) + 1) +#endif -// Header byte operations +// Header byte format (legacy mode only) +#define SMALL_V6_HEADER_MAGIC 0xA0 +#define SMALL_V6_HEADER_CLASS_MASK 0x0F #define SMALL_V6_HEADER_FROM_CLASS(class_idx) ((uint8_t)(SMALL_V6_HEADER_MAGIC | ((class_idx) & SMALL_V6_HEADER_CLASS_MASK))) // Forward declarations typedef struct SmallHeapCtxV6 SmallHeapCtxV6; typedef struct SmallPolicySnapshotV6 SmallPolicySnapshotV6; +typedef struct SmallTlsLaneV6 SmallTlsLaneV6; +typedef struct SmallSegmentV6 SmallSegmentV6; -// SmallHeapCtxV6 - TLS context for v6 +// ============================================================================ +// SmallTlsLaneV6 - Per-class TLS freelist (V6-HDR-0) +// ============================================================================ + +/// Per-class TLS lane for fast alloc/free +/// - freelist: singly-linked list of free blocks +/// - count: current number of blocks in freelist +/// - capacity: max blocks to cache (default: SMALL_V6_TLS_CAP) +/// - current_page: hint for refill source (optional) +struct SmallTlsLaneV6 { + void* freelist[SMALL_V6_TLS_CAP]; // TLS freelist (array for cache locality) + uint32_t count; // current count (hot field first) + uint32_t capacity; // max capacity + void* current_page; // current refill page (SmallPageMetaV6*) +}; + +// ============================================================================ +// SmallHeapCtxV6 - Per-thread heap context (V6-HDR-0) +// ============================================================================ + +/// Per-thread context containing lanes for each class +/// - lanes[]: one SmallTlsLaneV6 per class (C4, C5, C6) +/// - enabled_mask: bit mask of enabled classes +/// - segment: current active segment for this thread struct SmallHeapCtxV6 { - // C6 TLS freelist - void* tls_freelist_c6[SMALL_V6_TLS_CAP]; - uint8_t tls_count_c6; + // Per-class TLS lanes (V6-HDR-0 style) + SmallTlsLaneV6 lanes[NUM_SMALL_CLASSES_V6]; - // C5 TLS freelist (Phase v6-5) - void* tls_freelist_c5[SMALL_V6_TLS_CAP]; - uint8_t tls_count_c5; + // Enabled class mask (bit 0=C4, bit 1=C5, bit 2=C6) + uint8_t enabled_mask; - // C4 TLS freelist (Phase v6-6) - void* tls_freelist_c4[SMALL_V6_TLS_CAP]; - uint8_t tls_count_c4; + // Initialization flag + uint8_t initialized; // TLS segment ownership (for fast check) uintptr_t tls_seg_base; uintptr_t tls_seg_end; + + // Current segment reference (L2) + SmallSegmentV6* segment; + + // ========================================= + // Legacy fields (backward compat, Phase v6-6) + // TODO: Remove after full migration to lanes[] + // ========================================= + void* tls_freelist_c6[SMALL_V6_TLS_CAP]; + uint8_t tls_count_c6; + void* tls_freelist_c5[SMALL_V6_TLS_CAP]; + uint8_t tls_count_c5; + void* tls_freelist_c4[SMALL_V6_TLS_CAP]; + uint8_t tls_count_c4; }; // SmallPolicySnapshotV6 - Policy snapshot (v6-2: minimal) @@ -154,7 +223,10 @@ void* small_alloc_cold_v6(uint32_t class_idx, SmallHeapCtxV6* ctx); /// Cold path: free to page freelist (called when TLS full or cross-thread) void small_free_cold_v6(void* ptr, uint32_t class_idx); -// API +// ============================================================================ +// Legacy API (backward compat) +// ============================================================================ + SmallHeapCtxV6* small_heap_ctx_v6(void); void* small_alloc_fast_v6(size_t size, @@ -169,4 +241,87 @@ void small_free_fast_v6(void* ptr, const SmallPolicySnapshotV6* tiny_policy_snapshot_v6(void); +// ============================================================================ +// V6-HDR-0 TLS Lane API (Headerless Design) +// ============================================================================ + +/// Get SmallHeapCtxV6 for current thread (TLS accessor) +SmallHeapCtxV6* small_v6_get_ctx(void); + +/// Initialize lanes in ctx (called on first use) +void small_v6_ctx_init(SmallHeapCtxV6* ctx); + +// ---------------------------------------------------------------------------- +// Generic TLS Lane Operations (use SmallTlsLaneV6) +// ---------------------------------------------------------------------------- + +/// Alloc from TLS lane (fast path) +/// @param ctx: TLS context +/// @param lane_idx: lane index (0=C4, 1=C5, 2=C6) +/// @return: USER pointer or NULL (need refill) +static inline void* small_v6_tls_alloc(SmallHeapCtxV6* ctx, uint8_t lane_idx) { + SmallTlsLaneV6* lane = &ctx->lanes[lane_idx]; + if (likely(lane->count > 0)) { + void* blk = lane->freelist[--lane->count]; + return SMALL_V6_USER_FROM_BASE(blk); + } + return NULL; // Need refill from cold path +} + +/// Free to TLS lane (fast path, assumes TLS ownership already checked) +/// @param ctx: TLS context +/// @param ptr: USER pointer to free +/// @param lane_idx: lane index (0=C4, 1=C5, 2=C6) +/// @return: 1 if handled, 0 if lane full (need cold path) +static inline int small_v6_tls_free(SmallHeapCtxV6* ctx, void* ptr, uint8_t lane_idx) { + SmallTlsLaneV6* lane = &ctx->lanes[lane_idx]; + if (lane->count < lane->capacity) { + void* base = SMALL_V6_BASE_FROM_USER(ptr); + lane->freelist[lane->count++] = base; + return 1; + } + return 0; // Lane full, need cold path +} + +/// Check if ptr is owned by TLS segment (fast ownership check) +/// @param ctx: TLS context +/// @param ptr: USER pointer to check +/// @return: 1 if owned, 0 if not +static inline int small_v6_tls_owns(SmallHeapCtxV6* ctx, void* ptr) { + uintptr_t addr = (uintptr_t)ptr; + return addr >= ctx->tls_seg_base && addr < ctx->tls_seg_end; +} + +// ---------------------------------------------------------------------------- +// Cold Path (L1 -> L2) +// ---------------------------------------------------------------------------- + +/// Refill a TLS lane from cold path (L2 ColdIface) +/// @param ctx: TLS context +/// @param lane_idx: lane index to refill +/// @return: number of blocks refilled +int small_v6_lane_refill(SmallHeapCtxV6* ctx, uint8_t lane_idx); + +/// Drain a TLS lane to cold path (L2 ColdIface) +/// @param ctx: TLS context +/// @param lane_idx: lane index to drain +/// @param count: number of blocks to drain (0 = all) +void small_v6_lane_drain(SmallHeapCtxV6* ctx, uint8_t lane_idx, uint32_t count); + +// ---------------------------------------------------------------------------- +// Combined Alloc/Free API (V6-HDR-0 style) +// ---------------------------------------------------------------------------- + +/// Alloc with automatic refill +/// @param ctx: TLS context +/// @param lane_idx: lane index (0=C4, 1=C5, 2=C6) +/// @return: USER pointer or NULL on failure +void* small_v6_alloc(SmallHeapCtxV6* ctx, uint8_t lane_idx); + +/// Free with automatic cold path fallback +/// @param ctx: TLS context +/// @param ptr: USER pointer to free +/// @param lane_idx: lane index (0=C4, 1=C5, 2=C6) +void small_v6_free(SmallHeapCtxV6* ctx, void* ptr, uint8_t lane_idx); + #endif // HAKMEM_SMALLOBJECT_CORE_V6_BOX_H diff --git a/core/smallobject_core_v6.c b/core/smallobject_core_v6.c index 076122a4..cb7b0858 100644 --- a/core/smallobject_core_v6.c +++ b/core/smallobject_core_v6.c @@ -1,7 +1,10 @@ -// smallobject_core_v6.c - SmallObject Core v6 実装(Phase v6-3) +// smallobject_core_v6.c - SmallObject Core v6 実装 +// +// Phase V6-HDR-0: C6-only headerless core with OBSERVE mode #include #include +#include #include "box/smallobject_core_v6_box.h" #include "box/smallobject_cold_iface_v6.h" #include "box/smallsegment_v6_box.h" @@ -12,6 +15,31 @@ #define unlikely(x) __builtin_expect(!!(x), 0) #endif +// ============================================================================ +// OBSERVE Mode (V6-HDR-0) +// ============================================================================ +// ENV: HAKMEM_SMALL_V6_OBSERVE=1 enables logging at free entry + +#define V6_OBSERVE_UNINIT (-1) +#define V6_OBSERVE_OFF 0 +#define V6_OBSERVE_ON 1 + +static int g_v6_observe = V6_OBSERVE_UNINIT; + +static inline int small_v6_observe_enabled(void) { + if (unlikely(g_v6_observe == V6_OBSERVE_UNINIT)) { + const char* env = getenv("HAKMEM_SMALL_V6_OBSERVE"); + g_v6_observe = (env && env[0] == '1') ? V6_OBSERVE_ON : V6_OBSERVE_OFF; + } + return g_v6_observe == V6_OBSERVE_ON; +} + +/// Log free entry (called when OBSERVE=1) +static void small_v6_observe_free(void* ptr, uint32_t class_idx, int tls_owned) { + fprintf(stderr, "[V6_OBSERVE] free ptr=%p class=%u tls_owned=%d\n", + ptr, class_idx, tls_owned); +} + // TLS context static __thread struct SmallHeapCtxV6 g_small_heap_ctx_v6; static __thread int g_small_heap_ctx_v6_init = 0; @@ -250,8 +278,14 @@ void small_free_fast_v6(void* ptr, // Convert USER pointer to BASE pointer void* base = SMALL_V6_BASE_FROM_USER(ptr); + // V6-HDR-0: OBSERVE mode logging (check TLS ownership first for log) + int tls_owned = small_tls_owns_ptr_v6(ctx, ptr); + if (unlikely(small_v6_observe_enabled())) { + small_v6_observe_free(ptr, class_idx, tls_owned); + } + // Fast path: TLS segment ownership + TLS push - if (likely(small_tls_owns_ptr_v6(ctx, ptr))) { + if (likely(tls_owned)) { // C6 TLS push if (class_idx == SMALL_V6_C6_CLASS_IDX && ctx->tls_count_c6 < SMALL_V6_TLS_CAP) { ctx->tls_freelist_c6[ctx->tls_count_c6++] = base; diff --git a/docs/analysis/REGIONID_V6_DESIGN.md b/docs/analysis/REGIONID_V6_DESIGN.md new file mode 100644 index 0000000000000000000000000000000000000000..23890a6d605cae68774bfef8ae5531348875952e GIT binary patch literal 5690 zcmb7IU2I!t8BWFs&JnG4fqKKFzM|W3tk{knmWq@x{MRP!i5=N#($Z}wC-$*@V*4D= z=QvIJqX^U?Rjm;6E-I}|n-BtoR#7e}>J@^ju&PLG5|nWexjQno3*ozTfwq zKRc~Pq$KwFzW48W|IZ#MYBk+7<|=7(1JZ-+_E=@&ev&HBHJ}cR(-L>dyRXQke)gHyd#?NISsFA29Si2myLonEr@thh*exYvaCtckD8wwQ zw%LIPZ>1Z+{L@zLIv6A0lP}DklO|&#fM9jg)}Zmm5oB5Gs+|hR6tuqRs(qHIm#HIu%=d1%bP$EM=ohgr-fXlfibDr`y>}-jD;@3z}+LWbYGehu=9!_Xn;uf zk)`YhrHF!I#9n6Rbalg?Oe!`%7qmJj5w}_3y=uT*mQk#mdL<(GffvI}2vU&1sMXfW zdQ~q2#eGG!RR|m1CZ=K&6ZdqV-e?!n#l5Kd$=!E8{NRUo-;w(4bF~xY2@@*JwV`6N zk7p588=^z4Rht~(#VATC@dS=@&Tujrl9QMOB5mQtr_zm6>!Xs3iYJmjSvJ;66zaoa z6>Iu9`)=tcG{lkq@dtvIJCEVQpL3O6WLVe)Q45!-atK|QrXu5@+2z=ngfe;WnMX4k-aKq|SuW%`xXwp!Nh&F#(%ZNpYD zwHaC4u6J;#P+DDE+d#mvg)|(j1#)f%nY~-Ct;`%>s`26jL;5a>`c@>eD3>6kmg{WN z`S*KHr&#!Y59p|I>Gri>{+(JE4nI5@A=Sjmhx#fTndbF0)=zwbP&j$Hb{1Ny)qv22 z+WWf&OKNM8h^(I24CFiUaL6c~tsgfq%X9x(TOJi*i019b83#+3U7u!3l}TTy>$Zfz z+gP>~g~Nl6V1<^u&x1CN`4kqc zY6F=;L4j>F>?$+H^k7a!%LrrFoEO+ry$WGQ_4S!PWnw;6V?5@bdydgiFfp31J&ptu zZcHdh1Rc%pc8tdmaJWuOj$q;0>wpb|mXsJZKF88B_EPXqjm{)E?HY$bxKlIQ&36X| z#jOkOCFZOJ9J}Xn`XFx+3FS=x!I)6OW$6LrqR7rDEW-4OyT-e=ibb>`EE`bYnMMQZ zyYpz{kDU-7Md9b3_k#Ndm&?E-?C9sQ62%A`5a`UXD9=TR)+Tz6iwws5dO|peO9;xq zpPg20_aG@Qrxf>; zo-ZIN77+T%a!|0`@hx&4ig5EIxH5~Z=&|t+o`s5TVcRL!MaA+oGe7TgNE_gQx1+vX zHMY(jm7*lvX=`Qx0&H)?LQL(*vmZZb_~F82=76H{#xK5m=HM4u&2#Kcep-9cNCd-= zJa$XMg!0#CSEwHsKMAWJJ!j8xY;;I2VO^oO1KifQw^Ms-m|U+0bfC_@h4=lNldv}G~1fw{np91=H3g0QQ+{I6C?}Ss*DeREZ zDF5sGga@!Pn>sX~n^#2joy+3>t>p5lw4BSXAbkRV{WgzY-4()!EaV|=UJs2vl0_k1 z$`zM##9{(}1ypI{qs#X$l1AGL&ZIK4Ii)oBO-4wvDLMF9L(5?sN!?duLInKn{tZd` z-MiN=ha1~-6&Gg`D^R<4kuN3EG1uak9PO}K%}i|Ut;S+87Tz;X8}eG)Od>fMAnl0l z5NIECsc9FSNltuqZ{qo6A1l3rJiZd(;q7d|PA7w7&70RFvp*u=;y*-0d4PXL5F6oE z&$Gh8gzf@?esA2d3|C?Nj(%g|!`=}!u7{W(*iB!J;RcBnn$i5}MfYydBd%ND-@S|T zN}?3E&-HP7B_y;7J-lMxxmo-6$1Hjlh{kDEpORz}NgVID%!-Czng9LK@te