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>
582 lines
14 KiB
Markdown
582 lines
14 KiB
Markdown
# 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 id(L2 固定用)
|
||
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 保持)
|
||
```
|
||
|
||
**既存の Pool(Phase 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. TTL(30分)で自然蒸発
|
||
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 profiling(Phase 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: 16B~1KB の 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)
|