bench: report fastwc timing in microseconds

Sub-millisecond runs showed as a flat 0ms, hiding real differences. The
suite now times fastwc with a time_us helper, keeps the minimum reading
in µs, derives ms from it, and prints both (fastwc: 0ms (767µs)). GNU wc
stays at ms precision; solo case shows µs too.
This commit is contained in:
2026-08-29 18:56:50 -04:00
parent af61660c42
commit 55aab093e7
4 changed files with 41 additions and 24 deletions
+38 -21
View File
@@ -131,6 +131,17 @@ time_ms() {
printf '%s\n' "$(( (e - s) / 1000000 ))"
}
# time_us <cmd...> — run a command once and print elapsed wall time in µs.
# More precise than time_ms: sub-millisecond runs come out as e.g. 812,
# not 0. The benchmark keeps the µs reading for fastwc and derives the ms.
time_us() {
local s e
s=$(date +%s%N)
"$@" >/dev/null 2>&1
e=$(date +%s%N)
printf '%s\n' "$(( (e - s) / 1000 ))"
}
# capture_count <cmd...> — print the first whitespace-separated field of a
# command's output, i.e. the count reported by `wc -w/-l` or `fastwc -w/-l`.
capture_count() {
@@ -151,7 +162,7 @@ write_failed_report() {
printf 'failure : %s\n' "$reason"
printf '\nresults\n'
printf '%s\n' '-------'
printf '%-28s %10s %10s %8s %s\n' 'test' 'wc' 'fastwc' 'ratio' 'status'
printf '%-28s %10s %22s %8s %s\n' 'test' 'wc' 'fastwc (ms µs)' 'ratio' 'status'
printf '%s' "$RESULT_ROWS"
printf '\nfastwc must never be slower than %s wc — benchmark aborted.\n' "$BENCH_NAME"
} > "$report"
@@ -167,7 +178,7 @@ run_case() {
local mode="$1" lines="$2" flag="$3"
local noun='lines'; [[ "$lines" -eq 1 ]] && noun='line'
local label="${mode} (${lines} ${noun})"
local file wc_count fast_count wc_ms fast_ms ratio verdict reason row
local file wc_count fast_count wc_ms fast_us fast_ms ratio verdict reason row
local i d
file=$(createtxt "$lines") || return 1
@@ -177,16 +188,18 @@ run_case() {
fast_count=$(capture_count "$FASTWC" "$flag" "$file")
# speed: interleaved timing so both commands see identical cache warmth;
# keep the minimum of $BENCH_REPS runs each to reduce noise
# keep the minimum of $BENCH_REPS runs each to reduce noise; fastwc is
# timed in µs so sub-millisecond wins are visible in the report
wc_ms=''
fast_ms=''
fast_us=''
for ((i = 0; i < BENCH_REPS; i++)); do
d=$(time_ms "${WC_CMD[@]}" "$flag" "$file")
[[ -z "$wc_ms" || "$d" -lt "$wc_ms" ]] && wc_ms="$d"
d=$(time_ms "$FASTWC" "$flag" "$file")
[[ -z "$fast_ms" || "$d" -lt "$fast_ms" ]] && fast_ms="$d"
d=$(time_us "$FASTWC" "$flag" "$file")
[[ -z "$fast_us" || "$d" -lt "$fast_us" ]] && fast_us="$d"
done
fast_ms=$(( fast_us / 1000 ))
# ratio: how many times faster fastwc is than the reference (wc / fastwc)
if [[ -n "$wc_ms" && "$wc_ms" -gt 0 ]]; then
@@ -209,8 +222,9 @@ run_case() {
reason="fastwc was slower (fastwc: ${fast_ms}ms vs ${BENCH_NAME} wc: ${wc_ms}ms)"
fi
row=$(printf '%-28s %10s %10s %8s %s\n' \
"$label" "wc: ${wc_ms}ms" "fastwc: ${fast_ms}ms" "$ratio" "$verdict")
row=$(printf '%-28s %10s %22s %8s %s\n' \
"$label" "wc: ${wc_ms}ms" "fastwc: ${fast_ms}ms (${fast_us}µs)" \
"$ratio" "$verdict")
RESULT_ROWS+="${row}"$'\n'
printf '%s\n' "$row"
@@ -228,7 +242,7 @@ run_stdin_case() {
local mode="$1" lines="$2" flag="$3"
local noun='lines'; [[ "$lines" -eq 1 ]] && noun='line'
local label="stdin ${mode} (${lines} ${noun})"
local file wc_count fast_count wc_ms fast_ms ratio verdict reason row
local file wc_count fast_count wc_ms fast_us fast_ms ratio verdict reason row
local i d
file=$(createtxt "$lines") || return 1
@@ -237,14 +251,15 @@ run_stdin_case() {
fast_count=$(capture_count "$FASTWC" "$flag" < "$file")
wc_ms=''
fast_ms=''
fast_us=''
for ((i = 0; i < BENCH_REPS; i++)); do
d=$(time_ms "${WC_CMD[@]}" "$flag" < "$file")
[[ -z "$wc_ms" || "$d" -lt "$wc_ms" ]] && wc_ms="$d"
d=$(time_ms "$FASTWC" "$flag" < "$file")
[[ -z "$fast_ms" || "$d" -lt "$fast_ms" ]] && fast_ms="$d"
d=$(time_us "$FASTWC" "$flag" < "$file")
[[ -z "$fast_us" || "$d" -lt "$fast_us" ]] && fast_us="$d"
done
fast_ms=$(( fast_us / 1000 ))
# ratio: how many times faster fastwc is than the reference (wc / fastwc)
if [[ -n "$wc_ms" && "$wc_ms" -gt 0 ]]; then
@@ -267,8 +282,9 @@ run_stdin_case() {
reason="fastwc was slower (fastwc: ${fast_ms}ms vs ${BENCH_NAME} wc: ${wc_ms}ms)"
fi
row=$(printf '%-28s %10s %10s %8s %s\n' \
"$label" "wc: ${wc_ms}ms" "fastwc: ${fast_ms}ms" "$ratio" "$verdict")
row=$(printf '%-28s %10s %22s %8s %s\n' \
"$label" "wc: ${wc_ms}ms" "fastwc: ${fast_ms}ms (${fast_us}µs)" \
"$ratio" "$verdict")
RESULT_ROWS+="${row}"$'\n'
printf '%s\n' "$row"
@@ -286,7 +302,7 @@ run_solo_case() {
local lines="$1" flag="$2"
local noun='lines'; [[ "$lines" -eq 1 ]] && noun='line'
local label="solo ${lines} ${noun}"
local file ms best='' bytes gbps mlps i d
local file best_us='' best_ms bytes gbps mlps i d
file=$(createtxt "$lines") || {
printf '%-28s %s\n' "$label" 'SKIP (could not create test data)'
@@ -294,16 +310,17 @@ run_solo_case() {
}
for ((i = 0; i < BENCH_REPS; i++)); do
d=$(time_ms "$FASTWC" "$flag" "$file")
[[ -z "$best" || "$d" -lt "$best" ]] && best="$d"
d=$(time_us "$FASTWC" "$flag" "$file")
[[ -z "$best_us" || "$d" -lt "$best_us" ]] && best_us="$d"
done
best_ms=$(( best_us / 1000 ))
bytes=$((lines * 11))
gbps=$(awk -v b="$bytes" -v ms="$best" 'BEGIN { printf "%.2f", b / ms / 1e6 }')
mlps=$(awk -v l="$lines" -v ms="$best" 'BEGIN { printf "%.1f", l / ms / 1e3 }')
gbps=$(awk -v b="$bytes" -v ms="$best_ms" 'BEGIN { if (ms < 1) ms = 1; printf "%.2f", b / ms / 1e6 }')
mlps=$(awk -v l="$lines" -v ms="$best_ms" 'BEGIN { if (ms < 1) ms = 1; printf "%.1f", l / ms / 1e3 }')
printf '%-28s %12s %12s %14s\n' \
"$label" "fastwc: ${best}ms" "${gbps} GB/s" "${mlps} Mlines/s"
printf '%-28s %25s %12s %14s\n' \
"$label" "fastwc: ${best_ms}ms (${best_us}µs)" "${gbps} GB/s" "${mlps} Mlines/s"
}
# run_cases <words|lines> <-w|-l> <size...> — run run_case for every size,