From 141997b75d1e30ef486d0865f38f5958db1e6791 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 9 Apr 2026 22:37:52 +0000 Subject: [PATCH 1/2] fix: support macOS in quartz benchmark run_all.sh The script was hardcoded for Linux (searching for .so files and linux-x86_64 paths). Add platform detection via uname so it finds the correct native library on macOS (darwin .dylib) and Linux (aarch64 and x86_64). Skip K/Native benchmark on non-Linux since only linuxX64 target exists. https://claude.ai/code/session_01JZbyrS9xZEtJ9Y4yfsmnz1 --- quartz/benchmarks/run_all.sh | 59 +++++++++++++++++++++++++++--------- 1 file changed, 44 insertions(+), 15 deletions(-) diff --git a/quartz/benchmarks/run_all.sh b/quartz/benchmarks/run_all.sh index e7bf0f7ba..c92f720e0 100755 --- a/quartz/benchmarks/run_all.sh +++ b/quartz/benchmarks/run_all.sh @@ -11,16 +11,41 @@ set -uo pipefail ROOT="$(cd "$(dirname "$0")/../.." && pwd)" BENCH_DIR="$ROOT/quartz/benchmarks" -SO_NAME="libsecp256k1-jni.so" + +# ============================================================================ +# Platform detection +# ============================================================================ +OS_NAME="$(uname -s)" +ARCH="$(uname -m)" + +case "$OS_NAME" in + Linux) + SO_NAME="libsecp256k1-jni.so" + JAR_PLATFORM="linux" + case "$ARCH" in + aarch64|arm64) SO_PATH_PATTERN="*linux-aarch64*" ;; + *) SO_PATH_PATTERN="*linux-x86_64*" ;; + esac + ;; + Darwin) + SO_NAME="libsecp256k1-jni.dylib" + JAR_PLATFORM="darwin" + SO_PATH_PATTERN="*darwin*" + ;; + *) + echo "Unsupported OS: $OS_NAME" + exit 1 + ;; +esac # ============================================================================ # 1. C native benchmark # ============================================================================ echo "=== Building C native benchmark ===" -SO_PATH=$(find "$HOME/.gradle" -path "*/secp256k1-kmp-jni-jvm-linux*" -name "*.jar" 2>/dev/null | head -1) +SO_PATH=$(find "$HOME/.gradle" -path "*/secp256k1-kmp-jni-jvm-${JAR_PLATFORM}*" -name "*.jar" 2>/dev/null | head -1) if [ -z "$SO_PATH" ]; then - echo "ACINQ secp256k1-kmp-jni-jvm-linux JAR not found in Gradle cache." + echo "ACINQ secp256k1-kmp-jni-jvm-${JAR_PLATFORM} JAR not found in Gradle cache." echo "Run './gradlew :quartz:jvmTest --tests *.Secp256k1Test' first to download it." exit 1 fi @@ -29,9 +54,9 @@ WORK=$(mktemp -d) trap "rm -rf $WORK" EXIT (cd "$WORK" && jar xf "$SO_PATH") 2>/dev/null || true -SO_FILE=$(find "$WORK" -name "$SO_NAME" -path "*linux-x86_64*" 2>/dev/null | head -1) +SO_FILE=$(find "$WORK" -name "$SO_NAME" -path "$SO_PATH_PATTERN" 2>/dev/null | head -1) if [ -z "$SO_FILE" ]; then - echo "Could not find $SO_NAME for linux-x86_64 in JAR." + echo "Could not find $SO_NAME for $OS_NAME-$ARCH in JAR." exit 1 fi cp "$SO_FILE" "$WORK/" @@ -52,19 +77,23 @@ echo "$C_OUT" echo "" # ============================================================================ -# 2. Kotlin/Native benchmark +# 2. Kotlin/Native benchmark (Linux only — only linuxX64 target exists) # ============================================================================ -echo "=== Running Kotlin/Native benchmark ===" -"$ROOT/gradlew" -p "$ROOT" :quartz:linuxX64Test --tests "*.Secp256k1NativeBenchmark" \ - 2>/dev/null || true - -KN_XML="$ROOT/quartz/build/test-results/linuxX64Test/TEST-linuxX64Test.com.vitorpamplona.quartz.utils.secp256k1.Secp256k1NativeBenchmark.xml" KN_OUT="" -if [ -f "$KN_XML" ]; then - KN_OUT=$(sed -n '//p' "$KN_XML" | grep -v 'CDATA\|]]>') - echo "$KN_OUT" +if [ "$OS_NAME" = "Linux" ]; then + echo "=== Running Kotlin/Native benchmark ===" + "$ROOT/gradlew" -p "$ROOT" :quartz:linuxX64Test --tests "*.Secp256k1NativeBenchmark" \ + 2>/dev/null || true + + KN_XML="$ROOT/quartz/build/test-results/linuxX64Test/TEST-linuxX64Test.com.vitorpamplona.quartz.utils.secp256k1.Secp256k1NativeBenchmark.xml" + if [ -f "$KN_XML" ]; then + KN_OUT=$(sed -n '//p' "$KN_XML" | grep -v 'CDATA\|]]>') + echo "$KN_OUT" + else + echo "K/Native benchmark XML not found." + fi else - echo "K/Native benchmark XML not found." + echo "=== Skipping Kotlin/Native benchmark (only linuxX64 target available) ===" fi echo "" From 3c95229c5279944f966aabd726cd7e45543a4a8b Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 9 Apr 2026 22:54:22 +0000 Subject: [PATCH 2/2] fix: remove comparison table from benchmark script, keep raw results Each platform section now prints its results directly instead of collecting them for a final aggregated table. https://claude.ai/code/session_01JZbyrS9xZEtJ9Y4yfsmnz1 --- quartz/benchmarks/run_all.sh | 205 +---------------------------------- 1 file changed, 6 insertions(+), 199 deletions(-) diff --git a/quartz/benchmarks/run_all.sh b/quartz/benchmarks/run_all.sh index c92f720e0..b0bf22386 100755 --- a/quartz/benchmarks/run_all.sh +++ b/quartz/benchmarks/run_all.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash # -# Run all secp256k1 benchmarks and produce a comparison table. +# Run all secp256k1 benchmarks. # Run from the amethyst root directory: # # ./quartz/benchmarks/run_all.sh @@ -67,19 +67,16 @@ if ! gcc -O2 -o "$WORK/bench" "$BENCH_DIR/secp256k1_native_bench.c" \ fi echo "=== Running C native benchmark ===" -C_OUT="" if [ -f "$WORK/bench" ]; then - C_OUT=$("$WORK/bench" 2>&1) + "$WORK/bench" 2>&1 else echo "(skipped — compilation failed)" fi -echo "$C_OUT" echo "" # ============================================================================ # 2. Kotlin/Native benchmark (Linux only — only linuxX64 target exists) # ============================================================================ -KN_OUT="" if [ "$OS_NAME" = "Linux" ]; then echo "=== Running Kotlin/Native benchmark ===" "$ROOT/gradlew" -p "$ROOT" :quartz:linuxX64Test --tests "*.Secp256k1NativeBenchmark" \ @@ -87,8 +84,7 @@ if [ "$OS_NAME" = "Linux" ]; then KN_XML="$ROOT/quartz/build/test-results/linuxX64Test/TEST-linuxX64Test.com.vitorpamplona.quartz.utils.secp256k1.Secp256k1NativeBenchmark.xml" if [ -f "$KN_XML" ]; then - KN_OUT=$(sed -n '//p' "$KN_XML" | grep -v 'CDATA\|]]>') - echo "$KN_OUT" + sed -n '//p' "$KN_XML" | grep -v 'CDATA\|]]>' else echo "K/Native benchmark XML not found." fi @@ -105,10 +101,8 @@ echo "=== Running JVM benchmark ===" 2>/dev/null || true JVM_XML="$ROOT/quartz/build/test-results/jvmTest/TEST-com.vitorpamplona.quartz.utils.secp256k1.Secp256k1Benchmark.xml" -JVM_OUT="" if [ -f "$JVM_XML" ]; then - JVM_OUT=$(sed -n '//p' "$JVM_XML" | grep -v 'CDATA\|]]>') - echo "$JVM_OUT" + sed -n '//p' "$JVM_XML" | grep -v 'CDATA\|]]>' else echo "JVM benchmark XML not found." fi @@ -117,10 +111,7 @@ echo "" # ============================================================================ # 4. Android benchmark (if device/emulator connected) # ============================================================================ -ANDROID_OUT="" -HAS_ANDROID="" if command -v adb &>/dev/null && adb devices 2>/dev/null | grep -q "device$"; then - HAS_ANDROID="1" echo "=== Running Android benchmark (device detected) ===" "$ROOT/gradlew" -p "$ROOT" :benchmark:connectedBenchmarkAndroidTest \ -Pandroid.testInstrumentationRunnerArguments.class=com.vitorpamplona.quartz.benchmark.Secp256k1Benchmark \ @@ -130,14 +121,12 @@ if command -v adb &>/dev/null && adb devices 2>/dev/null | grep -q "device$"; th ANDROID_XML=$(find "$ROOT/benchmark/build" -path "*test-results*" -name "*.xml" \ -newer "$ROOT/quartz/benchmarks/run_all.sh" 2>/dev/null | head -1) if [ -n "$ANDROID_XML" ] && [ -f "$ANDROID_XML" ]; then - ANDROID_OUT=$(cat "$ANDROID_XML") - echo "Android benchmark results found." + cat "$ANDROID_XML" else # Try to find results from the sdcard benchmark output BENCH_JSON=$(adb shell "ls /sdcard/Download/*benchmark*json 2>/dev/null" 2>/dev/null | head -1 | tr -d '\r') if [ -n "$BENCH_JSON" ]; then - ANDROID_OUT=$(adb shell "cat '$BENCH_JSON'" 2>/dev/null) - echo "Android benchmark JSON found: $BENCH_JSON" + adb shell "cat '$BENCH_JSON'" 2>/dev/null else echo "Android benchmark ran but results not found." fi @@ -147,185 +136,3 @@ else echo "=== Skipping Android benchmark (no device/emulator connected) ===" echo "" fi - -# ============================================================================ -# 5. Build comparison table -# ============================================================================ - -# Parse functions -parse_c() { - local op="$1" - [ -z "$op" ] && return - echo "$C_OUT" | grep -E "$op" | head -1 | awk '{print $(NF-1)}' | tr -d ',' -} - -parse_kn() { - local op="$1" - [ -z "$op" ] && return - echo "$KN_OUT" | grep -E "$op" | head -1 | awk '{print $(NF-1)}' | tr -d ',' -} - -parse_jvm_jni() { - local op="$1" - [ -z "$op" ] && return - echo "$JVM_OUT" | grep -E "$op" | head -1 | \ - sed 's/.*Native:[[:space:]]*//' | awk '{print $1}' | tr -d ',' -} - -parse_jvm_kotlin() { - local op="$1" - [ -z "$op" ] && return - echo "$JVM_OUT" | grep -E "$op" | head -1 | \ - sed 's/.*Kotlin:[[:space:]]*//' | awk '{print $1}' | tr -d ',' -} - -# Android benchmark: parse ns from XML test results -# Format: -# AndroidX Benchmark puts the median ns in the test output -parse_android() { - local op="$1" - [ -z "$op" ] || [ -z "$ANDROID_OUT" ] && return - # Try to extract ns/op from the benchmark output and convert to ops/sec - local ns - ns=$(echo "$ANDROID_OUT" | grep -i "${op}" | grep -oP '[\d,]+(?=\s*ns)' | head -1 | tr -d ',') - if [ -n "$ns" ] && [ "$ns" != "0" ]; then - awk "BEGIN { printf \"%d\", 1000000000 / $ns }" - fi -} - -fmt() { - local v="$1" - if [ -z "$v" ] || [ "$v" = "--" ]; then - echo "--" - else - echo "$v" | sed ':a;s/\B[0-9]\{3\}\>/,&/;ta' - fi -} - -ratio() { - local a="$1" b="$2" - if [ -n "$a" ] && [ -n "$b" ] && [ "$b" != "0" ] && [ "$a" != "0" ]; then - awk "BEGIN { printf \"%.1fx\", $a / $b }" - else - echo "—" - fi -} - -# Operation definitions: label|c_pattern|kn_pattern|jvm_pattern|android_pattern|quartz_only -# quartz_only=1: no C/JNI equivalent, blank those columns -OPS=( - "verifySchnorr|verifySchnorr[[:space:]]|verifySchnorr[[:space:]]|verifySchnorr[[:space:]]|verifySchnorrOurs|" - "verifySchnorrFast||verifySchnorrFast|verifySchnorrFast|verifySchnorrFastOurs|1" - "signSchnorr|signSchnorr[[:space:]]|signSchnorr[[:space:]]|signSchnorr[[:space:]]|signSchnorrOurs|" - "signSchnorr (cached)||signSchnorr .cached|signSchnorr .cached|signSchnorrCachedPkOurs|1" - "compressedPubKeyFor|compressedPubKeyFor|compressedPubKeyFor|compressedPubKeyFor|compressedPubKeyForOurs|" - "secKeyVerify|secKeyVerify|secKeyVerify|secKeyVerify|secKeyVerifyOurs|" - "privKeyTweakAdd|privKeyTweakAdd|privKeyTweakAdd|privKeyTweakAdd|privateKeyAddOurs|" - "ecdh/tweakMul|ecPubKeyTweakMul|ecdhXOnly|ecdhXOnly|ecdhXOnlyOurs|" -) - -# Determine column count based on Android availability -if [ -n "$HAS_ANDROID" ]; then - COL_FMT="%-24s %14s %14s %14s %14s %14s\n" - SEP_FMT="%-24s %14s %14s %14s %14s %14s\n" -else - COL_FMT="%-24s %14s %14s %14s %14s\n" - SEP_FMT="%-24s %14s %14s %14s %14s\n" -fi - -echo "" -echo "==============================================================================================" -echo "COMPARISON TABLE — ops/sec (higher is better)" -echo "==============================================================================================" -echo "" - -if [ -n "$HAS_ANDROID" ]; then - printf "$COL_FMT" "" "libsecp256k1" "libsecp256k1" "Quartz" "Quartz" "Quartz" - printf "$COL_FMT" "Operation" "C (no JVM)" "JVM (C+JNI)" "JVM Kotlin" "K/Native" "Android" - printf "$SEP_FMT" "——————————————————————————" "——————————————" "——————————————" "——————————————" "——————————————" "——————————————" -else - printf "$COL_FMT" "" "libsecp256k1" "libsecp256k1" "Quartz" "Quartz" - printf "$COL_FMT" "Operation" "C (no JVM)" "JVM (C+JNI)" "JVM Kotlin" "K/Native" - printf "$SEP_FMT" "——————————————————————————" "——————————————" "——————————————" "——————————————" "——————————————" -fi - -for entry in "${OPS[@]}"; do - IFS='|' read -r label c_pat kn_pat jvm_pat android_pat qonly <<< "$entry" - - c_ops=$(parse_c "$c_pat") - kn_ops=$(parse_kn "$kn_pat") - jvm_jni_ops=$(parse_jvm_jni "$jvm_pat") - jvm_k_ops=$(parse_jvm_kotlin "$jvm_pat") - android_ops=$(parse_android "$android_pat") - - # Quartz-only: no C/JNI equivalent - [ "$qonly" = "1" ] && c_ops="" && jvm_jni_ops="" - - if [ -n "$HAS_ANDROID" ]; then - printf "$COL_FMT" \ - "$label" \ - "$(fmt "${c_ops:---}")" \ - "$(fmt "${jvm_jni_ops:---}")" \ - "$(fmt "${jvm_k_ops:---}")" \ - "$(fmt "${kn_ops:---}")" \ - "$(fmt "${android_ops:---}")" - else - printf "$COL_FMT" \ - "$label" \ - "$(fmt "${c_ops:---}")" \ - "$(fmt "${jvm_jni_ops:---}")" \ - "$(fmt "${jvm_k_ops:---}")" \ - "$(fmt "${kn_ops:---}")" - fi -done - -echo "" -echo "——————————————————————————————————————————————————————————————————————————————————————————————" -echo "" -echo "Ratios — lower is closer to native (1.0x = parity):" -echo "" - -if [ -n "$HAS_ANDROID" ]; then - printf "%-24s %14s %14s %14s\n" \ - "" "C (no JVM) vs" "JVM (C+JNI) vs" "libsecp C+JNI vs" - printf "%-24s %14s %14s %14s\n" \ - "Operation" "K/Native" "JVM Kotlin" "Android" - printf "%-24s %14s %14s %14s\n" \ - "——————————————————————————" "——————————————" "——————————————" "——————————————" -else - printf "%-24s %14s %14s\n" \ - "" "C (no JVM) vs" "JVM (C+JNI) vs" - printf "%-24s %14s %14s\n" \ - "Operation" "K/Native" "JVM Kotlin" - printf "%-24s %14s %14s\n" \ - "——————————————————————————" "——————————————" "——————————————" -fi - -for entry in "${OPS[@]}"; do - IFS='|' read -r label c_pat kn_pat jvm_pat android_pat qonly <<< "$entry" - - # Skip Quartz-only in ratio table - [ "$qonly" = "1" ] && continue - - c_ops=$(parse_c "$c_pat") - kn_ops=$(parse_kn "$kn_pat") - jvm_jni_ops=$(parse_jvm_jni "$jvm_pat") - jvm_k_ops=$(parse_jvm_kotlin "$jvm_pat") - android_ops=$(parse_android "$android_pat") - - r_c_kn=$(ratio "${c_ops:-0}" "${kn_ops:-0}") - r_jni_jvm=$(ratio "${jvm_jni_ops:-0}" "${jvm_k_ops:-0}") - - if [ -n "$HAS_ANDROID" ]; then - # For Android ratio, compare native C JNI benchmark on same device - # We don't have a separate C-on-Android number, so use the JNI native from JVM - # as an approximation. Better: parse the native Android benchmark results. - r_android=$(ratio "${jvm_jni_ops:-0}" "${android_ops:-0}") - printf "%-24s %14s %14s %14s\n" "$label" "$r_c_kn" "$r_jni_jvm" "$r_android" - else - printf "%-24s %14s %14s\n" "$label" "$r_c_kn" "$r_jni_jvm" - fi -done - -echo "" -echo "=============================================================================================="