Files
hakmem/PARTIAL_RELEASE_INVESTIGATION_REPORT_20251205.md
Moe Charm (CI) 2b2b607957 Add workload comparison and madvise investigation reports
Key findings from 2025-12-05 session:
1. HAKMEM vs mimalloc: 27x slower (4.5M vs 122M ops/s)
2. Root cause investigation: madvise 1081 calls vs mimalloc 0 calls
3. madvise disable test: -15% performance (worse, not better!)
4. Conclusion: MADV_POPULATE_WRITE is actually helping, not hurting
5. ChatGPT was right: time to move to user-space optimization phase

Reports added:
- WORKLOAD_COMPARISON_20251205.md
- PARTIAL_RELEASE_INVESTIGATION_REPORT_20251205.md

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 13:31:45 +09:00

312 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Partial Release無効化テスト結果レポート
**調査日**: 2025年12月5日
**目的**: madvise()がCPU時間の58%を占めている問題を解決するため、Partial Release機能の影響を調査
---
## 📊 測定結果サマリー
### ベンチマーク性能比較
| 条件 | Run1 (ops/s) | Run2 (ops/s) | Run3 (ops/s) | 平均 (ops/s) | 改善率 |
|-----|--------------|--------------|--------------|--------------|--------|
| **Partial Release有効** (baseline) | 4,784,048 | 4,709,849 | 4,739,815 | **4,744,571** | - |
| **Partial Release無効** (HAKMEM_TINY_SS_PARTIAL=0) | 4,920,803 | 4,778,194 | 4,889,397 | **4,862,798** | **+2.5%** |
### perf統計比較
| 指標 | Partial Release有効 | Partial Release無効 | 差 |
|-----|---------------------|---------------------|---|
| **cycles** | 1,164,151,908 | 1,160,123,269 | -4M (-0.3%) |
| **page-faults** | 6,759 | 6,760 | +1 (変化なし) |
| **cache-misses** | 8,210,925 | 8,142,282 | -69K (-0.8%) |
| **L1-dcache-load-misses** | 17,780,041 (3.99%) | 17,624,374 (3.96%) | -156K (-0.9%) |
| **user時間** | 0.030s | 0.035s | +0.005s |
| **sys時間** | 0.247s | 0.252s | +0.005s |
| **実行時間** | 0.278s | 0.288s | +0.010s |
---
## 🔍 重大な発見Partial Release無効化してもmadvise()は減らない!
### strace syscall統計
#### HAKMEM_TINY_SS_PARTIAL=0Partial Release無効
```
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
91.21 0.173846 160 1082 madvise
5.30 0.010094 9 1092 munmap
3.20 0.006106 5 1113 mmap
```
#### デフォルトPartial Release有効
```
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
91.86 0.159657 147 1081 madvise
4.96 0.008628 7 1091 munmap
2.90 0.005043 4 1113 mmap
```
**結論**: **madvise呼び出し回数はほぼ同じ1081-1082回**
---
## 🔬 詳細調査madvise()の発生源を特定
### 発見した環境変数
| 環境変数名 | デフォルト値 | 用途 | 効果 |
|-----------|-------------|------|------|
| **HAKMEM_TINY_SS_PARTIAL** | 1 (有効) | SuperSlabのPartial Release制御 | **効果なし** (2.5%改善は誤差範囲) |
| **HAKMEM_TINY_SS_PARTIAL_INTERVAL** | 4 | Partial Release実行間隔 | - |
| **HAKMEM_DISABLE_BATCH** | 0 (有効) | Batch madvise制御 | **効果なし** |
| **HAKMEM_SS_PREFAULT** | 0 (OFF) | SuperSlab prefault制御 | **効果なし** |
### 全機能無効化テスト
```bash
HAKMEM_DISABLE_BATCH=1 HAKMEM_TINY_SS_PARTIAL=0 HAKMEM_SS_PREFAULT=0 strace -c ./bench_random_mixed_hakmem 1000000 400 1
```
**結果**: **依然として1081回のmadvise呼び出し** (91.74% of syscall time)
---
## 💡 根本原因の特定
### madvise()呼び出し箇所の完全マップ
straceで実際の呼び出しを確認:
```bash
strace -e madvise ./bench_random_mixed_hakmem 1000000 400 1
```
**全てのmadvise呼び出しは `MADV_POPULATE_WRITE` であることが判明!**
```
madvise(0x73f0e2680000, 524288, MADV_POPULATE_WRITE) = 0
madvise(0x73f0e2300000, 524288, MADV_POPULATE_WRITE) = 0
madvise(0x73f0e2200000, 524288, MADV_POPULATE_WRITE) = 0
...
```
### 発生源コード
#### 1. **主犯: `core/superslab_cache.c` (Fallback Path)**
`/mnt/workdisk/public_share/hakmem/core/superslab_cache.c:113`
```c
// Pre-fault pages in fallback path (only after trim to actual SuperSlab size)
// This is critical: we MUST touch the pages after munmap() to establish valid mappings
// CRITICAL FIX (2025-12-05): Use MADV_POPULATE_WRITE for efficiency
#ifdef MADV_POPULATE_WRITE
int ret = madvise(ptr, ss_size, MADV_POPULATE_WRITE); // ← ここが主犯!
if (ret != 0) {
// Fallback: explicit memset
memset(ptr, 0, ss_size);
}
#else
// Fallback for kernels < 5.14
memset(ptr, 0, ss_size);
#endif
```
**問題点**:
- この`madvise()`は**環境変数で無効化できない**
- Fallback pathが常に使われているMAP_ALIGNED_SUPERが失敗
- munmap trim後に必ずMADV_POPULATE_WRITEが実行される
#### 2. **`core/box/ss_os_acquire_box.c:179`** (populate=1の場合)
```c
#ifdef MADV_POPULATE_WRITE
if (populate) {
int ret = madvise(ptr, ss_size, MADV_POPULATE_WRITE);
...
}
#endif
```
#### 3. **`core/hakmem_tiny_intel.inc:692`** (Partial Release - 今回無効化を試した箇所)
```c
static inline void superslab_partial_release(SuperSlab* ss, uint32_t epoch) {
#if defined(MADV_DONTNEED)
if (!g_ss_partial_enable) return; // ← HAKMEM_TINY_SS_PARTIAL=0で無効化
...
if (madvise(ss, len, MADV_DONTNEED) == 0) {
ss->partial_epoch = epoch;
}
#endif
}
```
**重要**: この関数は今回のベンチマークでは**ほとんど呼ばれていない**
#### 4. **`core/hakmem_batch.c:120,123`** (Batch madvise)
```c
int ret = madvise(ptr, size, MADV_FREE);
if (ret != 0) {
ret = madvise(ptr, size, MADV_DONTNEED);
}
```
---
## 📈 アーキテクチャ分析
### madvise()の4つの用途
| # | 場所 | 種類 | 目的 | 頻度 | 環境変数制御 |
|---|------|------|------|------|-------------|
| 1 | `superslab_cache.c:113` | **MADV_POPULATE_WRITE** | Fallback path でのページ確保 | **超高頻度** (~1000回) | ❌ **不可** |
| 2 | `ss_os_acquire_box.c:179` | **MADV_POPULATE_WRITE** | populate=1時のprefault | populate時のみ | HAKMEM_SS_PREFAULT |
| 3 | `hakmem_tiny_intel.inc:692` | **MADV_DONTNEED** | Partial Release (空SuperSlabのメモリ返却) | 低頻度 | ✅ HAKMEM_TINY_SS_PARTIAL |
| 4 | `hakmem_batch.c:120` | **MADV_FREE/DONTNEED** | Batch madvise (大きいalloc解放時) | 中頻度 | ✅ HAKMEM_DISABLE_BATCH |
### 今回のベンチマークでの実態
- **1081回のmadvise呼び出し全てが `MADV_POPULATE_WRITE`**
- これは**#1のFallback Path**から発生している
- **#3 Partial Release (MADV_DONTNEED) は今回のベンチマークではほとんど実行されていない**
---
## 🎯 結論
### 主要な発見
1. **`HAKMEM_TINY_SS_PARTIAL=0` は効果がほぼない(+2.5%程度)**
- 理由: このベンチマークではPartial Releaseがほとんど発動していない
- 1081回のmadviseのうち、MADV_DONTNEEDはほぼゼロ
2. **真の問題は `superslab_cache.c` のFallback Path**
- 全てのSuperSlab割り当てで MADV_POPULATE_WRITE が実行される
- 環境変数では制御不可能
- コメントによると「munmap trim後の再確保に必須」
3. **前回のperf分析での58%のmadvise時間は別の要因だった可能性**
- 前回のベンチマーク: `bench_random_mixed.c` (16-1024バイト)
- 今回のベンチマーク: `bench_random_mixed_hakmem 1000000 400 1` (16-1040バイト)
- ワークロードが異なるため、Partial Releaseの発動頻度も異なる
4. **性能への影響は限定的**
- MADV_POPULATE_WRITEは割り当て時の1回のみホットパスではない
- ウォームアップ後はSuperSlabの再利用で新規madviseは減る
- syscall時間の91%でも、全体の実行時間は0.25s程度
---
## 🔧 推奨される次のステップ
### Option 1: Fallback Path の最適化(根本対策)
**問題**: MAP_ALIGNED_SUPERが失敗し、常にFallback Pathが使われている
**調査項目**:
1. なぜMAP_ALIGNED_SUPERが失敗するのか
2. FreeBSDでは成功するが、Linuxでは失敗する
3. Fallback PathでのMADV_POPULATE_WRITEは本当に必須か
**改善案**:
```c
// Option A: 環境変数で制御可能にする
static int prefault_disabled = -1;
if (prefault_disabled == -1) {
const char* env = getenv("HAKMEM_NO_FALLBACK_PREFAULT");
prefault_disabled = (env && *env && *env != '0') ? 1 : 0;
}
if (!prefault_disabled) {
madvise(ptr, ss_size, MADV_POPULATE_WRITE);
}
// Option B: memsetフォールバックを常に使うsyscall削減
memset(ptr, 0, ss_size); // syscallなし、ただしCPU使用量増加
```
### Option 2: ワークロード別の最適化
**異なるベンチマークで再測定**:
```bash
# より長時間のベンチマークPartial Releaseが発動しやすい
./bench_random_mixed_hakmem 10000000 400 1
# より大きいワーキングセットSuperSlabの入れ替えが多い
./bench_random_mixed_hakmem 1000000 4000 1
```
### Option 3: perf record で詳細プロファイリング
**前回報告されていた58%のmadviseを再現**:
```bash
# 前回と同じベンチマーク条件で測定
perf record -g ./bench_random_mixed.c の該当コマンド
perf report --stdio
```
---
## 📋 コード参照
### 関連ファイル
- `/mnt/workdisk/public_share/hakmem/core/superslab_cache.c:113` - MADV_POPULATE_WRITE (主犯)
- `/mnt/workdisk/public_share/hakmem/core/box/ss_os_acquire_box.c:179` - MADV_POPULATE_WRITE (populate時)
- `/mnt/workdisk/public_share/hakmem/core/hakmem_tiny_intel.inc:685-698` - superslab_partial_release (MADV_DONTNEED)
- `/mnt/workdisk/public_share/hakmem/core/hakmem_batch.c:120` - Batch madvise (MADV_FREE/DONTNEED)
- `/mnt/workdisk/public_share/hakmem/core/hakmem_config.c:231-234` - HAKMEM_DISABLE_BATCH の処理
- `/mnt/workdisk/public_share/hakmem/core/hakmem_tiny_lifecycle.inc:78-90` - HAKMEM_TINY_SS_PARTIAL の処理
- `/mnt/workdisk/public_share/hakmem/core/box/ss_prefault_box.h:35-54` - HAKMEM_SS_PREFAULT の処理
---
## 🧪 再現手順
### 環境変数一覧
```bash
# Partial Release無効化効果: +2.5%程度)
export HAKMEM_TINY_SS_PARTIAL=0
# Batch madvise無効化効果: なし)
export HAKMEM_DISABLE_BATCH=1
# SuperSlab prefault無効化効果: なし)
export HAKMEM_SS_PREFAULT=0
```
### ベンチマーク実行
```bash
# ベースライン
./bench_random_mixed_hakmem 1000000 400 1
# Partial Release無効
HAKMEM_TINY_SS_PARTIAL=0 ./bench_random_mixed_hakmem 1000000 400 1
# 全無効化
HAKMEM_DISABLE_BATCH=1 HAKMEM_TINY_SS_PARTIAL=0 HAKMEM_SS_PREFAULT=0 ./bench_random_mixed_hakmem 1000000 400 1
# syscall詳細
strace -c ./bench_random_mixed_hakmem 1000000 400 1
strace -e madvise ./bench_random_mixed_hakmem 1000000 400 1 2>&1 | head -30
```
---
## 📚 参考資料
- `PERF_ANALYSIS_16_1024B_20251205.md` - 前回のperf分析madvise 58%問題)
- `LAZY_ZEROING_IMPLEMENTATION_RESULTS_20251204.md` - MADV_DONTNEED最適化の失敗例
- `EXPLICIT_PREFAULT_IMPLEMENTATION_REPORT_20251205.md` - MADV_POPULATE_WRITE導入の経緯
---
**報告者**: Claude Code
**最終更新**: 2025年12月5日