Files
hakmem/core/hakmem_policy.c
Moe Charm (CI) d355041638 Port: Tune Superslab Min-Keep and Shared Pool Soft Caps (04a60c316)
- Policy: Set tiny_min_keep for C2-C6 to reduce mmap/munmap churn
- Policy: Loosen tiny_cap (soft cap) for C4-C6 to allow more active slots
- Added tiny_min_keep field to FrozenPolicy struct

Larson: 52.13M ops/s (stable)

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-26 15:06:36 +09:00

160 lines
6.7 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "hakmem_policy.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
static _Atomic(FrozenPolicy*) g_frozen_pol = NULL;
void hkm_policy_init(void) {
FrozenPolicy* pol = (FrozenPolicy*)calloc(1, sizeof(FrozenPolicy));
if (!pol) return;
// Defaults aligned with current implementation
// Tiny caps: keep placeholders (not enforced in hot path yet)
pol->tiny_cap[0] = 2048;
pol->tiny_cap[1] = 1024;
pol->tiny_cap[2] = 768;
pol->tiny_cap[3] = 512;
pol->tiny_cap[4] = 512; // Loosened from 256 for Shared Pool
pol->tiny_cap[5] = 512; // Loosened from 256
pol->tiny_cap[6] = 256; // Loosened from 128
pol->tiny_cap[7] = 256; // C7 soft cap: Keep at 256 to force legacy fallback (legacy is 45x faster: 47M vs 1M ops/s)
// Tiny min-keep (per class Superslab reserve).
// C2-C6: Keep a few slabs to reduce mmap/munmap churn in mixed workloads.
for (int i = 0; i < 8; i++) pol->tiny_min_keep[i] = 0;
pol->tiny_min_keep[2] = 2; // 32B
pol->tiny_min_keep[3] = 2; // 64B
pol->tiny_min_keep[4] = 2; // 128B
pol->tiny_min_keep[5] = 1; // 256B
pol->tiny_min_keep[6] = 1; // 512B
// ========================================================================
// CAP初期値の設計思想:
// ========================================================================
// 現在の値は「保守的」設定(メモリフットプリント優先):
// - Mid: {64, 64, 64, 32, 16} → 合計 15.0 MB (64KB/page)
// - Large: {8, 8, 4, 2, 1} → 合計 4.5 MB
//
// パフォーマンス優先設定(推奨):
// - Mid: {256, 256, 256, 128, 64} → 合計 60.0 MB (4倍化)
// - Large: {32, 32, 16, 8, 4} → 合計 18.0 MB (4倍化)
//
// トレードオフ:
// - CAP大: ヒット率↑、フットプリント↑
// - CAP小: ヒット率↓、フットプリント↓
//
// 環境変数で変更可能:
// HAKMEM_CAP_MID=256,256,256,128,64
// HAKMEM_CAP_LARGE=32,32,16,8,4
// ========================================================================
// L1: Mid/Large caps Phase 6.21: Revert to conservative + Bridge classes
// Phase 2: Mid={256,256,256,128,64} Large={32,32,16,8,4} (4x, 78MB total)
// Phase 6.21: Mid={64,64,64,32,16,32,32} Large={8,8,4,2,1} (1x + bridges, ~22MB total)
// Bridge classes (40KB, 52KB) now hardcoded in g_class_sizes[], CAP=32 each
uint16_t mid_defaults[7] = { 64, 64, 64, 32, 16, 32, 32 }; // Added slots 5,6 for Bridge
uint16_t large_defaults[5] = { 8, 8, 4, 2, 1 }; // Reverted to 1x
memcpy(pol->mid_cap, mid_defaults, sizeof(mid_defaults));
memcpy(pol->large_cap, large_defaults, sizeof(large_defaults));
// Phase 6.21: Disable DYN1/DYN2 (replaced by hardcoded Bridge classes)
pol->mid_dyn1_bytes = 0; // Disabled (Bridge classes now hardcoded)
pol->mid_cap_dyn1 = 0;
pol->mid_dyn2_bytes = 0; // Disabled
pol->mid_cap_dyn2 = 0;
// ========================================================================
// W_MAX (切り上げ許容倍率) の設計思想:
// ========================================================================
// W_MAX = 要求サイズの何倍までのクラスを許容するか
//
// 現在の値:
// - w_max_mid = 1.40 (40%切り上げ許容) - やや保守的
// - w_max_large = 1.30 (30%切り上げ許容) - 保守的 **問題あり**
//
// 問題点:
// w_max_large=1.30だと、32-64KBギャップで多くの要求が弾かれる
// 例: 35KB要求 → 64KB使用は 1.83倍 > 1.30 → NG → malloc fallback
//
// 推奨値:
// - w_max_mid = 1.401.60 (40-60%許容)
// - w_max_large = 1.60 (60%許容) ⭐⭐⭐ 即効改善
//
// トレードオフ:
// - W_MAX大: ヒット率↑、内部断片化↑
// - W_MAX小: ヒット率↓、内部断片化↓
// ========================================================================
// shard/policy maps default to 0 (noop)
pol->w_max_mid = 1.60f; // Phase 6.25: Looser for MidPool performance (was 1.40)
pol->w_max_large = 1.30f; // Phase 6.21: Revert to 1.30 (Bridge classes now cover 32-64KB gap)
pol->w_max = 1.6f; // legacy aggregate (unused by ACE)
pol->thp_threshold = 2 * 1024 * 1024; // 2MiB
pol->generation = 1;
// Optional env overrides for quick experiments
const char* e_mid = getenv("HAKMEM_WMAX_MID");
if (e_mid) {
float v = (float)atof(e_mid);
if (v >= 1.0f && v <= 2.0f) pol->w_max_mid = v;
}
const char* e_large = getenv("HAKMEM_WMAX_LARGE");
if (e_large) {
float v = (float)atof(e_large);
if (v >= 1.0f && v <= 2.0f) pol->w_max_large = v;
}
// Optional CAP overrides: comma-separated integers per class
const char* cap_mid = getenv("HAKMEM_CAP_MID");
if (cap_mid) {
char buf[256]; strncpy(buf, cap_mid, sizeof(buf)-1); buf[sizeof(buf)-1] = '\0';
char* save = NULL; char* tok = strtok_r(buf, ",", &save); int i=0;
while (tok && i < 5) { pol->mid_cap[i++] = (uint16_t)atoi(tok); tok = strtok_r(NULL, ",", &save); }
}
const char* cap_lg = getenv("HAKMEM_CAP_LARGE");
if (cap_lg) {
char buf[256]; strncpy(buf, cap_lg, sizeof(buf)-1); buf[sizeof(buf)-1] = '\0';
char* save = NULL; char* tok = strtok_r(buf, ",", &save); int i=0;
while (tok && i < 5) { pol->large_cap[i++] = (uint16_t)atoi(tok); tok = strtok_r(NULL, ",", &save); }
}
// Optional dynamic Mid class from env (bytes)
const char* dyn1 = getenv("HAKMEM_MID_DYN1");
if (dyn1) {
long v = atol(dyn1);
if (v >= 2048 && v <= 32768) pol->mid_dyn1_bytes = (uint32_t)v;
}
const char* cap_dyn1 = getenv("HAKMEM_CAP_MID_DYN1");
if (cap_dyn1) {
int v = atoi(cap_dyn1);
if (v >= 0 && v < 65535) pol->mid_cap_dyn1 = (uint16_t)v;
}
const char* dyn2 = getenv("HAKMEM_MID_DYN2");
if (dyn2) {
long v = atol(dyn2);
if (v >= 2048 && v <= 32768) pol->mid_dyn2_bytes = (uint32_t)v;
}
const char* cap_dyn2 = getenv("HAKMEM_CAP_MID_DYN2");
if (cap_dyn2) {
int v = atoi(cap_dyn2);
if (v >= 0 && v < 65535) pol->mid_cap_dyn2 = (uint16_t)v;
}
atomic_store(&g_frozen_pol, pol);
}
void hkm_policy_publish(FrozenPolicy* new_pol) {
if (!new_pol) return;
FrozenPolicy* old = atomic_exchange(&g_frozen_pol, new_pol);
// NOTE: In a real RCU scheme we would wait for a grace period.
// For now, free the old snapshot immediately.
free(old);
}
const FrozenPolicy* hkm_policy_get(void) {
FrozenPolicy* pol = atomic_load(&g_frozen_pol);
return pol;
}