Phase 83-1 + Allocator Comparison: Switch dispatch fixed (NO-GO +0.32%), PROFILE correction, SCORECARD update

Key changes:
- Phase 83-1: Switch dispatch fixed mode (tiny_inline_slots_switch_dispatch_fixed_box) - NO-GO (marginal +0.32%, branch reduction negligible)
  Reason: lazy-init pattern already optimal, Phase 78-1 pattern shows diminishing returns

- Allocator comparison baseline update (10-run SSOT, WS=400, ITERS=20M):
  tcmalloc: 115.26M (92.33% of mimalloc)
  jemalloc: 97.39M (77.96% of mimalloc)
  system: 85.20M (68.24% of mimalloc)
  mimalloc: 124.82M (baseline)

- hakmem PROFILE correction: scripts/run_mixed_10_cleanenv.sh + run_allocator_quick_matrix.sh
  PROFILE explicitly set to MIXED_TINYV3_C7_SAFE for hakmem measurements
  Result: baseline stabilized to 55.53M (44.46% of mimalloc)
  Previous unstable measurement (35.57M) was due to profile leak

- Documentation:
  * PERFORMANCE_TARGETS_SCORECARD.md: Reference allocators + M1/M2 milestone status
  * PHASE83_1_SWITCH_DISPATCH_FIXED_RESULTS.md: Phase 83-1 analysis (NO-GO)
  * ALLOCATOR_COMPARISON_QUICK_RUNBOOK.md: Quick comparison procedure
  * ALLOCATOR_COMPARISON_SSOT.md: Detailed SSOT methodology

- M2 milestone status: 44.46% (target 55%, gap -10.54pp) - structural improvements needed

🤖 Generated with Claude Code
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Moe Charm (CI)
2025-12-18 18:50:00 +09:00
parent d5c1113b4c
commit 89a9212700
50 changed files with 4428 additions and 58 deletions

View File

