| #!/bin/bash |
| # Run Eigen benchmarks and collect JSON results with metadata. |
| # |
| # Expected environment variables: |
| # EIGEN_CI_BUILDDIR - build directory containing benchmark executables |
| # EIGEN_BENCH_TARGET - ISA target name (e.g. "x86-64-avx2") |
| # EIGEN_BENCH_SCOPE - "nightly" (core subset) or "weekly" (all) |
| # EIGEN_BENCH_REPETITIONS - number of repetitions per benchmark (default: 5) |
| |
| set -ex |
| |
| rootdir=$(pwd) |
| builddir=${EIGEN_CI_BUILDDIR:-.bench-build} |
| results_dir="$(pwd)/${builddir}/results" |
| mkdir -p "${results_dir}" |
| |
| target=${EIGEN_BENCH_TARGET:?EIGEN_BENCH_TARGET must be set} |
| scope=${EIGEN_BENCH_SCOPE:-nightly} |
| reps=${EIGEN_BENCH_REPETITIONS:-5} |
| |
| # Auto-promote to weekly on Sundays (day 0) so the full suite runs once a |
| # week without requiring a separate GitLab schedule. |
| if [ "${scope}" = "nightly" ] && [ "$(date -u +%u)" = "7" ]; then |
| echo "Sunday detected, promoting scope from nightly to weekly." |
| scope="weekly" |
| fi |
| |
| # Runtime ISA check: skip if the runner lacks the required instruction set. |
| if [[ "${target}" == *"avx512"* ]]; then |
| if ! grep -q 'avx512dq' /proc/cpuinfo 2>/dev/null; then |
| echo "WARNING: Runner does not support AVX-512 DQ. Skipping benchmarks." |
| exit 0 |
| fi |
| fi |
| if [[ "${target}" == *"avx2"* ]]; then |
| if ! grep -q 'avx2' /proc/cpuinfo 2>/dev/null; then |
| echo "WARNING: Runner does not support AVX2. Skipping benchmarks." |
| exit 0 |
| fi |
| fi |
| |
| cd "${builddir}" |
| |
| # Determine which benchmarks to run. |
| bench_list=() |
| if [[ "${scope}" == "weekly" ]]; then |
| while IFS= read -r -d '' exe; do |
| bench_list+=("${exe}") |
| done < <(find . -maxdepth 1 -type f -executable -name 'bench_*' -print0 | sort -z) |
| else |
| while IFS= read -r name; do |
| [[ -z "$name" || "$name" == \#* ]] && continue |
| name=$(echo "$name" | xargs) # trim whitespace |
| [[ -z "$name" ]] && continue |
| if [[ -x "./${name}" ]]; then |
| bench_list+=("./${name}") |
| else |
| echo "WARNING: ${name} not found, skipping." |
| fi |
| done < "${rootdir}/ci/scripts/benchmark_targets.txt" |
| fi |
| |
| if [[ ${#bench_list[@]} -eq 0 ]]; then |
| echo "ERROR: No benchmark executables found." |
| exit 1 |
| fi |
| |
| # Collect system info. |
| cpu_model=$(grep -m1 'model name' /proc/cpuinfo 2>/dev/null | cut -d: -f2 | xargs || echo "unknown") |
| timestamp=$(date -u +%Y-%m-%dT%H-%M-%SZ) |
| commit=${CI_COMMIT_SHORT_SHA:-$(git rev-parse --short HEAD 2>/dev/null || echo "unknown")} |
| successful_runs=0 |
| |
| # Run each benchmark executable. |
| for bench_exe in "${bench_list[@]}"; do |
| bench_name=$(basename "${bench_exe}") |
| outfile="${results_dir}/${bench_name}.json" |
| |
| echo "=== Running ${bench_name} (${reps} repetitions) ===" |
| if ! "${bench_exe}" \ |
| --benchmark_format=json \ |
| --benchmark_out="${outfile}" \ |
| --benchmark_repetitions="${reps}" \ |
| --benchmark_report_aggregates_only=false \ |
| 2>&1; then |
| echo "WARNING: ${bench_name} failed (possibly SIGILL), skipping." |
| rm -f "${outfile}" |
| continue |
| fi |
| successful_runs=$((successful_runs + 1)) |
| done |
| |
| cd "${rootdir}" |
| |
| if [[ ${successful_runs} -eq 0 ]]; then |
| echo "ERROR: No benchmark executables completed successfully." |
| exit 1 |
| fi |
| |
| # Wrap each result file with metadata and produce a combined output. |
| python3 - "${results_dir}" "${timestamp}" "${commit}" "${target}" "${cpu_model}" "${scope}" <<'PYEOF' |
| import json |
| import glob |
| import os |
| import sys |
| |
| results_dir = sys.argv[1] |
| timestamp = sys.argv[2] |
| commit = sys.argv[3] |
| target = sys.argv[4] |
| cpu_model = sys.argv[5] |
| scope = sys.argv[6] |
| |
| metadata = { |
| "timestamp": timestamp, |
| "date": timestamp[:10], |
| "commit": commit, |
| "target": target, |
| "cpu_model": cpu_model, |
| "scope": scope, |
| "ci_job_id": os.environ.get("CI_JOB_ID", ""), |
| "ci_pipeline_id": os.environ.get("CI_PIPELINE_ID", ""), |
| "runner_description": os.environ.get("CI_RUNNER_DESCRIPTION", ""), |
| } |
| |
| combined = {"metadata": metadata, "files": {}} |
| |
| for jf in sorted(glob.glob(os.path.join(results_dir, "bench_*.json"))): |
| name = os.path.splitext(os.path.basename(jf))[0] |
| with open(jf) as f: |
| data = json.load(f) |
| entry = { |
| "context": data.get("context", {}), |
| "benchmarks": data.get("benchmarks", []), |
| } |
| combined["files"][name] = entry |
| |
| outpath = os.path.join(results_dir, f"{timestamp}_{commit}_{target}.json") |
| with open(outpath, "w") as f: |
| json.dump(combined, f, indent=2) |
| |
| print(f"Combined results written to {outpath}") |
| print(f" {len(combined['files'])} benchmark files, target={target}") |
| PYEOF |