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>
261 lines
7.0 KiB
Markdown
261 lines
7.0 KiB
Markdown
# Benchmarking Checklist - 二度と間違えないために
|
||
|
||
## ❌ **何が起きたか(2025-10-26 事件)**
|
||
|
||
### 問題
|
||
Phase 1-4.1 の全てのベンチマークで **glibc malloc** を測定していた。
|
||
hakmem の性能を測定していると思い込んでいたが、実際は system allocator だった。
|
||
|
||
### 発覚の経緯
|
||
1. Phase 3: 391 M ops/sec と報告
|
||
2. Phase 4: 373 M ops/sec に退行
|
||
3. Phase 4.1: 381 M ops/sec に改善
|
||
4. Phase 4.2 で Makefile を修正して hakmem を正しくリンク
|
||
5. **103 M ops/sec** という衝撃の結果
|
||
6. → 全ての測定が glibc malloc だったと判明
|
||
|
||
### 根本原因
|
||
```makefile
|
||
# 以前(ターゲットなし)
|
||
make bench_tiny
|
||
↓
|
||
# Makefileの暗黙ルール
|
||
gcc bench_tiny.c -o bench_tiny # hakmem とリンクせず!
|
||
```
|
||
|
||
bench_tiny.c は単に `malloc/free` を呼ぶだけなので、system allocator が使われた。
|
||
|
||
---
|
||
|
||
## ✅ **二度と起こらないための対策**
|
||
|
||
### 1. Makefile の明示的なターゲット(完了)
|
||
|
||
```makefile
|
||
# Phase 4: Tiny Pool benchmarks (properly linked with hakmem)
|
||
TINY_BENCH_OBJS = hakmem.o hakmem_config.o ... (全ての hakmem オブジェクト)
|
||
|
||
bench_tiny: bench_tiny.o $(TINY_BENCH_OBJS)
|
||
$(CC) -o $@ $^ $(LDFLAGS)
|
||
@echo "✓ bench_tiny built with hakmem" # 明確なメッセージ
|
||
|
||
bench_tiny_mt: bench_tiny_mt.o $(TINY_BENCH_OBJS)
|
||
$(CC) -o $@ $^ $(LDFLAGS)
|
||
@echo "✓ bench_tiny_mt built with hakmem"
|
||
|
||
test_mf2: test_mf2.o $(TINY_BENCH_OBJS)
|
||
$(CC) -o $@ $^ $(LDFLAGS)
|
||
@echo "✓ test_mf2 built with hakmem"
|
||
```
|
||
|
||
### 2. ベンチマーク実行前のチェックリスト
|
||
|
||
#### **Step 1: ビルド確認**
|
||
```bash
|
||
# 必ず make clean してから
|
||
make clean && make bench_tiny
|
||
|
||
# ビルドメッセージを確認
|
||
# 期待: "✓ bench_tiny built with hakmem"
|
||
```
|
||
|
||
#### **Step 2: リンク確認**
|
||
```bash
|
||
# 方法 A: シンボル確認(推奨)
|
||
nm ./bench_tiny | grep hak_tiny_alloc
|
||
# 期待: hak_tiny_alloc のシンボルが見つかる
|
||
# NG: "no symbols" または何も出ない
|
||
|
||
# 方法 B: ldd 確認(共有ライブラリの場合)
|
||
ldd ./bench_tiny
|
||
# hakmem が静的リンクされているので libhakmem.so は出ないが、
|
||
# libc.so, libpthread.so, libm.so が出ることを確認
|
||
|
||
# 方法 C: size 確認
|
||
size ./bench_tiny
|
||
# 期待: text サイズが大きい(hakmem コードが含まれる)
|
||
# NG: text サイズが数KB程度(bench_tiny.c のみ)
|
||
```
|
||
|
||
**正しい例**:
|
||
```bash
|
||
$ nm ./bench_tiny | grep hak_tiny_alloc
|
||
000000000000b2a0 T hak_tiny_alloc
|
||
000000000000cd10 T hak_tiny_alloc_superslab
|
||
|
||
$ size ./bench_tiny
|
||
text data bss dec hex filename
|
||
89234 3456 12800 105490 19c12 bench_tiny
|
||
```
|
||
|
||
**間違いの例(glibc malloc を使っている)**:
|
||
```bash
|
||
$ nm ./bench_tiny | grep hak_tiny_alloc
|
||
# 何も出ない!
|
||
|
||
$ size ./bench_tiny
|
||
text data bss dec hex filename
|
||
2345 128 64 2537 9e9 bench_tiny
|
||
```
|
||
|
||
#### **Step 3: 実行前の sanity check**
|
||
```bash
|
||
# hakmem 初期化メッセージを確認(HAKMEM_DEBUG_VERBOSE=1 の場合)
|
||
# または stats 出力で hakmem が動いているか確認
|
||
./bench_tiny 2>&1 | head -20
|
||
```
|
||
|
||
---
|
||
|
||
### 3. ベンチマークスクリプトの標準化
|
||
|
||
**推奨**: `make bench_tiny` を使う(Makefile で正しく設定済み)
|
||
|
||
**非推奨**: 手動で gcc を叩く(リンク漏れのリスク)
|
||
|
||
---
|
||
|
||
### 4. ドキュメント化されたワークフロー
|
||
|
||
#### **標準ベンチマーク手順**
|
||
```bash
|
||
# 1. Clean build
|
||
make clean
|
||
|
||
# 2. Build with verification
|
||
make bench_tiny 2>&1 | tee build.log
|
||
grep "✓ bench_tiny built with hakmem" build.log || echo "ERROR: hakmem not linked!"
|
||
|
||
# 3. Verify linkage
|
||
nm ./bench_tiny | grep hak_tiny_alloc || echo "ERROR: hakmem symbols not found!"
|
||
|
||
# 4. Run benchmark (3 times for stability)
|
||
for i in {1..3}; do
|
||
echo "=== Run $i/3 ==="
|
||
./bench_tiny 2>&1 | tail -6
|
||
echo ""
|
||
done
|
||
|
||
# 5. Compare with baseline
|
||
# (save results and compare)
|
||
```
|
||
|
||
---
|
||
|
||
### 5. 自動化された検証
|
||
|
||
#### **verify_bench.sh**(作成推奨)
|
||
```bash
|
||
#!/bin/bash
|
||
# verify_bench.sh - Verify benchmark binary is correctly linked
|
||
|
||
BINARY="$1"
|
||
if [ ! -f "$BINARY" ]; then
|
||
echo "❌ Binary not found: $BINARY"
|
||
exit 1
|
||
fi
|
||
|
||
# Check for hakmem symbols
|
||
if nm "$BINARY" | grep -q "hak_tiny_alloc"; then
|
||
echo "✅ hakmem symbols found"
|
||
else
|
||
echo "❌ hakmem symbols NOT found - using system allocator!"
|
||
exit 1
|
||
fi
|
||
|
||
# Check binary size (heuristic)
|
||
TEXT_SIZE=$(size "$BINARY" | awk 'NR==2 {print $1}')
|
||
if [ "$TEXT_SIZE" -gt 50000 ]; then
|
||
echo "✅ Binary size OK ($TEXT_SIZE bytes)"
|
||
else
|
||
echo "⚠️ Binary size suspiciously small ($TEXT_SIZE bytes)"
|
||
exit 1
|
||
fi
|
||
|
||
echo "✅ Verification passed: $BINARY"
|
||
```
|
||
|
||
**使用例**:
|
||
```bash
|
||
chmod +x verify_bench.sh
|
||
./verify_bench.sh ./bench_tiny
|
||
```
|
||
|
||
---
|
||
|
||
## 📊 **真の性能データ(Phase 4.2 で初めて正しく測定)**
|
||
|
||
### System malloc (glibc)
|
||
- **Throughput**: ~370 M ops/sec
|
||
- **Latency**: 2.7 ns/op
|
||
|
||
### hakmem (Phase 4.2)
|
||
- **Throughput**: **103 M ops/sec**
|
||
- **Latency**: **9.6 ns/op**
|
||
- **Gap**: glibc が **3.6倍速い**
|
||
|
||
### mimalloc (推定)
|
||
- **Latency**: 14 ns/op (ANALYSIS_SUMMARY.md)
|
||
- **Throughput**: ~71 M ops/sec
|
||
- **Note**: hakmem の方が速い可能性がある
|
||
|
||
---
|
||
|
||
## 🔄 **今後のベンチマーク protocol**
|
||
|
||
### Before every benchmark:
|
||
1. ✅ `make clean`
|
||
2. ✅ `make bench_tiny` (ビルドメッセージ確認)
|
||
3. ✅ `nm ./bench_tiny | grep hak_tiny_alloc` (シンボル確認)
|
||
4. ✅ Run benchmark 3 times
|
||
5. ✅ Compare with baseline
|
||
|
||
### After benchmark:
|
||
1. ✅ Record exact commit hash
|
||
2. ✅ Record build command
|
||
3. ✅ Record verification result
|
||
4. ✅ Save raw data
|
||
|
||
### Before claiming performance change:
|
||
1. ✅ Verify > 5% difference
|
||
2. ✅ Run 5+ times for statistical significance
|
||
3. ✅ Check for system noise (other processes)
|
||
4. ✅ Document what changed
|
||
|
||
---
|
||
|
||
## 📝 **Lessons Learned**
|
||
|
||
1. **Make の暗黙ルールは危険** - 明示的なターゲットを常に定義
|
||
2. **"動く" ≠ "正しい"** - malloc/free は常に動く(system allocator がある)
|
||
3. **測定前の検証が必須** - nm/ldd/size でリンク確認
|
||
4. **性能変化には根拠が必要** - コード変更と性能の因果関係を確認
|
||
5. **ドキュメント化** - チェックリストがないと同じ間違いを繰り返す
|
||
|
||
---
|
||
|
||
## 🎯 **Action Items**
|
||
|
||
- [x] Makefile に明示的ターゲット追加
|
||
- [x] BENCHMARKING_CHECKLIST.md 作成
|
||
- [ ] verify_bench.sh スクリプト作成(オプション)
|
||
- [ ] BENCHMARK_PROTOCOL.md 作成(詳細手順)
|
||
- [ ] Phase 0 (Baseline) の性能を測定し直す
|
||
- [ ] Phase 1-4 の性能を正しく測定し直す
|
||
|
||
---
|
||
|
||
## 🚫 **絶対にやってはいけないこと**
|
||
|
||
1. ❌ `make bench_tiny` なしで手動コンパイル
|
||
2. ❌ リンク確認なしでベンチマーク実行
|
||
3. ❌ 1回だけの測定で結論
|
||
4. ❌ 性能変化の原因を確認せずにコミット
|
||
5. ❌ ベースラインなしで「改善」を主張
|
||
|
||
---
|
||
|
||
## ✅ **今後は絶対に間違えない**
|
||
|
||
このチェックリストに従えば、二度と同じ間違いは起きませんにゃ!🐱✨
|