Files
hakmem/core/hakmem_elo.c

314 lines
11 KiB
C
Raw Normal View History

// hakmem_elo.c - ELO Rating Strategy Selection Implementation
//
// Multi-objective optimization using ELO rating system
// Composite score: 40% CPU + 30% PageFaults + 30% Memory
#include "hakmem_elo.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
// Global state
static EloStrategyCandidate g_strategies[ELO_MAX_STRATEGIES];
static int g_num_strategies = 0;
static int g_initialized = 0;
static int g_current_strategy = 0;
static uint64_t g_total_selections = 0;
// Strategy threshold presets (geometric progression from 512KB to 8MB)
static const size_t STRATEGY_THRESHOLDS[] = {
524288, // 512KB
786432, // 768KB
1048576, // 1MB
1572864, // 1.5MB
2097152, // 2MB
3145728, // 3MB
4194304, // 4MB
6291456, // 6MB
8388608, // 8MB
12582912, // 12MB
16777216, // 16MB
33554432 // 32MB
};
// Fast random (for epsilon-greedy)
static uint64_t g_rng_state = 123456789;
static uint64_t fast_random(void) {
g_rng_state ^= g_rng_state << 13;
g_rng_state ^= g_rng_state >> 7;
g_rng_state ^= g_rng_state << 17;
return g_rng_state;
}
// Initialize ELO system
void hak_elo_init(void) {
if (g_initialized) return;
// Initialize random seed
g_rng_state = (uint64_t)time(NULL);
// Create strategy candidates
g_num_strategies = sizeof(STRATEGY_THRESHOLDS) / sizeof(STRATEGY_THRESHOLDS[0]);
if (g_num_strategies > ELO_MAX_STRATEGIES) {
g_num_strategies = ELO_MAX_STRATEGIES;
}
for (int i = 0; i < g_num_strategies; i++) {
g_strategies[i].strategy_id = i;
g_strategies[i].elo_rating = ELO_INITIAL_RATING;
g_strategies[i].wins = 0;
g_strategies[i].losses = 0;
g_strategies[i].draws = 0;
g_strategies[i].samples = 0;
g_strategies[i].threshold_bytes = STRATEGY_THRESHOLDS[i];
g_strategies[i].active = 1;
}
// Select initial strategy (middle of the pack)
g_current_strategy = g_num_strategies / 2;
g_initialized = 1;
#if !HAKMEM_BUILD_RELEASE
fprintf(stderr, "[ELO] Initialized %d strategies (thresholds: 512KB-32MB)\n", g_num_strategies);
#endif
}
// Shutdown ELO system
void hak_elo_shutdown(void) {
if (!g_initialized) return;
hak_elo_print_leaderboard();
g_initialized = 0;
}
// Epsilon-greedy strategy selection
int hak_elo_select_strategy(void) {
if (!g_initialized) hak_elo_init();
g_total_selections++;
// Epsilon-greedy: 10% exploration, 90% exploitation
double rand_val = (double)(fast_random() % 1000) / 1000.0;
if (rand_val < ELO_EPSILON) {
// Exploration: random active strategy
int active_count = 0;
int active_indices[ELO_MAX_STRATEGIES];
for (int i = 0; i < g_num_strategies; i++) {
if (g_strategies[i].active) {
active_indices[active_count++] = i;
}
}
if (active_count > 0) {
int idx = fast_random() % active_count;
g_current_strategy = active_indices[idx];
}
} else {
// Exploitation: highest ELO rating
double best_rating = -1e9;
int best_idx = 0;
for (int i = 0; i < g_num_strategies; i++) {
if (g_strategies[i].active && g_strategies[i].elo_rating > best_rating) {
best_rating = g_strategies[i].elo_rating;
best_idx = i;
}
}
g_current_strategy = best_idx;
}
return g_current_strategy;
}
// Get threshold for strategy
size_t hak_elo_get_threshold(int strategy_id) {
if (!g_initialized) hak_elo_init();
if (strategy_id < 0 || strategy_id >= g_num_strategies) {
return STRATEGY_THRESHOLDS[g_num_strategies / 2];
}
return g_strategies[strategy_id].threshold_bytes;
}
// Record allocation result
void hak_elo_record_alloc(int strategy_id, size_t size, uint64_t duration_ns) {
if (!g_initialized) return;
if (strategy_id < 0 || strategy_id >= g_num_strategies) return;
EloStrategyCandidate* strategy = &g_strategies[strategy_id];
strategy->samples++;
// Simple tracking (real implementation would track full stats)
(void)size;
(void)duration_ns;
}
// Compute composite score (normalized 0-1)
double hak_elo_compute_score(const EloAllocStats* stats) {
// Normalize each metric (lower is better, so invert)
// For now, use simple heuristics
const double MAX_CPU_NS = 100000.0; // 100 microseconds
const double MAX_PAGE_FAULTS = 1000.0;
const double MAX_BYTES_LIVE = 100000000.0; // 100MB
double cpu_score = 1.0 - fmin(stats->cpu_ns / MAX_CPU_NS, 1.0);
double pf_score = 1.0 - fmin(stats->page_faults / MAX_PAGE_FAULTS, 1.0);
double mem_score = 1.0 - fmin(stats->bytes_live / MAX_BYTES_LIVE, 1.0);
// Weighted combination: 40% CPU, 30% PageFaults, 30% Memory
return 0.4 * cpu_score + 0.3 * pf_score + 0.3 * mem_score;
}
// Update ELO ratings (standard ELO formula)
void hak_elo_update_ratings(EloStrategyCandidate* a, EloStrategyCandidate* b, double score_diff) {
// Expected probability of A winning
double expected_a = 1.0 / (1.0 + pow(10.0, (b->elo_rating - a->elo_rating) / 400.0));
// Actual result: 1.0 = A wins, 0.0 = B wins, 0.5 = draw
double actual_a;
if (score_diff > 0.01) {
actual_a = 1.0; // A wins
a->wins++;
b->losses++;
} else if (score_diff < -0.01) {
actual_a = 0.0; // B wins
a->losses++;
b->wins++;
} else {
actual_a = 0.5; // Draw
a->draws++;
b->draws++;
}
// Update ratings
a->elo_rating += ELO_K_FACTOR * (actual_a - expected_a);
b->elo_rating += ELO_K_FACTOR * ((1.0 - actual_a) - (1.0 - expected_a));
}
// Trigger ELO evolution (pairwise comparison)
void hak_elo_trigger_evolution(void) {
if (!g_initialized) return;
#if !HAKMEM_BUILD_RELEASE
fprintf(stderr, "[ELO] Triggering evolution (pairwise comparison)...\n");
#endif
// Count active strategies with enough samples
int eligible[ELO_MAX_STRATEGIES];
int eligible_count = 0;
for (int i = 0; i < g_num_strategies; i++) {
if (g_strategies[i].active && g_strategies[i].samples >= ELO_MIN_SAMPLES) {
eligible[eligible_count++] = i;
}
}
if (eligible_count < 2) {
{ const char* q = getenv("HAKMEM_QUIET"); if (!(q && strcmp(q, "1") == 0)) fprintf(stderr, "[ELO] Not enough eligible strategies (need 2, have %d)\n", eligible_count); }
return;
}
// Perform pairwise comparisons (all pairs)
int comparisons = 0;
for (int i = 0; i < eligible_count; i++) {
for (int j = i + 1; j < eligible_count; j++) {
EloStrategyCandidate* a = &g_strategies[eligible[i]];
EloStrategyCandidate* b = &g_strategies[eligible[j]];
// Mock comparison (in real implementation, would run N samples)
// For now, simulate based on threshold proximity to 2MB (optimal)
size_t optimal = 2097152; // 2MB
double score_a = 1.0 / (1.0 + fabs((double)a->threshold_bytes - (double)optimal) / (double)optimal);
double score_b = 1.0 / (1.0 + fabs((double)b->threshold_bytes - (double)optimal) / (double)optimal);
double score_diff = score_a - score_b;
hak_elo_update_ratings(a, b, score_diff);
comparisons++;
}
}
{ const char* q = getenv("HAKMEM_QUIET"); if (!(q && strcmp(q, "1") == 0)) fprintf(stderr, "[ELO] Completed %d pairwise comparisons\n", comparisons); }
// Survival: keep top-M strategies
if (eligible_count > ELO_SURVIVAL_COUNT) {
// Sort by ELO rating
int sorted_indices[ELO_MAX_STRATEGIES];
for (int i = 0; i < eligible_count; i++) {
sorted_indices[i] = eligible[i];
}
// Simple bubble sort (good enough for 12 strategies)
for (int i = 0; i < eligible_count - 1; i++) {
for (int j = 0; j < eligible_count - i - 1; j++) {
if (g_strategies[sorted_indices[j]].elo_rating <
g_strategies[sorted_indices[j + 1]].elo_rating) {
int temp = sorted_indices[j];
sorted_indices[j] = sorted_indices[j + 1];
sorted_indices[j + 1] = temp;
}
}
}
// Deactivate bottom strategies
for (int i = ELO_SURVIVAL_COUNT; i < eligible_count; i++) {
int idx = sorted_indices[i];
g_strategies[idx].active = 0;
{ const char* q = getenv("HAKMEM_QUIET"); if (!(q && strcmp(q, "1") == 0)) fprintf(stderr, "[ELO] Strategy %d (%.0fKB) eliminated (rating: %.1f)\n",
idx, g_strategies[idx].threshold_bytes / 1024.0, g_strategies[idx].elo_rating); }
}
}
hak_elo_print_leaderboard();
}
// Print statistics
void hak_elo_print_stats(void) {
if (!g_initialized) return;
{ const char* q = getenv("HAKMEM_QUIET"); if (!(q && strcmp(q, "1") == 0)) fprintf(stderr, "\n[ELO] Statistics:\n"); }
{ const char* q = getenv("HAKMEM_QUIET"); if (!(q && strcmp(q, "1") == 0)) fprintf(stderr, " Total selections: %lu\n", g_total_selections); }
{ const char* q = getenv("HAKMEM_QUIET"); if (!(q && strcmp(q, "1") == 0)) fprintf(stderr, " Active strategies: "); }
int active_count = 0;
for (int i = 0; i < g_num_strategies; i++) {
if (g_strategies[i].active) active_count++;
}
{ const char* q = getenv("HAKMEM_QUIET"); if (!(q && strcmp(q, "1") == 0)) fprintf(stderr, "%d/%d\n", active_count, g_num_strategies); }
}
// Print leaderboard
void hak_elo_print_leaderboard(void) {
if (!g_initialized) return;
{ const char* q = getenv("HAKMEM_QUIET"); if (!(q && strcmp(q, "1") == 0)) {
fprintf(stderr, "\n[ELO] Leaderboard:\n");
fprintf(stderr, " Rank | ID | Threshold | ELO Rating | W/L/D | Samples | Status\n");
fprintf(stderr, " -----|----|-----------+------------+-------+---------+--------\n");
} }
// Sort by ELO rating
int sorted_indices[ELO_MAX_STRATEGIES];
for (int i = 0; i < g_num_strategies; i++) {
sorted_indices[i] = i;
}
for (int i = 0; i < g_num_strategies - 1; i++) {
for (int j = 0; j < g_num_strategies - i - 1; j++) {
if (g_strategies[sorted_indices[j]].elo_rating <
g_strategies[sorted_indices[j + 1]].elo_rating) {
int temp = sorted_indices[j];
sorted_indices[j] = sorted_indices[j + 1];
sorted_indices[j + 1] = temp;
}
}
}
for (int i = 0; i < g_num_strategies; i++) {
int idx = sorted_indices[i];
EloStrategyCandidate* s = &g_strategies[idx];
{ const char* q = getenv("HAKMEM_QUIET"); if (!(q && strcmp(q, "1") == 0)) fprintf(stderr, " %4d | %2d | %7.0fKB | %10.1f | %lu/%lu/%lu | %7lu | %s\n",
i + 1, s->strategy_id, s->threshold_bytes / 1024.0, s->elo_rating,
s->wins, s->losses, s->draws, s->samples,
s->active ? "ACTIVE" : "eliminated"); }
}
}
// Release-silent logging
#include "hakmem_internal.h"