Files
hakmem/docs/archive/PHASE_6.11_DYNAMIC_THRESHOLDS.md
Moe Charm (CI) 52386401b3 Debug Counters Implementation - Clean History
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>
2025-11-05 12:31:14 +09:00

619 lines
15 KiB
Markdown
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.

# 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 のような上位クラス流用の"どこまで許すか"
効きどころ:
- 中粒(232KiB)のプールヒット率
- 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 gateHugePage 適用最小サイズ)**
```c
THP_THRESHOLD {1MB, 2MB, 4MB}
効きどころ:
- vm(大塊)の TLB/fault 最適化
- 環境依存なので慎重に
```
#### **5. mmap_cutovermmap に切替えるサイズ)**
```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 の Arm3候補
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 窓
3060
// 報酬
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) の一部