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)
4852EOF
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 '
5660import os, sys
5761d = os.path.join(sys.argv[1], "flat")
5862os.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))
6266PYEOF2
@@ -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
100113fi
101114if [ " ${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
104126fi
105127if [ " ${BENCH_BIG:- 1} " = 1 ]; then
106128 make_big_tree " $OUT /tree-big"
0 commit comments