Files
hakmem/docs/archive/PHASE_6.11_DYNAMIC_THRESHOLDS.md

619 lines
15 KiB
Markdown
Raw Normal View History

# 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 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 窓
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) の一部