Skip to content

Commit aa4e284

Browse files
nikhilunniclaude
andcommitted
bench: FLAT_ENTRIES knob, 20k gate default, liveness heartbeat
The flat-dir case at 120k entries takes ~8 minutes per mke2fs run (quadratic dirent insertion), which made the recurring CI gate look hung and cost most of an hour. The gate now runs a 20k-entry flat dir on the normal 1+5 schedule; FLAT_ENTRIES=120000 remains available for dedicated headline runs (2 runs, no warmup). A 30s heartbeat keeps CI logs showing liveness during long runs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 50cc768 commit aa4e284

1 file changed

Lines changed: 36 additions & 14 deletions

File tree

‎tools/bench.sh‎

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,19 @@
33
#
44
# (a) small-files: ~120k small files across nested dirs (node_modules-like)
55
# (b) big-files: ~4 GiB tree containing multi-GiB files
6-
# (c) flat-dir: 120k files in ONE directory (pnpm-store shape; the
7-
# case the htree requirement exists for)
6+
# (c) flat-dir: FLAT_ENTRIES files in ONE directory (pnpm-store shape;
7+
# the case the htree requirement exists for)
88
#
99
# Usage: tools/bench.sh [outdir]
1010
# E2SBIN e2fsprogs sbin dir (default: brew keg or PATH)
1111
# BENCH_SMALL=0 skip the small-files benchmark
1212
# BENCH_BIG=0 skip the big-files benchmark
1313
# BENCH_FLAT=0 skip the flat-dir benchmark
14-
# ASSERT_FASTER=1 exit nonzero unless mkext4 wins both
14+
# FLAT_ENTRIES=N flat-dir size (default 20000 for the recurring gate:
15+
# mke2fs is QUADRATIC on flat directories — at 120k
16+
# entries a single mke2fs run takes ~8 minutes, so the
17+
# full-size case is for dedicated headline runs only)
18+
# ASSERT_FASTER=1 exit nonzero unless mkext4 wins every case
1519
#
1620
# Requires hyperfine.
1721

@@ -48,15 +52,15 @@ print(n, "files", file=sys.stderr)
4852
EOF
4953
}
5054

51-
make_flat_tree() { # 120k small files in a single directory
52-
local t=$1
55+
make_flat_tree() { # $2 small files in a single directory
56+
local t=$1 n=$2
5357
[ -d "$t" ] && return
54-
echo "generating flat-dir tree..." >&2
55-
python3 - "$t" <<'PYEOF2'
58+
echo "generating flat-dir tree ($n files)..." >&2
59+
python3 - "$t" "$n" <<'PYEOF2'
5660
import os, sys
5761
d = os.path.join(sys.argv[1], "flat")
5862
os.makedirs(d, exist_ok=True)
59-
for i in range(120000):
63+
for i in range(int(sys.argv[2])):
6064
with open(os.path.join(d, "entry_%06d_pad" % i), "wb") as fh:
6165
fh.write(b"x" * (100 + (i * 37) % 2000))
6266
PYEOF2
@@ -74,13 +78,22 @@ make_big_tree() { # ~4 GiB: two multi-GiB files + some medium ones
7478
done
7579
}
7680

77-
run_case() { # $1 name, $2 tree, $3 image-blocks, $4 inode count
78-
local name=$1 tree=$2 blocks=$3 inodes=$4
81+
run_case() { # $1 name, $2 tree, $3 image-blocks, $4 inodes, $5 runs, $6 warmup
82+
local name=$1 tree=$2 blocks=$3 inodes=$4 runs=${5:-5} warmup=${6:-1}
7983
local img_a="$OUT/$name-mke2fs.img" img_b="$OUT/$name-mkext4.img"
80-
echo "== $name" >&2
81-
hyperfine --warmup 1 --runs 5 --export-json "$OUT/$name.json" \
84+
echo "== $name ($runs runs + $warmup warmup)" >&2
85+
# Heartbeat so CI logs show liveness during long runs (a slow run is
86+
# not a hang: mke2fs is quadratic on flat directories). Exits on its
87+
# own if the script dies mid-benchmark.
88+
local t0=$SECONDS
89+
( while sleep 30; do kill -0 $$ 2>/dev/null || exit
90+
echo " [$name] still benchmarking, $((SECONDS - t0))s elapsed" >&2; done ) &
91+
local hb=$!
92+
hyperfine --warmup "$warmup" --runs "$runs" --export-json "$OUT/$name.json" \
8293
--command-name mke2fs "rm -f $img_a && $E2SBIN/mke2fs $MKFS_OPTS -N $inodes -d $tree $img_a $blocks" \
8394
--command-name mkext4 "rm -f $img_b && MKEXT4_INODES=$inodes $MKFS_RS $tree $img_b $((blocks * 4096))"
95+
kill "$hb" 2>/dev/null || true
96+
wait "$hb" 2>/dev/null || true
8497
# Sanity: our image must be fsck-clean.
8598
"$E2SBIN/e2fsck" -fn "$img_b" >/dev/null
8699
python3 - "$OUT/$name.json" <<'EOF'
@@ -99,8 +112,17 @@ if [ "${BENCH_SMALL:-1}" = 1 ]; then
99112
run_case small "$OUT/tree-small" 262144 160000 # 1 GiB image
100113
fi
101114
if [ "${BENCH_FLAT:-1}" = 1 ]; then
102-
make_flat_tree "$OUT/tree-flat"
103-
run_case flat "$OUT/tree-flat" 262144 160000 # 1 GiB image
115+
FLAT_ENTRIES=${FLAT_ENTRIES:-20000}
116+
FLAT_TREE="$OUT/tree-flat-$FLAT_ENTRIES" # keyed by size: no stale cache
117+
make_flat_tree "$FLAT_TREE" "$FLAT_ENTRIES"
118+
if [ "$FLAT_ENTRIES" -ge 60000 ]; then
119+
# 2 runs, no warmup: a single mke2fs pass over a flat 120k-entry
120+
# directory takes ~8 minutes (quadratic dirent insertion), so the
121+
# usual 1+5 schedule would cost an hour for no extra signal.
122+
run_case flat "$FLAT_TREE" 262144 $((FLAT_ENTRIES + 40000)) 2 0 # 1 GiB image
123+
else
124+
run_case flat "$FLAT_TREE" 262144 $((FLAT_ENTRIES + 40000)) # 1 GiB image
125+
fi
104126
fi
105127
if [ "${BENCH_BIG:-1}" = 1 ]; then
106128
make_big_tree "$OUT/tree-big"

0 commit comments

Comments
 (0)