From 1bfda0d0026f835f0d90476c7c81c61be25de286 Mon Sep 17 00:00:00 2001 From: Stephan Bauroth Date: Tue, 20 Oct 2026 16:01:35 +0000 Subject: [PATCH 1/5] bench.sh: detect failed runs and fail accordingly because redis has to be detected in a more complicated way, rework the output and remove superfluous sleeps while at it. the server startup wait is bounded (with a hard timeout on redis-cli), and the server is always torn down by pid: when it dies or hangs under the preloaded allocator the run is marked failed instead of hanging the whole benchmark. --- bench.sh | 79 +++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 69 insertions(+), 10 deletions(-) diff --git a/bench.sh b/bench.sh index 65b8981..bbc33ec 100755 --- a/bench.sh +++ b/bench.sh @@ -39,6 +39,7 @@ readonly version_linux=6.5.1 verbose="no" ldpreload="LD_PRELOAD" timecmd="$(type -P time)" # the shell builtin doesn't have all the options we need +timeout_cmd="$(type -P timeout gtimeout 2>/dev/null | head -1)" # on macos: brew install coreutils sedcmd=sed darwin="" extso=".so" @@ -46,6 +47,8 @@ procs=8 repeats=1 # repeats of all tests test_repeats=1 # repeats per test sleep=0 # mini sleeps between tests seem to improve stability +error=0 # binary marker for errors during the run +failed_runs="" # accumulated failed runs case "$OSTYPE" in darwin*) darwin="1" @@ -527,6 +530,15 @@ function set_spec_bench_dir { readonly allocfill=" " readonly benchfill=" " +function kill_job_tree { # kill and its children (e.g. a redis server running under time) + local p + for p in $(pgrep -P "$1" 2>/dev/null); do + kill -9 $(pgrep -P "$p" 2>/dev/null) 2>/dev/null + kill -9 "$p" 2>/dev/null + done + kill -9 "$1" 2>/dev/null +} + function run_test_env_cmd { # if ! [ -z "$sleep" ]; then sleep "$sleep" @@ -562,16 +574,53 @@ function run_test_env_cmd { # "$outfile.server.txt" & - sleep 1s - $redis_dir/redis-cli flushall - sleep 1s + tmpfile=$(mktemp) + ( + $timecmd -a -o "$benchres.line" -f "$1${benchfill:${#1}} $2${allocfill:${#2}} %E %M %U %S %F %R" /usr/bin/env $3 $redis_dir/redis-server > "$outfile.server.txt" + echo $? > "$tmpfile" + ) & + local serverjob=$! + sleep 0.1 + if [ -s "$tmpfile" ]; then + error="1" + failed_runs+="$1:$2 " + return + fi + rm -f "$tmpfile" + # wait (bounded) for the server to accept commands: a server that dies + # under a preloaded allocator must not hang the whole benchmark + local server_ok="" retry + for retry in $(seq 1 60); do + if $timeout_cmd 10s $redis_dir/redis-cli flushall > /dev/null 2>&1; then + server_ok="1"; break + fi + [ -s "$tmpfile" ] && break # server exited while waiting + sleep 1 + done + if [ -z "$server_ok" ]; then + echo "$1 $2: redis server not accepting commands" >&2 + error="1" + failed_runs+="$1:$2 " + kill_job_tree "$serverjob" + wait "$serverjob" 2>/dev/null + return + fi $4 >> "$outfile" - sleep 1s - $redis_dir/redis-cli flushall - sleep 1s - $redis_dir/redis-cli shutdown - sleep 1s + $timeout_cmd 10s $redis_dir/redis-cli flushall > /dev/null + $timeout_cmd 10s $redis_dir/redis-cli shutdown 2> /dev/null + # a surviving server would silently serve the next allocator's run; + # its measured time is unreliable too + for retry in $(seq 1 20); do + pgrep -P "$serverjob" > /dev/null || break + sleep 0.5 + done + if pgrep -P "$serverjob" > /dev/null; then + echo "$1 $2: redis server did not shut down" >&2 + error="1" + failed_runs+="$1:$2 " + kill_job_tree "$serverjob" + fi + wait "$serverjob" 2>/dev/null ;; security) echo $2 >> $outfile @@ -605,7 +654,12 @@ function run_test_env_cmd { # "$outfile";; + $timecmd -a -o "$benchres.line" -f "$1${benchfill:${#1}} $2${allocfill:${#2}} %E %M %U %S %F %R" /usr/bin/env $3 $4 < "$infile" > "$outfile" + if test "$?" != "0"; then + error="1" + failed_runs+="$1:$2 " + fi + ;; esac # fixup larson with relative time @@ -801,3 +855,8 @@ do echo "" fi done + +if test "$error" != "0"; then + echo "Failed tests: $failed_runs" + exit 1 +fi From 35865f4891de666b2fdc3587751f86410232a259 Mon Sep 17 00:00:00 2001 From: Stephan Bauroth Date: Thu, 22 Oct 2026 14:57:39 +0000 Subject: [PATCH 2/5] bench.sh: -T/--timeout per-run limit killing the run's process group --- bench.sh | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/bench.sh b/bench.sh index bbc33ec..369b9fe 100755 --- a/bench.sh +++ b/bench.sh @@ -47,6 +47,7 @@ procs=8 repeats=1 # repeats of all tests test_repeats=1 # repeats per test sleep=0 # mini sleeps between tests seem to improve stability +run_limit=0 # time limit per run in seconds (0 = unlimited) error=0 # binary marker for errors during the run failed_runs="" # accumulated failed runs case "$OSTYPE" in @@ -377,6 +378,8 @@ while : ; do test_repeats="$flag_arg";; -s=*|--sleep=*) sleep="$flag_arg";; + -T=*|--timeout=*) + run_limit="$flag_arg";; -v|--verbose) verbose="yes";; -h|--help|-\?|help|\?) @@ -389,6 +392,7 @@ while : ; do echo " -r= number of repeats of the full suite (=$repeats)" echo " -n= number of repeats of each individual test (=$test_repeats)" echo " -s=, --sleep= seconds of sleep between each test (=$sleep)" + echo " -T=, --timeout= time limit per run in seconds; on expiry the run's whole process group is killed and the run counts as failed (=$run_limit)" echo " --external= read external allocators from , one per line, in the format " echo "" echo " allt run all tests" @@ -457,6 +461,18 @@ while : ; do shift done +# the timeout wrapper: timeout runs the command in its own process group and +# signals that whole group on expiry, so no grandchild (make -j children, +# lean --make workers, ...) can outlive a capped run +run_wrap="" +if test "$run_limit" != "0"; then + if test -z "$timeout_cmd"; then + warning "-T ignored: no timeout command found (on macOS: brew install coreutils)" + else + run_wrap="$timeout_cmd -k 10s ${run_limit}s" + fi +fi + echo "benchmarking on $procs cores." echo "use '-h' or '--help' for help on configuration options." echo "" @@ -544,7 +560,7 @@ function run_test_env_cmd { # /dev/null return fi - $4 >> "$outfile" + $run_wrap $4 >> "$outfile" $timeout_cmd 10s $redis_dir/redis-cli flushall > /dev/null $timeout_cmd 10s $redis_dir/redis-cli shutdown 2> /dev/null # a surviving server would silently serve the next allocator's run; @@ -654,7 +670,7 @@ function run_test_env_cmd { # "$outfile" + $timecmd -a -o "$benchres.line" -f "$1${benchfill:${#1}} $2${allocfill:${#2}} %E %M %U %S %F %R" /usr/bin/env $3 $run_wrap $4 < "$infile" > "$outfile" if test "$?" != "0"; then error="1" failed_runs+="$1:$2 " From 60c02c7868b16bd3bd6d4698ded148779f11ce48 Mon Sep 17 00:00:00 2001 From: Stephan Bauroth Date: Fri, 23 Oct 2026 11:41:00 +0000 Subject: [PATCH 3/5] bench.sh: do not record results of failed runs --- bench.sh | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/bench.sh b/bench.sh index 369b9fe..9a5bbb3 100755 --- a/bench.sh +++ b/bench.sh @@ -562,6 +562,7 @@ function run_test_env_cmd { # &2 error="1" failed_runs+="$1:$2 " + run_failed="1" kill_job_tree "$serverjob" fi wait "$serverjob" 2>/dev/null @@ -674,10 +676,19 @@ function run_test_env_cmd { # Date: Fri, 23 Oct 2026 11:44:39 +0000 Subject: [PATCH 4/5] README: note ltalloc faults (upstream archived) --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index efce48f..c714119 100644 --- a/README.md +++ b/README.md @@ -95,6 +95,8 @@ for the versions: allocator, used by [WebKit](https://webkit.org). - **lt**: The [_ltalloc_](https://github.com/r-lyeh-archived/ltalloc) allocator, a multi-threaded memory allocator based on free lists best suited for many small allocations. + This allocator is unmaintained (upstream archived) and faults under _mstressN_, + _leanN_, and _sh8benchN_ on x86-64 Linux; there are no entries for it in those. - **mesh**: The [_mesh_](https://github.com/plasma-umass/mesh) allocator, a memory allocator that automatically reduces the memory footprint of C/C++ applications. Also tested as **nomesh** with the meshing feature disabled. From 4569a5de7494a6f4615600a617462a8a1cf20933 Mon Sep 17 00:00:00 2001 From: Stephan Bauroth Date: Mon, 2 Nov 2026 12:46:52 +0000 Subject: [PATCH 5/5] bench.sh: use pkill instead of pgrep+kill in kill_job_tree --- bench.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bench.sh b/bench.sh index 9a5bbb3..7b08f88 100755 --- a/bench.sh +++ b/bench.sh @@ -549,7 +549,7 @@ readonly benchfill=" " function kill_job_tree { # kill and its children (e.g. a redis server running under time) local p for p in $(pgrep -P "$1" 2>/dev/null); do - kill -9 $(pgrep -P "$p" 2>/dev/null) 2>/dev/null + pkill -9 -P "$p" 2>/dev/null kill -9 "$p" 2>/dev/null done kill -9 "$1" 2>/dev/null