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>
14 KiB
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 構造体
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)
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
実装内容:
SiteRule構造体追加(16B × 2048)lookup_site_rule()実装(O(1) 4-probe)hak_alloc_at()に Site Rules ルックアップ追加- 手動で Top-10 site のルール設定(テスト用)
期待される効果:
- L2 Pool hit 率: 0% → 20%
- minor faults: -10%
Phase 6.10.2: ELO 学習自動化
実装内容:
- 各
(site, class)の使用頻度・再利用間隔を記録 - Top-K(上位 100 site)のみ Site Rule 作成
- 勝率 > 60% で FROZEN へ
期待される効果:
- L2 Pool hit 率: 20% → 40%
- minor faults: -20%
Phase 6.10.3: TTL/Adoption Gate
実装内容:
- TTL(30分)で自然蒸発
- Adoption Gate(勝率 < 60% は不採用)
- 予算整合性チェック(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 で自然蒸発
// 使われなくなった Site Rule は自動削除
if (now - rule->last_used > TTL_SEC) {
rule->tag = 0; // 削除
}
TTL: 1800秒(30分)
3. Adoption Gate(採択ゲート)
// 勝率 < 60% の規則は採用しない
if (rule->win_rate < 0.6) {
return false; // フォールバック
}
4. 予算整合性
// クラスごとの pool_budget(既定)と
// Site Rule の budget_hint の総和が超過しないよう
// ELO で水充填(water-filling)
🔧 実装例(擬似コード)
Phase 6.10.1 MVP
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);
}
}
📊 成功判定の見える化
メトリクス
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 で削減)
🎓 論文への貢献
新規性
-
アドレス直結キャッシュの自動最適化
- 既存研究: サイズクラスのみ(tcmalloc/jemalloc)
- hakmem: call-site × size_class の 2D 最適化
-
ELO による Site Rules 自動生成
- 手動チューニング不要
- Top-K のみ保持で軽量(32KB)
-
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)
- ✅ 設計完了 - このドキュメント
- ⏳ データ構造実装 -
hakmem_site_rules.h/c - ⏳ ルックアップ実装 -
lookup_site_rule() - ⏳ hak_alloc_at() 統合 - Site Rules ルート追加
- ⏳ 手動ルール設定 - Top-10 site(テスト用)
- ⏳ ベンチマーク - mixed/mir で効果測定
Phase 6.10.2 以降
- ELO 学習自動化
- TTL/Adoption Gate
- 論文執筆
🚀 ChatGPT Pro 推奨の最適化(2025-10-21 追記)
システム名: SACS (Site-Aware Adaptive Cache System)
読み方: サックス(サクサク動く!)
特徴:
- Site-Aware: Call-site アドレス直結
- Adaptive: ELO + Dynamic Thresholds
- Cache System: L1/L2/L3 階層
論文タイトル案:
"SACS: Site-Aware Adaptive Cache System with ELO-driven Dynamic Optimization"
1. non-empty ビットマップ(O(1) 空クラススキップ)
目的: 空フリーリストのループ探索を O(1) で解決
// 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)
// ❌ 現状(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)
// ✅ 最適化(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) が致命的な遅延の源
// ❌ 削除すべき(hakmem_pool.c:218)
memset(user_ptr, 0, g_class_sizes[class_idx]);
最適化: デバッグモードのみ有効化
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以下は専用のシンプルなキャッシュプール
// 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)