#!/usr/bin/env bash # benchCyclesPrecise.sh — measure per-call cycle counts via the # emu.time()-based runner (scripts/runInMameCycles.sh). # # For each benchmark in benchmarks/, build a wrapper that calls the # bench function ITERS times between START / DONE markers; the runner # captures emulated time and converts to cycles assuming the IIgs # slow-mode clock (1023000 Hz — IIe-compatible default; our binary # doesn't enable fast mode unless its wrapper does). # # Output: markdown table with cycles-per-call. Both clang and the # Calypsi numbers (from `tools/calypsi/cc65816`) are reported when # Calypsi is installed. set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" BENCH_DIR="$PROJECT_ROOT/benchmarks" CLANG="$PROJECT_ROOT/tools/llvm-mos-build/bin/clang" LLVM_MC="$PROJECT_ROOT/tools/llvm-mos-build/bin/llvm-mc" LINK="$PROJECT_ROOT/tools/link816" RUNNER="$PROJECT_ROOT/scripts/runInMameCycles.sh" oCrt0=$(mktemp --suffix=.o) oLibgcc=$(mktemp --suffix=.o) "$LLVM_MC" -arch=w65816 -filetype=obj "$PROJECT_ROOT/runtime/src/crt0.s" -o "$oCrt0" "$LLVM_MC" -arch=w65816 -filetype=obj "$PROJECT_ROOT/runtime/src/libgcc.s" -o "$oLibgcc" # Per-benchmark inputs / extern decls (mirrors benchCycles.sh). benchInputs() { case "$1" in sumOfSquares) echo 'sumOfSquares(50)';; fib) echo 'fib(10)';; strcpy) echo 'mystrcpy(dst, "hello world!")';; memcmp) echo 'mymemcmp("hello", "hello", 5)';; bsearch) echo 'bsearch(arr, 8, 5)';; dotProduct) echo 'dotProduct(va, vb, 4)';; popcount) echo 'popcount(0x12345678UL)';; crc32) echo 'crc32((const unsigned char *)"hello", 5)';; *) echo "/* unknown */";; esac } benchExtern() { case "$1" in sumOfSquares) echo 'extern unsigned long sumOfSquares(unsigned short n);';; fib) echo 'extern unsigned short fib(unsigned short n);';; strcpy) echo 'extern char *mystrcpy(char *d, const char *s); static char dst[16];';; memcmp) echo 'extern int mymemcmp(const void *a, const void *b, unsigned int n);';; bsearch) echo 'extern int bsearch(const int *arr, int n, int key); static const int arr[] = {1,2,3,4,5,6,7,8};';; dotProduct) echo 'extern long dotProduct(const short *a, const short *b, unsigned int n); static const short va[] = {1,2,3,4}; static const short vb[] = {5,6,7,8};';; popcount) echo 'extern int popcount(unsigned long x);';; crc32) echo 'extern unsigned long crc32(const unsigned char *p, unsigned int n);';; *) echo '';; esac } # How many iterations to run each bench for. Bigger = more # precise (smaller relative measurement noise) but longer runtime. # Heavy benches get fewer iters; cheap benches get more. benchIters() { case "$1" in sumOfSquares) echo 50;; # ~1600 cyc/call → ~80k cyc total fib) echo 100;; strcpy) echo 200;; memcmp) echo 500;; bsearch) echo 200;; dotProduct) echo 200;; popcount) echo 500;; crc32) echo 200;; *) echo 100;; esac } runOneBench() { local name="$1" local extern_decl call_expr iters extern_decl=$(benchExtern "$name") call_expr=$(benchInputs "$name") iters=$(benchIters "$name") if [ -z "$extern_decl" ] || [ "$call_expr" = "/* unknown */" ]; then echo "(no input config)"; return fi local cwrap obench owrap bin cwrap=$(mktemp --suffix=.c) owrap=$(mktemp --suffix=.o) obench=$(mktemp --suffix=.o) bin=$(mktemp --suffix=.bin) cat > "$cwrap" </dev/null \ || { echo "compile-fail"; rm -f "$cwrap" "$owrap"; return; } "$CLANG" --target=w65816 -O2 -ffunction-sections -c "$BENCH_DIR/$name.c" -o "$obench" 2>/dev/null \ || { echo "compile-fail"; rm -f "$cwrap" "$owrap" "$obench"; return; } "$LINK" -o "$bin" --text-base 0x1000 "$oCrt0" "$oLibgcc" "$owrap" "$obench" 2>/dev/null \ || { echo "link-fail"; rm -f "$cwrap" "$owrap" "$obench" "$bin"; return; } local val val=$(bash "$RUNNER" "$bin" "$iters" 2>&1 | grep -oE 'cyc_per_call=[0-9.]+' | head -1 | sed 's/cyc_per_call=//') rm -f "$cwrap" "$owrap" "$obench" "$bin" if [ -z "$val" ]; then echo "(no read)" else printf '%.0f cyc/call' "$val" fi } printf '| Benchmark | Per-call cycles (clang) |\n' printf '|-----------|------------------------:|\n' for src in "$BENCH_DIR"/*.c; do name=$(basename "$src" .c) result=$(runOneBench "$name") printf '| %s | %s |\n' "$name" "$result" done rm -f "$oCrt0" "$oLibgcc"