Major Features: - Debug counter infrastructure for Refill Stage tracking - Free Pipeline counters (ss_local, ss_remote, tls_sll) - Diagnostic counters for early return analysis - Unified larson.sh benchmark runner with profiles - Phase 6-3 regression analysis documentation Bug Fixes: - Fix SuperSlab disabled by default (HAKMEM_TINY_USE_SUPERSLAB) - Fix profile variable naming consistency - Add .gitignore patterns for large files Performance: - Phase 6-3: 4.79 M ops/s (has OOM risk) - With SuperSlab: 3.13 M ops/s (+19% improvement) This is a clean repository without large log files. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
619 lines
15 KiB
Markdown
619 lines
15 KiB
Markdown
# Phase 6.11: 動的閾値学習 - ELO による閾値最適化
|
||
|
||
**Date**: 2025-10-21
|
||
**Status**: 設計完了(実装待機)
|
||
**発案者**: tomoaki(ユーザー)
|
||
**詳細設計**: ChatGPT Pro
|
||
|
||
---
|
||
|
||
## 🎯 **コンセプト**
|
||
|
||
**tomoaki の発想**:
|
||
> 「ELO なんだから、キャッシュの固定値その物を学習して動かせばいいんじゃにゃい?」
|
||
|
||
**ChatGPT の詳細化**:
|
||
- **全部を常時動かすのはNG**
|
||
- **3〜5個の効きが大きい閾値だけ**を対象
|
||
- **CANARY 窓で小さく試してから FROZEN に焼く**
|
||
|
||
---
|
||
|
||
## 📊 **学習対象の閾値(効果順)**
|
||
|
||
### **★★★ 最優先(効果大/副作用小)**
|
||
|
||
#### **1. W_MAX(切り上げ許容率)**
|
||
|
||
```c
|
||
// 上位クラス流用の許容度
|
||
// 例: 20KB 要求 → 32KB クラスを使う(32/20 = 1.6)
|
||
W_MAX ∈ {1.25, 1.5, 1.75, 2.0}
|
||
|
||
役割:
|
||
- 20KB → 32KB のような上位クラス流用の"どこまで許すか"
|
||
|
||
効きどころ:
|
||
- 中粒(2〜32KiB)のプールヒット率 ↑
|
||
- mir/mixed の p99 改善
|
||
```
|
||
|
||
**具体例**:
|
||
```
|
||
W_MAX = 1.25(厳しめ):
|
||
- 20KB 要求 → 25KB 以下のクラスのみ使用
|
||
- ヒット率: 低い
|
||
- メモリ効率: 高い(無駄少ない)
|
||
|
||
W_MAX = 1.75(緩め):
|
||
- 20KB 要求 → 35KB 以下のクラスまで使用
|
||
- ヒット率: 高い
|
||
- メモリ効率: やや低い(無駄増える)
|
||
|
||
→ ELO が最適値を学習
|
||
```
|
||
|
||
#### **2. Hot/Warm/Cold の境界(ms)**
|
||
|
||
```c
|
||
// 再利用間隔に基づく Free policy 切り替え
|
||
hot_ms ∈ {50, 100, 200} // Hot: < 100ms → KEEP
|
||
warm_ms ∈ {500, 800, 1200} // Warm: 100-800ms → FREE (MADV_FREE)
|
||
// Cold: >= 800ms → DONTNEED (batch)
|
||
|
||
効きどころ:
|
||
- minor-faults と RSS のバランス最適化
|
||
- 頻繁な再利用を KEEP で高速化
|
||
```
|
||
|
||
**具体例**:
|
||
```
|
||
hot_ms = 50(厳しめ):
|
||
- 50ms 以内の再利用のみ KEEP
|
||
- minor-faults: 多い
|
||
- RSS: 少ない
|
||
|
||
hot_ms = 200(緩め):
|
||
- 200ms 以内の再利用を KEEP
|
||
- minor-faults: 少ない
|
||
- RSS: 多い
|
||
|
||
→ ELO がワークロードに最適な境界を学習
|
||
```
|
||
|
||
#### **3. BATCH_THRESHOLD の基準レンジ(下限/上限)**
|
||
|
||
```c
|
||
// madvise バッチ処理の対象範囲
|
||
BATCH_RANGE ∈ {(64KiB, 2MiB), (128KiB, 4MiB), (256KiB, 8MiB)}
|
||
|
||
効きどころ:
|
||
- madvise 呼びすぎ/返さなさすぎの両極端を回避
|
||
- syscall 頻度と RSS のバランス
|
||
```
|
||
|
||
---
|
||
|
||
### **★★ 次優先(環境依存)**
|
||
|
||
#### **4. THP gate(HugePage 適用最小サイズ)**
|
||
|
||
```c
|
||
THP_THRESHOLD ∈ {1MB, 2MB, 4MB}
|
||
|
||
効きどころ:
|
||
- vm(大塊)の TLB/fault 最適化
|
||
- 環境依存なので慎重に
|
||
```
|
||
|
||
#### **5. mmap_cutover(mmap に切替えるサイズ)**
|
||
|
||
```c
|
||
MMAP_CUTOVER ∈ {512KB, 768KB, 1MB, 2MB}
|
||
|
||
効きどころ:
|
||
- 巨大側の syscall 頻度・断片化最適化
|
||
```
|
||
|
||
---
|
||
|
||
## 🔧 **学習方法(軽量バンディット)**
|
||
|
||
### **基本方針**
|
||
|
||
```
|
||
FROZEN:
|
||
- 閾値は固定(読み取りのみ、オーバーヘッドゼロ)
|
||
|
||
CANARY 窓(30〜60秒):
|
||
- 候補閾値を試す
|
||
- 報酬を集計
|
||
- 勝者を採択
|
||
|
||
FROZEN(採用後):
|
||
- 新しい閾値で凍結
|
||
- 一定時間(dwell time)は変更禁止
|
||
```
|
||
|
||
---
|
||
|
||
### **候補数の最適解: 3候補(ChatGPT Pro 推奨)**
|
||
|
||
**理由**:
|
||
1. **3候補**: 探索空間を抑えつつ十分なバリエーション
|
||
- 2候補では選択肢が少なすぎる
|
||
- 4候補以上では収束が遅い(CANARY 窓数が増える)
|
||
2. **ε-greedy (ε=0.1)**: 10%探索、90%最良選択で効率的
|
||
3. **CANARY window**: 30秒で新候補テスト可能
|
||
4. **Hysteresis**: ±1 step のみ変更で安定性確保
|
||
|
||
**初期値の選び方**:
|
||
- 中間値を初期値とする(例: W_MAX は 1.5)
|
||
- 理由: 両方向に探索可能(上下に候補あり)
|
||
|
||
---
|
||
|
||
### **1. 候補集合の離散化**
|
||
|
||
```c
|
||
// W_MAX の候補
|
||
const double W_MAX_CANDIDATES[] = {1.25, 1.5, 1.75};
|
||
const int W_MAX_NUM_CANDIDATES = 3;
|
||
|
||
// hot_ms の候補
|
||
const int HOT_MS_CANDIDATES[] = {50, 100, 200};
|
||
const int HOT_MS_NUM_CANDIDATES = 3;
|
||
|
||
// warm_ms の候補(順序制約: hot_ms < warm_ms)
|
||
const int WARM_MS_CANDIDATES[] = {500, 800, 1200};
|
||
const int WARM_MS_NUM_CANDIDATES = 3;
|
||
|
||
// BATCH_RANGE の候補
|
||
typedef struct {
|
||
size_t min;
|
||
size_t max;
|
||
} BatchRange;
|
||
|
||
const BatchRange BATCH_RANGE_CANDIDATES[] = {
|
||
{64 * 1024, 2 * 1024 * 1024},
|
||
{128 * 1024, 4 * 1024 * 1024},
|
||
{256 * 1024, 8 * 1024 * 1024}
|
||
};
|
||
const int BATCH_RANGE_NUM_CANDIDATES = 3;
|
||
```
|
||
|
||
---
|
||
|
||
### **2. 報酬(reward)の定義**
|
||
|
||
```c
|
||
// 窓ごとに報酬を計算
|
||
typedef struct {
|
||
double p99; // レイテンシ P99
|
||
uint64_t minor_faults; // minor page faults
|
||
uint64_t syscalls; // syscall 回数
|
||
uint64_t rss_kb; // RSS (KB)
|
||
} Metrics;
|
||
|
||
// 改善率の計算
|
||
static inline double improv(double current, double baseline) {
|
||
if (baseline == 0) return 0.0;
|
||
return (baseline - current) / baseline;
|
||
}
|
||
|
||
// RSS ペナルティ(増えすぎたらマイナス)
|
||
static inline double rss_penalty(uint64_t rss, uint64_t baseline_rss) {
|
||
if (rss <= baseline_rss * 1.1) return 0.0; // 10% までは許容
|
||
return (double)(rss - baseline_rss * 1.1) / (double)baseline_rss;
|
||
}
|
||
|
||
// 総合報酬(重み付き和)
|
||
double calculate_reward(Metrics m, Metrics baseline) {
|
||
const double wL = 0.5; // latency 重み
|
||
const double wF = 0.3; // faults 重み
|
||
const double wS = 0.1; // syscalls 重み
|
||
const double wR = 0.1; // RSS penalty 重み
|
||
|
||
return wL * improv(m.p99, baseline.p99)
|
||
+ wF * improv(m.minor_faults, baseline.minor_faults)
|
||
+ wS * improv(m.syscalls, baseline.syscalls)
|
||
- wR * rss_penalty(m.rss_kb, baseline.rss_kb);
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### **3. 探索(ε-greedy)**
|
||
|
||
```c
|
||
// Arm(候補)の状態
|
||
typedef struct {
|
||
double avg_reward; // 平均報酬
|
||
int count; // 試行回数
|
||
} Arm;
|
||
|
||
// W_MAX の Arm(3候補)
|
||
static Arm g_arms_wmax[3] = {{0.0, 0}, {0.0, 0}, {0.0, 0}};
|
||
static int g_current_wmax_idx = 1; // 初期値: 1.5
|
||
|
||
// ε-greedy 選択
|
||
#define EPSILON 0.1 // 10% の確率でランダム探索
|
||
|
||
static int select_arm_epsilon_greedy(Arm* arms, int num_arms, int current_idx) {
|
||
// 10% の確率でランダム
|
||
if ((rand() % 100) < (EPSILON * 100)) {
|
||
return rand() % num_arms;
|
||
}
|
||
|
||
// 90% の確率で最良 Arm
|
||
int best_idx = 0;
|
||
double best_avg = arms[0].avg_reward;
|
||
|
||
for (int i = 1; i < num_arms; i++) {
|
||
if (arms[i].avg_reward > best_avg) {
|
||
best_avg = arms[i].avg_reward;
|
||
best_idx = i;
|
||
}
|
||
}
|
||
|
||
return best_idx;
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### **4. 採択ゲート(勝者判定)**
|
||
|
||
```c
|
||
// 採択条件
|
||
#define MIN_SAMPLES 10 // 最低試行回数
|
||
#define CONFIDENCE_LEVEL 1.96 // 95% 信頼区間
|
||
|
||
// 95% 信頼区間の上限
|
||
static double upper_confidence_bound(Arm* arm) {
|
||
if (arm->count == 0) return 0.0;
|
||
|
||
// 標準誤差(簡易版:分散を 1 と仮定)
|
||
double se = 1.0 / sqrt(arm->count);
|
||
return arm->avg_reward + CONFIDENCE_LEVEL * se;
|
||
}
|
||
|
||
// 勝者判定(現在の設定より有意に良いか?)
|
||
static int is_improvement_significant(Arm* candidate, Arm* current) {
|
||
if (candidate->count < MIN_SAMPLES) return 0;
|
||
if (current->count < MIN_SAMPLES) return 1; // 現在が未学習なら即採用
|
||
|
||
// 候補の下限 > 現在の上限 → 有意な改善
|
||
double candidate_lower = candidate->avg_reward - CONFIDENCE_LEVEL / sqrt(candidate->count);
|
||
double current_upper = current->avg_reward + CONFIDENCE_LEVEL / sqrt(current->count);
|
||
|
||
return candidate_lower > current_upper;
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### **5. ヒステリシス(揺れ防止)**
|
||
|
||
```c
|
||
// 1回の採用で ±1 ステップまで
|
||
#define MAX_STEP 1
|
||
|
||
// Dwell time(採用後の変更禁止期間)
|
||
#define DWELL_TIME_SEC 600 // 10分
|
||
|
||
static time_t g_last_adoption_time = 0;
|
||
|
||
static int clamp_step(int current_idx, int new_idx, int max_step) {
|
||
int diff = new_idx - current_idx;
|
||
|
||
if (diff > max_step) return current_idx + max_step;
|
||
if (diff < -max_step) return current_idx - max_step;
|
||
|
||
return new_idx;
|
||
}
|
||
|
||
static int can_adopt(void) {
|
||
time_t now = time(NULL);
|
||
return (now - g_last_adoption_time) >= DWELL_TIME_SEC;
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### **6. CANARY 窓終端での更新**
|
||
|
||
```c
|
||
// CANARY 窓終端(30〜60秒ごと)に呼ばれる
|
||
void canary_window_end(Metrics m, Metrics baseline) {
|
||
if (!can_adopt()) return; // Dwell time 中は何もしない
|
||
|
||
// 今回試した候補の報酬を記録
|
||
double reward = calculate_reward(m, baseline);
|
||
|
||
int cur_arm = g_current_wmax_idx; // 今回使った Arm
|
||
g_arms_wmax[cur_arm].avg_reward =
|
||
(g_arms_wmax[cur_arm].avg_reward * g_arms_wmax[cur_arm].count + reward)
|
||
/ (g_arms_wmax[cur_arm].count + 1);
|
||
g_arms_wmax[cur_arm].count++;
|
||
|
||
// 十分なサンプルが溜まったら勝者を判定
|
||
if (g_arms_wmax[cur_arm].count >= MIN_SAMPLES) {
|
||
int best = select_arm_epsilon_greedy(g_arms_wmax, W_MAX_NUM_CANDIDATES, cur_arm);
|
||
|
||
if (is_improvement_significant(&g_arms_wmax[best], &g_arms_wmax[cur_arm])) {
|
||
// ヒステリシス(±1 ステップまで)
|
||
int new_idx = clamp_step(g_current_wmax_idx, best, MAX_STEP);
|
||
|
||
if (new_idx != g_current_wmax_idx) {
|
||
// 採用!
|
||
g_current_wmax_idx = new_idx;
|
||
g_frozen_policy.W_MAX = W_MAX_CANDIDATES[new_idx];
|
||
|
||
printf("[ELO] W_MAX adopted: %.2f (arm %d)\n",
|
||
g_frozen_policy.W_MAX, new_idx);
|
||
|
||
g_last_adoption_time = time(NULL);
|
||
|
||
// Arm をリセット(次の CANARY まで)
|
||
for (int i = 0; i < W_MAX_NUM_CANDIDATES; i++) {
|
||
g_arms_wmax[i].avg_reward *= 0.9; // 減衰
|
||
g_arms_wmax[i].count = (int)(g_arms_wmax[i].count * 0.9);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 🎯 **Site Rules との併用**
|
||
|
||
### **優先順位**
|
||
|
||
```
|
||
[入力] (size, pc/site)
|
||
|
||
┌─ Step 1: Site Rules ルックアップ(O(1))
|
||
│ hit? → route/shard を即適用
|
||
│ miss? → Step 2 へ
|
||
│
|
||
└─ Step 2: FrozenPolicy(閾値学習の結果を使用)
|
||
- W_MAX で上位クラス流用判定
|
||
- hot_ms/warm_ms で Free policy 決定
|
||
- BATCH_RANGE で batch 対象判定
|
||
```
|
||
|
||
**ポイント**:
|
||
- **Site Rule で route 固定**している `(site, class)` には**閾値学習は影響しない**
|
||
- **閾値学習は "既定"側を更新**(Site Rule がない場合のフォールバック)
|
||
- **局所は Site Rule、全体は閾値学習**できれいに分業
|
||
|
||
---
|
||
|
||
## 📈 **期待される効果**
|
||
|
||
### **W_MAX 学習**
|
||
|
||
```
|
||
現状(固定 W_MAX = 1.5):
|
||
- 20KB 要求 → 32KB クラス(1.6 > 1.5 なので不可)
|
||
- Pool ミス → malloc へフォールバック
|
||
|
||
学習後(W_MAX = 1.75):
|
||
- 20KB 要求 → 32KB クラス(1.6 < 1.75 なので可)
|
||
- Pool ヒット!
|
||
|
||
効果:
|
||
- L2 Pool ヒット率: +15〜35%
|
||
- mir/mixed の p99: -10〜25%
|
||
```
|
||
|
||
### **Hot/Warm/Cold 境界学習**
|
||
|
||
```
|
||
現状(固定 hot_ms = 100, warm_ms = 800):
|
||
- 150ms で再利用 → Warm 扱い(MADV_FREE)
|
||
- minor-faults 発生
|
||
|
||
学習後(hot_ms = 200, warm_ms = 1200):
|
||
- 150ms で再利用 → Hot 扱い(KEEP)
|
||
- minor-faults 削減
|
||
|
||
効果:
|
||
- minor-faults/sec: -30〜70%(Warm 厚めが効く)
|
||
```
|
||
|
||
### **BATCH_RANGE 学習**
|
||
|
||
```
|
||
現状(固定 64KiB〜2MiB):
|
||
- 3MiB 要求 → batch 対象外(即時 munmap)
|
||
- syscall 多い
|
||
|
||
学習後(128KiB〜4MiB):
|
||
- 3MiB 要求 → batch 対象(遅延 munmap)
|
||
- syscall 削減
|
||
|
||
効果:
|
||
- madvise/sec: -20〜40%
|
||
- RSS 変動のスパイク抑制
|
||
```
|
||
|
||
---
|
||
|
||
## ⚠️ **ガードレール(必須制約)**
|
||
|
||
### **1. 順序制約**
|
||
|
||
```c
|
||
// hot_ms < warm_ms を常に保証
|
||
if (new_hot_ms >= current_warm_ms) {
|
||
return; // 不正な組み合わせは拒否
|
||
}
|
||
|
||
// BATCH_MIN ≤ BATCH_MAX を保証
|
||
if (new_batch_min > new_batch_max) {
|
||
return; // 不正な組み合わせは拒否
|
||
}
|
||
|
||
// W_MAX ≥ 1.0 を保証
|
||
if (new_wmax < 1.0) {
|
||
return; // 不正な値は拒否
|
||
}
|
||
```
|
||
|
||
### **2. Dwell time(変更禁止期間)**
|
||
|
||
```c
|
||
// 採用直後は 10 分間変更禁止
|
||
#define DWELL_TIME_SEC 600
|
||
|
||
// 揺れ防止
|
||
if (!can_adopt()) {
|
||
return; // まだ変更できない
|
||
}
|
||
```
|
||
|
||
### **3. ±1 ステップ制限**
|
||
|
||
```c
|
||
// 1回の採用で ±1 ステップまで
|
||
// (大ジャンプ禁止)
|
||
int new_idx = clamp_step(current_idx, best_idx, 1);
|
||
```
|
||
|
||
### **4. 安全弁固定(頻繁に動かさない)**
|
||
|
||
```c
|
||
// これらは月次/週次レベルでのみ調整
|
||
// - mmap_cutover(影響が大きい)
|
||
// - THP gate(環境依存)
|
||
```
|
||
|
||
---
|
||
|
||
## 🚀 **最小セット(すぐ始めるなら)**
|
||
|
||
### **Phase 6.11.1: W_MAX 学習のみ**
|
||
|
||
```c
|
||
// 候補
|
||
W_MAX ∈ {1.25, 1.5, 1.75}
|
||
|
||
// CANARY 窓
|
||
30〜60秒
|
||
|
||
// 報酬
|
||
wL=0.5 (p99), wF=0.3 (faults), wS=0.1 (syscalls), wR=0.1 (RSS)
|
||
|
||
// 探索
|
||
ε-greedy (ε=0.1)
|
||
|
||
// 採択
|
||
±1 ステップ、dwell time 10分
|
||
```
|
||
|
||
**期待効果**:
|
||
- L2 Pool ヒット率: +20%
|
||
- mir/mixed の p99: -15%
|
||
|
||
---
|
||
|
||
### **Phase 6.11.2: Hot/Warm/Cold 境界追加**
|
||
|
||
```c
|
||
// 候補(格子探索)
|
||
hot_ms ∈ {50, 100, 200}
|
||
warm_ms ∈ {600, 900, 1200}
|
||
|
||
// 順序制約
|
||
hot_ms < warm_ms を常に保証
|
||
```
|
||
|
||
**期待効果**:
|
||
- minor-faults/sec: -40%
|
||
|
||
---
|
||
|
||
### **Phase 6.11.3: BATCH_RANGE 追加**
|
||
|
||
```c
|
||
// 候補
|
||
BATCH_RANGE ∈ {(64K, 2M), (128K, 4M), (256K, 8M)}
|
||
```
|
||
|
||
**期待効果**:
|
||
- madvise/sec: -30%
|
||
- RSS スパイク抑制
|
||
|
||
---
|
||
|
||
## 📊 **オーバーヘッド**
|
||
|
||
### **FROZEN 時(通常)**
|
||
|
||
```
|
||
オーバーヘッド: ゼロ
|
||
- 閾値は固定値を読み取るだけ
|
||
- per-op コストなし
|
||
```
|
||
|
||
### **CANARY 窓(30〜60秒ごと)**
|
||
|
||
```
|
||
オーバーヘッド: O(1) 集計のみ
|
||
- 窓終端での報酬計算
|
||
- Arm 更新
|
||
- 勝者判定
|
||
|
||
所要時間: < 1ms(無視できる)
|
||
```
|
||
|
||
---
|
||
|
||
## 🎯 **まとめ**
|
||
|
||
### ✅ **ユーザーの洞察は完璧**
|
||
|
||
**「ELO なんだから、キャッシュの固定値その物を学習して動かせばいい」**
|
||
|
||
→ **その通り!閾値自体を ELO で最適化すべき**
|
||
|
||
### 🚀 **実装プラン**
|
||
|
||
**Phase 6.11.1: W_MAX 学習**
|
||
- 候補: {1.25, 1.5, 1.75}
|
||
- ε-greedy 探索
|
||
- CANARY 窓で採択
|
||
|
||
**Phase 6.11.2: Hot/Warm/Cold 境界**
|
||
- 候補: hot_ms ∈ {50, 100, 200}, warm_ms ∈ {600, 900, 1200}
|
||
- 格子探索
|
||
|
||
**Phase 6.11.3: BATCH_RANGE**
|
||
- 候補: {(64K, 2M), (128K, 4M), (256K, 8M)}
|
||
|
||
**期待効果**:
|
||
- L2 Pool ヒット率: +20%
|
||
- minor-faults/sec: -40%
|
||
- madvise/sec: -30%
|
||
- オーバーヘッド: ゼロ(FROZEN 時)
|
||
|
||
**ChatGPT の助言**:
|
||
- **対象を 3〜5 個に絞る**(全部動かさない)
|
||
- **CANARY 窓で小さく試す**(30〜60秒)
|
||
- **±1 ステップ制限**(大ジャンプ禁止)
|
||
- **Dwell time 10分**(揺れ防止)
|
||
|
||
これで **mimalloc の "adaptive size classes" を ELO で自動化**!😺✨
|
||
|
||
---
|
||
|
||
**Generated**: 2025-10-21
|
||
**発案者**: tomoaki(ユーザー)
|
||
**詳細設計**: ChatGPT Pro
|
||
**実装**: Phase 6.11.1 から段階的に開始予定
|
||
**システム名**: SACS (Site-Aware Adaptive Cache System) の一部
|