// smallobject_stats_mid_v3.c // Phase v11a-2: Stats collection for MID v3.5 page lifetime tracking #include #include #include #include #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]); } } }