/* hakmem_ace_metrics.c - ACE Learning Layer Metrics Collection */ #include "hakmem_ace_metrics.h" #include #include #include #include #include /* グローバルカウンタ(atomicで高速更新) */ _Atomic uint64_t g_ace_alloc_count = 0; _Atomic uint64_t g_ace_free_count = 0; _Atomic uint64_t g_ace_mutex_wait_ns = 0; /* 前回収集時のカウンタ(delta計算用) */ static uint64_t s_prev_alloc_count = 0; static uint64_t s_prev_free_count = 0; static uint64_t s_prev_timestamp_ms = 0; /* LLC miss monitoring用 */ static bool s_llc_available = false; /* ========== 初期化・破棄 ========== */ void hkm_ace_metrics_init(void) { /* カウンタリセット */ atomic_store_explicit(&g_ace_alloc_count, 0, memory_order_relaxed); atomic_store_explicit(&g_ace_free_count, 0, memory_order_relaxed); atomic_store_explicit(&g_ace_mutex_wait_ns, 0, memory_order_relaxed); s_prev_alloc_count = 0; s_prev_free_count = 0; struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); s_prev_timestamp_ms = (uint64_t)ts.tv_sec * 1000ULL + (uint64_t)ts.tv_nsec / 1000000ULL; /* LLC miss監視が利用可能かチェック */ s_llc_available = hkm_ace_llc_available(); } void hkm_ace_metrics_destroy(void) { /* 特に何もしない */ } /* ========== 現在時刻取得(ミリ秒) ========== */ static inline uint64_t get_monotonic_ms(void) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return (uint64_t)ts.tv_sec * 1000ULL + (uint64_t)ts.tv_nsec / 1000000ULL; } /* ========== スループット計算 ========== */ static void collect_throughput(struct hkm_ace_metrics *out) { uint64_t now_ms = get_monotonic_ms(); uint64_t elapsed_ms = (now_ms > s_prev_timestamp_ms) ? (now_ms - s_prev_timestamp_ms) : 1; uint64_t curr_alloc = atomic_load_explicit(&g_ace_alloc_count, memory_order_relaxed); uint64_t curr_free = atomic_load_explicit(&g_ace_free_count, memory_order_relaxed); uint64_t delta_alloc = curr_alloc - s_prev_alloc_count; uint64_t delta_free = curr_free - s_prev_free_count; uint64_t delta_ops = delta_alloc + delta_free; /* ops/sec = delta_ops / (elapsed_ms / 1000.0) */ out->throughput_ops = (delta_ops * 1000ULL) / elapsed_ms; /* 次回用に保存 */ s_prev_alloc_count = curr_alloc; s_prev_free_count = curr_free; s_prev_timestamp_ms = now_ms; out->timestamp_ms = now_ms; } /* ========== LLC miss rate 計算 ========== */ static void collect_llc_miss_rate(struct hkm_ace_metrics *out) { if (!s_llc_available) { out->llc_miss_rate = 0.0; return; } uint64_t misses = 0, references = 0; hkm_ace_llc_read(&misses, &references); if (references > 0) { out->llc_miss_rate = (double)misses / (double)references; } else { out->llc_miss_rate = 0.0; } } /* ========== Mutex contention 計算 ========== */ static void collect_mutex_wait(struct hkm_ace_metrics *out) { uint64_t total_wait_ns = atomic_load_explicit(&g_ace_mutex_wait_ns, memory_order_relaxed); out->mutex_wait_ns = total_wait_ns; /* リセット(次の窓用) */ atomic_store_explicit(&g_ace_mutex_wait_ns, 0, memory_order_relaxed); } /* ========== Remote free backlog 読み取り ========== */ extern uint32_t hkm_tiny_get_remote_backlog(uint8_t class_idx); /* tiny poolから取得 */ static void collect_remote_backlog(struct hkm_ace_metrics *out) { for (int c = 0; c < 8; c++) { /* TODO: tiny poolの実装に合わせて調整 */ out->remote_free_backlog[c] = 0; /* プレースホルダー */ } } /* ========== Fragmentation ratio 計算(/proc/self/status読み取り) ========== */ static void collect_fragmentation_and_rss(struct hkm_ace_metrics *out) { FILE *fp = fopen("/proc/self/status", "r"); if (!fp) { out->fragmentation_ratio = 1.0; out->rss_mb = 0; return; } uint64_t vm_size_kb = 0; uint64_t vm_rss_kb = 0; char line[256]; while (fgets(line, sizeof(line), fp)) { if (strncmp(line, "VmSize:", 7) == 0) { sscanf(line + 7, "%lu", &vm_size_kb); } else if (strncmp(line, "VmRSS:", 6) == 0) { sscanf(line + 6, "%lu", &vm_rss_kb); } } fclose(fp); /* Fragmentation ratio = RSS / VmSize */ if (vm_size_kb > 0) { out->fragmentation_ratio = (double)vm_rss_kb / (double)vm_size_kb; } else { out->fragmentation_ratio = 1.0; } /* RSS in MB */ out->rss_mb = vm_rss_kb / 1024; } /* ========== Fast metrics 収集(0.5-1s間隔) ========== */ void hkm_ace_metrics_collect_fast(struct hkm_ace_metrics *out) { memset(out, 0, sizeof(*out)); collect_throughput(out); collect_llc_miss_rate(out); collect_mutex_wait(out); collect_remote_backlog(out); } /* ========== Slow metrics 収集(30-60s間隔) ========== */ void hkm_ace_metrics_collect_slow(struct hkm_ace_metrics *out) { collect_fragmentation_and_rss(out); } /* ========== Full metrics 収集 ========== */ void hkm_ace_metrics_collect(struct hkm_ace_metrics *out) { hkm_ace_metrics_collect_fast(out); hkm_ace_metrics_collect_slow(out); } /* ========== LLC miss monitoring (rdpmc wrapper) ========== */ /* rdpmc利用可能性チェック * 注: Linux kernelで CONFIG_PERF_EVENTS=y が必要 * /proc/sys/kernel/perf_event_paranoid <= 1 が必要 */ bool hkm_ace_llc_available(void) { /* 簡易チェック: /proc/sys/kernel/perf_event_paranoid を読む */ FILE *fp = fopen("/proc/sys/kernel/perf_event_paranoid", "r"); if (!fp) { return false; } int paranoid = 2; /* デフォルト値 */ int ret = fscanf(fp, "%d", ¶noid); fclose(fp); (void)ret; /* Suppress unused warning */ /* paranoid <= 1 なら rdpmc 使用可能 */ return (paranoid <= 1); } /* LLC miss/reference カウンタ読み取り * 注: Phase 1では簡易実装(常に0を返す) * Phase 3でperf_event_open()を使った本実装に変更 */ void hkm_ace_llc_read(uint64_t *misses, uint64_t *references) { /* Phase 1: プレースホルダー実装 */ *misses = 0; *references = 1; /* 0除算回避 */ /* TODO Phase 3: perf_event_open() + rdpmc() 実装 * * struct perf_event_attr attr; * memset(&attr, 0, sizeof(attr)); * attr.type = PERF_TYPE_HARDWARE; * attr.config = PERF_COUNT_HW_CACHE_MISSES; * int fd_miss = perf_event_open(&attr, 0, -1, -1, 0); * * attr.config = PERF_COUNT_HW_CACHE_REFERENCES; * int fd_ref = perf_event_open(&attr, 0, -1, -1, 0); * * read(fd_miss, misses, sizeof(uint64_t)); * read(fd_ref, references, sizeof(uint64_t)); */ }