@ -0,0 +1,141 @@
#!/usr/bin/env bash
set -euo pipefail
# Allocator comparison matrix using the SAME benchmark binary via LD_PRELOAD.
#
# Why:
# - Different binaries introduce layout tax (text size/I-cache) and can make hakmem look much worse/better.
# - This script uses `bench_random_mixed_system` as the single fixed binary and swaps allocators via LD_PRELOAD.
#
# What it runs:
# - system (no LD_PRELOAD)
# - hakmem (LD_PRELOAD=./libhakmem.so)
# - mimalloc (LD_PRELOAD=$MIMALLOC_SO) if provided
# - jemalloc (LD_PRELOAD=$JEMALLOC_SO) if provided
# - tcmalloc (LD_PRELOAD=$TCMALLOC_SO) if provided
#
# SSOT alignment:
# - Applies the same "cleanenv defaults" as `scripts/run_mixed_10_cleanenv.sh`.
# - IMPORTANT: never LD_PRELOAD the shell/script itself; apply LD_PRELOAD only to the benchmark binary exec.
#
# Usage:
# make bench_random_mixed_system shared
# export MIMALLOC_SO=/path/to/libmimalloc.so.2 # optional
# export JEMALLOC_SO=/path/to/libjemalloc.so.2 # optional
# export TCMALLOC_SO=/path/to/libtcmalloc.so # optional
# RUNS=10 scripts/run_allocator_preload_matrix.sh
#
# Tunables:
# HAKMEM_PROFILE=MIXED_TINYV3_C7_SAFE ITERS=20000000 WS=400 RUNS=10
root_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "${root_dir}"
profile="${HAKMEM_PROFILE:-MIXED_TINYV3_C7_SAFE}"
iters="${ITERS:-20000000}"
ws="${WS:-400}"
runs="${RUNS:-10}"
if [[ ! -x ./bench_random_mixed_system ]]; then
echo "[preload-matrix] Missing ./bench_random_mixed_system (build via: make bench_random_mixed_system)" >&2
exit 1
fi
extract_throughput() {
rg -o "Throughput = +[0-9]+ ops/s" | rg -o "[0-9]+"
}
stats_py='
import statistics,sys
xs=[int(x) for x in sys.stdin.read().strip().split() if x.strip()]
if not xs:
sys.exit(1)
xs_sorted=sorted(xs)
mean=sum(xs)/len(xs)
median=statistics.median(xs_sorted)
stdev=statistics.pstdev(xs) if len(xs)>1 else 0.0
cv=(stdev/mean*100.0) if mean>0 else 0.0
print(f"runs={len(xs)} mean={mean/1e6:.2f}M median={median/1e6:.2f}M cv={cv:.2f}% min={min(xs)/1e6:.2f}M max={max(xs)/1e6:.2f}M")
'
apply_cleanenv_defaults() {
# Keep reproducible even if user exported env vars.
case "${profile}" in
MIXED_TINYV3_C7_BALANCED)
export HAKMEM_SS_MEM_LEAN=1
export HAKMEM_SS_MEM_LEAN_DECOMMIT=OFF
export HAKMEM_SS_MEM_LEAN_TARGET_MB=10
;;
*)
export HAKMEM_SS_MEM_LEAN=0
export HAKMEM_SS_MEM_LEAN_DECOMMIT=OFF
export HAKMEM_SS_MEM_LEAN_TARGET_MB=10
;;
esac
# Force known research knobs OFF to avoid accidental carry-over.
export HAKMEM_TINY_HEADER_WRITE_ONCE=0
export HAKMEM_TINY_C7_PRESERVE_HEADER=0
export HAKMEM_TINY_TCACHE=0
export HAKMEM_TINY_TCACHE_CAP=64
export HAKMEM_MALLOC_TINY_DIRECT=0
export HAKMEM_FRONT_FASTLANE_ALLOC_LEGACY_DIRECT=0
export HAKMEM_FORCE_LIBC_ALLOC=0
export HAKMEM_ENV_SNAPSHOT_SHAPE=0
export HAKMEM_TINY_C7_ULTRA_HEADER_LIGHT=0
export HAKMEM_TINY_C2_LOCAL_CACHE=0
export HAKMEM_TINY_INLINE_SLOTS_SWITCHDISPATCH_FIXED=0
# Keep cleanenv aligned with promoted knobs.
export HAKMEM_FASTLANE_DIRECT=1
export HAKMEM_FREE_TINY_FAST_MONO_DUALHOT=1
export HAKMEM_FREE_TINY_FAST_MONO_LEGACY_DIRECT=1
export HAKMEM_WARM_POOL_SIZE=16
export HAKMEM_TINY_C4_INLINE_SLOTS=1
export HAKMEM_TINY_C5_INLINE_SLOTS=1
export HAKMEM_TINY_C6_INLINE_SLOTS=1
export HAKMEM_TINY_INLINE_SLOTS_FIXED=1
export HAKMEM_TINY_INLINE_SLOTS_SWITCHDISPATCH=1
}
run_preload_n() {
local label="$1"
local preload="$2"
echo ""
echo "== ${label} (profile=${profile}) =="
apply_cleanenv_defaults
for i in $(seq 1 "${runs}"); do
if [[ -n "${preload}" ]]; then
local preload_abs
preload_abs="$(realpath "${preload}")"
# Apply LD_PRELOAD ONLY to the benchmark binary exec (not to bash/rg/python).
HAKMEM_PROFILE="${profile}" LD_PRELOAD="${preload_abs}" \
./bench_random_mixed_system "${iters}" "${ws}" 1 2>&1 | extract_throughput || true
else
HAKMEM_PROFILE="${profile}" \
./bench_random_mixed_system "${iters}" "${ws}" 1 2>&1 | extract_throughput || true
fi
done | python3 -c "${stats_py}"
}
run_preload_n "system (no preload)" ""
if [[ -x ./libhakmem.so ]]; then
run_preload_n "hakmem (LD_PRELOAD libhakmem.so)" ./libhakmem.so
else
echo ""
echo "== hakmem (LD_PRELOAD libhakmem.so) =="
echo "skipped (missing ./libhakmem.so; build via: make shared)"
fi
if [[ -n "${MIMALLOC_SO:-}" && -e "${MIMALLOC_SO}" ]]; then
run_preload_n "mimalloc (LD_PRELOAD)" "${MIMALLOC_SO}"
fi
if [[ -n "${JEMALLOC_SO:-}" && -e "${JEMALLOC_SO}" ]]; then
run_preload_n "jemalloc (LD_PRELOAD)" "${JEMALLOC_SO}"
fi
if [[ -n "${TCMALLOC_SO:-}" && -e "${TCMALLOC_SO}" ]]; then
run_preload_n "tcmalloc (LD_PRELOAD)" "${TCMALLOC_SO}"
fi