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