Files
hakmem/docs/archive/PHASE_6.10_SITE_RULES.md

582 lines
14 KiB
Markdown
Raw Normal View History

# Phase 6.10: Site Rules - アドレス直結キャッシュ最適化
**Date**: 2025-10-21
**Status**: 設計完了(実装待機)
**発案者**: tomoakiユーザー
**詳細設計**: ChatGPT Pro
---
## 🎯 **コンセプト**
**tomoaki の発想**:
> 「アドレスその物を ELO だけどどう?」
**核心アイデア**:
- `(call-site address, size_class)` → キャッシュ経路L1/L2/L3を**直接結びつける**
- ELO で Site Rules を**学習・作成・凍結・破棄**を自動化
- 用途別に最適化parse_json の 8KB は L2+KEEP、tmp_buffer の 8KB は L3+DONTNEED
---
## 📊 **現状の問題Phase 6.9.1 まで)**
### **サイズクラスだけでは不十分**
```
現状:
size → class → L1/L2/L3 ルーティング
問題:
同じ 8KiB でも用途が違う:
- parse_json() の 8KiB → 頻繁に再利用HOT
- tmp_buffer() の 8KiB → 使い捨てCOLD
→ 全部同じプールに入れると効率悪い!
```
### **ベンチマーク結果Phase 6.9.1**
```
mixed scenario:
- hakmem: +66% vs mimalloc
- 原因: 中粒2-32KiBの最適化不足
mir scenario:
- hakmem: +52% vs mimalloc
- 原因: 256KB は Pool 範囲外2-32KiB のみ)
```
---
## 💡 **Site Rules の解決策**
### **アドレス直結ルーティング**
```
Site Rules:
(site_id, size_class) → route/policy/shard
効果:
- parse_json() → L2 + KEEP + shard_0粘着
- tmp_buffer() → L3 + DONTNEEDバッチ解放
→ 用途別に最適化!
```
### **2層合成アーキテクチャ**
```
[入力] (size, pc/site, owner_tid)
┌─ Step0: 例外早抜け超大サイズ明示L3
│ if (size ≥ mmap_cutover) → L3(BigCache)
├─ Step1: Site Rules ルックアップO(1)
│ hit? → route(L1/L2/L3)・policy(KEEP/FREE/DONTNEED)・shard を即適用
└─ Step2: クラス既定FrozenPolicyへフォールバック
- ≤1KiB かつ tinyslab[class]=on → L1
- ≤32KiB かつ midpool[class]=on → L2
- else → L3 or sys
```
**優先順位**: `Site Rule クラス既定 最終フォールバック`
---
## 🏗️ **データ構造16B エントリ)**
### **SiteRule 構造体**
```c
typedef struct {
uint8_t tag; // site hash 上位ビット
uint8_t cls; // size_class
uint8_t route; // 0:L1 1:L2 2:L3
uint8_t policy; // 0:KEEP 1:FREE 2:DONTNEED
uint8_t shard; // pool shard idL2 固定用)
uint8_t budget; // slab 上限ヒント
uint16_t ttl; // 自然蒸発(短命ルールの削除)
} SiteRule;
```
**メモリ使用量**: 16B × 2048 = **32KB のみ**
### **ルックアップO(1) 4-probe**
```c
static SiteRule g_site_rules[2048]; // 32KB
static int g_rule_count = 0;
static inline bool lookup_site_rule(uintptr_t site, uint8_t cls, SiteRule* out) {
uint32_t hash = (site >> 4) ^ cls;
uint32_t idx = hash & 0x7FF; // 2048-1
for (int i = 0; i < 4; i++) { // 4-probe
SiteRule* r = &g_site_rules[(idx + i) & 0x7FF];
if (r->tag == (hash >> 11) && r->cls == cls) {
*out = *r;
return true;
}
if (r->tag == 0) break; // 空スロット
}
return false;
}
```
---
## ⚡ **Phase 6.9.1 Pool との相乗効果**
### **Pool が活きるシナリオ**
```
例1: JSON Parser
site_id = 0x401234 (parse_json+0x20)
Site Rule: (0x401234, 8KB) → L2_POOL + KEEP + shard_3
効果:
- 8KB 割当 → 常に shard_3 の Pool へ
- 同じ shard に粘着 → キャッシュヒット率 ↑
- KEEP policy → madvise なし → syscall 削減
例2: MIR Builder
site_id = 0x402100 (build_mir+0x50)
Site Rule: (0x402100, 32KB) → L2_POOL + FREE + shard_7
効果:
- 32KB 割当 → shard_7 固定
- FREE policy → MADV_FREE のみVA 保持)
```
**既存の PoolPhase 6.9.1)がそのまま使える**
Site Rules は「どの Pool/shard を使うか」を決めるだけ。
---
## 📈 **段階的導入プラン**
### **Phase 6.10.1: Site Rules MVP**
**実装内容**:
1. `SiteRule` 構造体追加16B × 2048
2. `lookup_site_rule()` 実装O(1) 4-probe
3. `hak_alloc_at()` に Site Rules ルックアップ追加
4. 手動で Top-10 site のルール設定(テスト用)
**期待される効果**:
- L2 Pool hit 率: 0% → 20%
- minor faults: -10%
### **Phase 6.10.2: ELO 学習自動化**
**実装内容**:
1.`(site, class)` の使用頻度・再利用間隔を記録
2. Top-K上位 100 siteのみ Site Rule 作成
3. 勝率 > 60% で FROZEN へ
**期待される効果**:
- L2 Pool hit 率: 20% → 40%
- minor faults: -20%
### **Phase 6.10.3: TTL/Adoption Gate**
**実装内容**:
1. TTL30分で自然蒸発
2. Adoption Gate勝率 < 60% は不採用
3. 予算整合性チェックpool_budget 超過防止)
**期待される効果**:
- メモリ肥大化防止
- 安定性向上
---
## 🎯 **ベンチマーク目標**
### **mixed scenario**
```
現状Phase 6.9.1:
- hakmem: +66% vs mimalloc
目標Phase 6.10:
- hakmem: +20% 以内 vs mimalloc
改善の内訳:
- L2 Pool hit 率: 0% → 40%Site Rules で誘導)
- minor faults: -30%KEEP policy + shard 粘着)
- syscall (madvise): -50%KEEP/FREE の使い分け)
```
### **mir scenario**
```
現状Phase 6.9.1:
- hakmem: +52% vs mimalloc
- 原因: 256KB は Pool 範囲外
目標Phase 6.10:
- hakmem: +30% 以内 vs mimalloc
- 対策: Site Rules で BigCache への誘導改善
```
---
## ⚠️ **注意点(落とし穴回避)**
### **1. Top-K のみ保持(メモリ肥大化防止)**
**NG**: 全 site を追跡(数万件 → 数MB
**OK**: Top-100 のみ2048 スロット × 16B = 32KB
### **2. TTL で自然蒸発**
```c
// 使われなくなった Site Rule は自動削除
if (now - rule->last_used > TTL_SEC) {
rule->tag = 0; // 削除
}
```
**TTL**: 1800秒30分
### **3. Adoption Gate採択ゲート**
```c
// 勝率 < 60% の規則は採用しない
if (rule->win_rate < 0.6) {
return false; // フォールバック
}
```
### **4. 予算整合性**
```c
// クラスごとの pool_budget既定
// Site Rule の budget_hint の総和が超過しないよう
// ELO で水充填water-filling
```
---
## 🔧 **実装例(擬似コード)**
### **Phase 6.10.1 MVP**
```c
void* hak_alloc_at(size_t size, hak_callsite_t site) {
if (!g_initialized) hak_init();
// Step 0: 例外早抜け
if (size >= MMAP_CUTOVER) {
return bigcache_try_alloc(size, site);
}
uint8_t cls = classify_size(size); // branchless
// Step 1: Site Rules ルックアップO(1)
SiteRule r;
if (lookup_site_rule(site, cls, &r)) {
// Site Rule 優先: L1/L2/L3 直接ルーティング
switch (r.route) {
case ROUTE_L1_TINYSLAB:
return tinyslab_alloc(r.shard, cls);
case ROUTE_L2_POOL:
return hak_pool_try_alloc(size, site); // ← Phase 6.9.1 Pool
case ROUTE_L3_BIGCACHE:
return bigcache_try_alloc(size, site);
}
}
// Step 2: フォールバック(クラス既定)
size_t threshold = hak_elo_get_threshold(...);
if (size >= threshold) {
return alloc_mmap(size);
} else {
return alloc_malloc(size);
}
}
```
---
## 📊 **成功判定の見える化**
### **メトリクス**
```c
typedef struct {
uint64_t rule_hits; // Site Rule 適用回数
uint64_t rule_misses; // フォールバック回数
uint64_t pool_mid_hits; // L2 Pool ヒット回数
uint64_t minor_faults; // minor page faults
uint64_t madvise_calls; // madvise syscall 回数
} SiteRulesStats;
```
### **目標値**
```
rule_hit_rate = rule_hits / (rule_hits + rule_misses)
目標: > 40%
pool_mid_hit_rate = pool_mid_hits / total_mid_allocs
目標: > 40%Phase 6.9.1 の 0% から改善)
minor_faults/sec
目標: -30%Phase 6.9.1 比)
madvise_calls/sec
目標: -50%KEEP policy で削減)
```
---
## 🎓 **論文への貢献**
### **新規性**
1. **アドレス直結キャッシュの自動最適化**
- 既存研究: サイズクラスのみtcmalloc/jemalloc
- hakmem: call-site × size_class の 2D 最適化
2. **ELO による Site Rules 自動生成**
- 手動チューニング不要
- Top-K のみ保持で軽量32KB
3. **2層合成アーキテクチャ**
- Site Rule局所最適+ クラス既定(全体最適)
- フォールバックで安全性保証
### **論文タイトル案**
**"Site-Aware Memory Allocation with Automatic Cache Routing via ELO Learning"**
**要旨**:
- Call-site profilingPhase 6.0-6.9)の次のステップ
- (site, class) → cache route の直接結びつけ
- ELO で自動学習・凍結
- 32KB のオーバーヘッドで 40% の中粒性能改善
---
## 🚀 **次のアクション**
### **Phase 6.10.1 実装MVP**
1.**設計完了** - このドキュメント
2.**データ構造実装** - `hakmem_site_rules.h/c`
3.**ルックアップ実装** - `lookup_site_rule()`
4.**hak_alloc_at() 統合** - Site Rules ルート追加
5.**手動ルール設定** - Top-10 siteテスト用
6.**ベンチマーク** - mixed/mir で効果測定
### **Phase 6.10.2 以降**
- ELO 学習自動化
- TTL/Adoption Gate
- 論文執筆
---
## 🚀 **ChatGPT Pro 推奨の最適化**2025-10-21 追記)
### **システム名: SACS (Site-Aware Adaptive Cache System)**
**読み方**: サックス(サクサク動く!)
**特徴**:
- **S**ite-Aware: Call-site アドレス直結
- **A**daptive: ELO + Dynamic Thresholds
- **C**ache **S**ystem: L1/L2/L3 階層
**論文タイトル案**:
> "SACS: Site-Aware Adaptive Cache System with ELO-driven Dynamic Optimization"
---
### **1. non-empty ビットマップ**O(1) 空クラススキップ)
**目的**: 空フリーリストのループ探索を O(1) で解決
```c
// shardごとに: bit i = クラスiに空きがある
typedef struct {
PoolBlock* freelist[POOL_NUM_CLASSES];
uint32_t nonempty_mask; // NEW: ビットマップ
} PoolShard;
static inline int find_nonempty_class(uint32_t mask, int idx) {
uint32_t avail = mask & (~0u << idx); // idx以上の部分
if (avail) {
return __builtin_ctz(avail); // 最下位1bitの位置O(1)
}
return -1; // 空なし
}
// フリーリスト操作時にビット更新
void update_nonempty_mask(PoolShard* shard, int class_idx) {
if (shard->freelist[class_idx]) {
shard->nonempty_mask |= (1u << class_idx); // ビット立てる
} else {
shard->nonempty_mask &= ~(1u << class_idx); // ビット落とす
}
}
```
**効果**:
- 空クラス探索: O(n) → O(1)
- p99 改善: ~5-10 ns探索ループ削減
---
### **2. branchless クラス決定**LUT化
**現状の問題**: if文連続で O(n)
```c
// ❌ 現状O(n)
int hak_pool_get_class_index(size_t size) {
if (size <= 2048) return 0;
if (size <= 4096) return 1;
if (size <= 8192) return 2;
if (size <= 16384) return 3;
if (size <= 32768) return 4;
return -1;
}
```
**最適化**: LUT で O(1)
```c
// ✅ 最適化O(1)
static const uint8_t SIZE_TO_CLASS[33] = {
0,0,0, // 0-2KB → Class 0
1,1, // 3-4KB → Class 1
2,2,2,2, // 5-8KB → Class 2
3,3,3,3,3,3,3,3, // 9-16KB → Class 3
4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4 // 17-32KB → Class 4
};
static inline int ceil_class_idx(size_t sz) {
uint32_t kb = (sz + 1023) >> 10; // 切り上げてKB単位
return (kb < 33) ? SIZE_TO_CLASS[kb] : -1;
}
```
**効果**:
- クラス決定: 5分岐 → LUT読み1回
- p99 改善: ~2-5 ns
---
### **3. memset 禁止**(デバッグモードのみ)
**問題**: `memset(user_ptr, 0, class_size)` が致命的な遅延の源
```c
// ❌ 削除すべきhakmem_pool.c:218
memset(user_ptr, 0, g_class_sizes[class_idx]);
```
**最適化**: デバッグモードのみ有効化
```c
void* hak_pool_try_alloc(size_t size, uintptr_t site_id) {
// ... allocation logic ...
void* user_ptr = (char*)raw + HEADER_SIZE;
// ✅ ゼロ化禁止calloc以外
#ifdef HAKMEM_DEBUG_SANITIZE
memset(user_ptr, 0xA5, g_class_sizes[class_idx]); // パターン埋め
#endif
return user_ptr;
}
```
**効果**:
- 8KB memset 削減: ~50-100 ns
- 32KB memset 削減: ~200-400 ns
- **合計**: 2-32KB 範囲で 15-25% 高速化
---
### **4. L1 TinySlab の位置づけ**Phase 6.12+
**コンセプト**: 1KB以下は専用のシンプルなキャッシュプール
```c
// L1 TinySlab: 16B1KB の 8クラス
#define TINYSLAB_CLASSES 8
static const size_t g_tinyslab_sizes[TINYSLAB_CLASSES] = {
16, 32, 64, 128, 256, 512, 768, 1024
};
// TLS (Thread-Local Storage)
__thread struct {
FreeBlock* freelist[TINYSLAB_CLASSES];
uint8_t nonempty_mask; // ビットマップ
} g_tinyslab_tls;
```
**特徴**:
- **シンプル**: フリーリストのみSite Rules なし)
- **TLS**: スレッドローカルでロックフリー
- **KEEP policy**: madvise 呼ばない
- **軽量**: 総メモリ < 1MB/thread
**実装タイミング**: Phase 6.12+Phase 6.10/6.11 完了後)
---
### **5. キャッシュクラス数の最適解**
**推奨**: **5クラス現状維持**
```
L2 MidPool:
Class 0: 2KB
Class 1: 4KB
Class 2: 8KB
Class 3: 16KB
Class 4: 32KB
```
**理由**:
- mimalloc/jemalloc も類似粒度
- 5クラスで管理コスト最小
- W_MAX で柔軟性確保20KB → 32KB 許容)
- ビットマップ + LUT で将来拡張も O(1) のまま
**将来の拡張**Phase 6.12+:
```
7クラス: 2/4/8/12/16/24/32KB
↑ 12KB と 24KB を追加(断片化削減)
```
---
### **最適化の優先度**
| 最適化 | 効果p99 改善) | 実装コスト | Phase |
|--------|----------------|-----------|-------|
| **memset 禁止** | 15-25% | 極小 | 6.10.1 |
| **non-empty bitmap** | 5-10 ns | 小 | 6.10.1 |
| **branchless class** | 2-5 ns | 極小 | 6.10.1 |
| **L1 TinySlab** | 30-50%(小サイズ) | 中 | 6.12+ |
**Phase 6.10.1 で実装すべき**: memset 禁止 + non-empty bitmap + branchless class
---
**Generated**: 2025-10-21
**発案者**: tomoakiユーザー
**詳細設計**: ChatGPT Pro
**実装**: Phase 6.10.1 から開始予定
**システム名**: SACS (Site-Aware Adaptive Cache System)