Files
hakmem/core/smallobject_stats_mid_v3.c

209 lines
7.0 KiB
C
Raw Permalink Normal View History

Phase v11a-2: Core MID v3.5 implementation - segment, cold iface, stats, learner Implement 5-layer infrastructure for multi-class MID v3.5 (C5-C7, 257-1KiB): 1. SegmentBox_mid_v3 (L2 Physical) - core/smallobject_segment_mid_v3.c (9.5 KB) - 2MiB segments, 64KiB pages (32 per segment) - Per-class free page stacks (LIFO) - RegionIdBox registration - Slots: C5→170, C6→102, C7→64 2. ColdIface_mid_v3 (L2→L1) - core/box/smallobject_cold_iface_mid_v3_box.h (NEW) - core/smallobject_cold_iface_mid_v3.c (3.5 KB) - refill: get page from free stack or new segment - retire: calculate free_hit_ratio, publish stats, return to stack - Clean separation: TLS cache for hot path, ColdIface for cold path 3. StatsBox_mid_v3 (L2→L3) - core/smallobject_stats_mid_v3.c (7.2 KB) - Circular buffer history (1000 events) - Per-page metrics: class_idx, allocs, frees, free_hit_ratio_bps - Periodic aggregation (every 100 retires) - Learner notification callback 4. Learner v2 (L3) - core/smallobject_learner_v2.c (11 KB) - Multi-class aggregation: allocs[8], retire_count[8], avg_free_hit_bps[8] - Exponential smoothing (90% history + 10% new) - Per-class efficiency tracking - Stats snapshot API - Route decision disabled for v11a-2 (v11b feature) 5. Build Integration - Modified Makefile: added 4 new .o files (segment, cold_iface, stats, learner) - Updated box header prototypes - Clean compilation, all dependencies resolved Architecture Decision Implementation: - v7 remains frozen (C5/C6 research preset) - MID v3.5 becomes unified 257-1KiB main path - Multi-class isolation: per-class free stacks - Dormant infrastructure: linked but not active (zero overhead) Performance: - Build: clean compilation - Sanity benchmark: 27.3M ops/s (no regression vs v10) - Memory: ~30MB RSS (baseline maintained) Design Compliance: ✅ Layer separation: L2 (segment) → L2 (cold iface) → L3 (stats) → L3 (learner) ✅ Hot path clean: alloc/free never touch stats/learner ✅ Backward compatible: existing MID v3 routes unchanged ✅ Transparent: v11a-2 is dormant (no behavior change) Next Phase (v11a-3): - Activate C5/C6/C7 routing through MID v3.5 - Connect TLS cache to segment refill - Verify performance under load - Then Phase v11a-4: dynamic C5 ratio routing 🤖 Generated with Claude Code Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2025-12-12 06:37:06 +09:00
// smallobject_stats_mid_v3.c
// Phase v11a-2: Stats collection for MID v3.5 page lifetime tracking
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#include "box/smallobject_stats_mid_v3_box.h"
#include "box/smallobject_learner_v2_box.h"
// ============================================================================
// Helper: Get timestamp
// ============================================================================
static inline uint64_t get_timestamp_ns(void) {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (uint64_t)ts.tv_sec * 1000000000ULL + (uint64_t)ts.tv_nsec;
}
static inline uint64_t get_timestamp_ms(void) {
return get_timestamp_ns() / 1000000ULL;
}
// ============================================================================
// Stats Context
// ============================================================================
typedef struct {
SmallPageStatsPublished_MID_v3 history[SMALL_STATS_HISTORY_SIZE];
uint64_t total_published;
uint32_t current_idx;
uint32_t eval_interval;
bool enabled;
} SmallStatsContext_MID_v3;
static SmallStatsContext_MID_v3 g_stats_ctx = {
.total_published = 0,
.current_idx = 0,
.eval_interval = SMALL_STATS_EVAL_INTERVAL,
.enabled = true
};
// ============================================================================
// Stats Publishing
// ============================================================================
void small_stats_mid_v3_publish(const SmallPageStatsMID_v3 *stat) {
if (!stat || !g_stats_ctx.enabled) return;
SmallStatsContext_MID_v3 *ctx = &g_stats_ctx;
// Create published event
SmallPageStatsPublished_MID_v3 pub = {
.stat = *stat,
.page_ptr = NULL,
.retire_timestamp_ns = get_timestamp_ns(),
.sequence_number = ctx->total_published
};
// Store in history (circular buffer)
uint32_t idx = ctx->current_idx % SMALL_STATS_HISTORY_SIZE;
ctx->history[idx] = pub;
ctx->current_idx++;
ctx->total_published++;
// Periodic aggregation: notify Learner
if (ctx->total_published % ctx->eval_interval == 0) {
// Call Learner v2 to record this page stats
small_learner_v2_record_page_stats(stat);
}
}
const SmallPageStatsPublished_MID_v3* small_stats_mid_v3_latest(void) {
SmallStatsContext_MID_v3 *ctx = &g_stats_ctx;
if (ctx->total_published == 0) {
return NULL;
}
uint32_t idx = (ctx->current_idx - 1) % SMALL_STATS_HISTORY_SIZE;
return &ctx->history[idx];
}
uint64_t small_stats_mid_v3_published_count(void) {
return g_stats_ctx.total_published;
}
// ============================================================================
// Periodic Aggregation
// ============================================================================
void small_stats_mid_v3_aggregate(SmallPageStatsAggregate_MID_v3 *out) {
if (!out) return;
memset(out, 0, sizeof(*out));
out->aggregate_timestamp_ns = get_timestamp_ns();
SmallStatsContext_MID_v3 *ctx = &g_stats_ctx;
// Aggregate from history (simple approach: aggregate last N events)
uint32_t count = ctx->total_published < SMALL_STATS_HISTORY_SIZE
? ctx->total_published
: SMALL_STATS_HISTORY_SIZE;
for (uint32_t i = 0; i < count; i++) {
SmallPageStatsPublished_MID_v3 *pub = &ctx->history[i];
SmallPageStatsMID_v3 *stat = &pub->stat;
if (stat->class_idx < 8) {
out->class_allocations[stat->class_idx] += stat->total_allocations;
out->class_frees[stat->class_idx] += stat->total_frees;
out->class_retire_count[stat->class_idx]++;
// Weighted average for free hit ratio
uint64_t weight = stat->total_allocations;
if (weight > 0) {
out->class_avg_free_hit_bps[stat->class_idx] +=
(stat->free_hit_ratio_bps * weight);
}
}
out->total_allocations += stat->total_allocations;
out->total_frees += stat->total_frees;
out->total_pages_retired++;
}
// Normalize weighted averages
for (int i = 0; i < 8; i++) {
if (out->class_allocations[i] > 0) {
out->class_avg_free_hit_bps[i] /= out->class_allocations[i];
}
}
if (out->total_allocations > 0) {
out->global_avg_free_hit_bps =
(out->total_frees * 10000) / out->total_allocations;
}
out->eval_count = ctx->total_published / ctx->eval_interval;
}
const SmallPageStatsAggregate_MID_v3* small_stats_mid_v3_aggregate_snapshot(void) {
static SmallPageStatsAggregate_MID_v3 snapshot;
small_stats_mid_v3_aggregate(&snapshot);
return &snapshot;
}
// ============================================================================
// Configuration
// ============================================================================
void small_stats_mid_v3_set_eval_interval(uint32_t interval) {
g_stats_ctx.eval_interval = interval > 0 ? interval : SMALL_STATS_EVAL_INTERVAL;
}
uint32_t small_stats_mid_v3_get_eval_interval(void) {
return g_stats_ctx.eval_interval;
}
void small_stats_mid_v3_set_enabled(bool enabled) {
g_stats_ctx.enabled = enabled;
}
void small_stats_mid_v3_reset(void) {
memset(&g_stats_ctx, 0, sizeof(g_stats_ctx));
g_stats_ctx.eval_interval = SMALL_STATS_EVAL_INTERVAL;
g_stats_ctx.enabled = true;
}
// ============================================================================
// Debugging & Monitoring
// ============================================================================
void small_stats_mid_v3_print_latest(void) {
const SmallPageStatsPublished_MID_v3 *pub = small_stats_mid_v3_latest();
if (!pub) {
fprintf(stderr, "[MID_v3_Stats] No stats published yet\n");
return;
}
const SmallPageStatsMID_v3 *stat = &pub->stat;
fprintf(stderr, "[MID_v3_Stats] Latest (seq=%lu):\n", pub->sequence_number);
fprintf(stderr, " class_idx=%u allocs=%lu frees=%lu capacity=%u\n",
stat->class_idx, stat->total_allocations, stat->total_frees,
stat->page_alloc_count);
fprintf(stderr, " free_hit_ratio=%u bps (%.2f%%)\n",
stat->free_hit_ratio_bps, stat->free_hit_ratio_bps / 100.0);
}
void small_stats_mid_v3_print_aggregate(void) {
const SmallPageStatsAggregate_MID_v3 *agg = small_stats_mid_v3_aggregate_snapshot();
fprintf(stderr, "[MID_v3_Stats] Aggregate:\n");
fprintf(stderr, " total_allocations=%lu total_frees=%lu total_retires=%u\n",
agg->total_allocations, agg->total_frees, agg->total_pages_retired);
fprintf(stderr, " global_avg_free_hit=%u bps (%.2f%%)\n",
agg->global_avg_free_hit_bps, agg->global_avg_free_hit_bps / 100.0);
for (int i = 0; i < 8; i++) {
if (agg->class_retire_count[i] > 0) {
fprintf(stderr, " C%d: allocs=%lu frees=%lu retires=%u avg_hit=%u bps\n",
i, agg->class_allocations[i], agg->class_frees[i],
agg->class_retire_count[i], agg->class_avg_free_hit_bps[i]);
}
}
}