Skip to content

Commit 4ce9fb2

Browse files
committed
WIP
1 parent 312d5a3 commit 4ce9fb2

5 files changed

Lines changed: 678 additions & 555 deletions

File tree

.github/workflows/bencher-ab.yml

Lines changed: 46 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -45,74 +45,22 @@ jobs:
4545
- name: Build and run benchmarks
4646
run: |
4747
git config --global --add safe.directory /__w/CCF/CCF
48-
mkdir build
49-
cd build
50-
cmake -GNinja -DWORKER_THREADS=2 ..
51-
ninja
52-
# Microbenchmarks
53-
./tests.sh -VV -L benchmark
54-
# End to end performance tests
55-
./tests.sh -VV -L perf -C perf
56-
# Convert microbenchmark output to bencher json
57-
source env/bin/activate
58-
PYTHONPATH=../tests python convert_pico_to_bencher.py
48+
./scripts/bench-ab.sh run --results-dir build/bench-ab
5949
60-
- name: Upload PR results
50+
- name: Upload benchmark logs
6151
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
6252
with:
63-
name: benchmark-pr-${{ github.run_id }}
64-
path: build/bencher.json
53+
name: benchmark-pr-logs-${{ github.run_id }}
54+
path: build/bench-ab/logs/*.log
55+
if-no-files-found: ignore
6556
retention-days: 7
57+
if: success() || failure()
6658

67-
benchmark_main:
68-
name: Benchmark Main
69-
runs-on:
70-
[
71-
self-hosted,
72-
1ES.Pool=gha-vmss-d16av6-ci,
73-
"JobId=bab_benchmark_main-${{ github.run_id }}-${{ github.run_number }}-${{ github.run_attempt }}",
74-
]
75-
if: *check_trigger_conditions
76-
container:
77-
image: mcr.microsoft.com/azurelinux/base/core:3.0
78-
options: --user root
79-
steps:
80-
- name: Setup container dependencies
81-
run: |
82-
gpg --import /etc/pki/rpm-gpg/MICROSOFT-RPM-GPG-KEY
83-
tdnf -y update && tdnf -y install ca-certificates git
84-
85-
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
86-
with:
87-
ref: main
88-
fetch-depth: 0
89-
90-
- name: Install dependencies
91-
run: ./scripts/setup-ci.sh
92-
93-
- name: Confirm platform
94-
run: python3 tests/infra/platform_detection.py virtual
95-
96-
- name: Build and run benchmarks
97-
run: |
98-
git config --global --add safe.directory /__w/CCF/CCF
99-
mkdir build
100-
cd build
101-
cmake -GNinja -DWORKER_THREADS=2 ..
102-
ninja
103-
# Microbenchmarks
104-
./tests.sh -VV -L benchmark
105-
# End to end performance tests
106-
./tests.sh -VV -L perf -C perf
107-
# Convert microbenchmark output to bencher json
108-
source env/bin/activate
109-
PYTHONPATH=../tests python convert_pico_to_bencher.py
110-
111-
- name: Upload main results
59+
- name: Upload PR results
11260
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
11361
with:
114-
name: benchmark-main-${{ github.run_id }}
115-
path: build/bencher.json
62+
name: benchmark-pr-${{ github.run_id }}
63+
path: build/bench-ab/bencher-pr-*.json
11664
retention-days: 7
11765

11866
compare:
@@ -126,35 +74,54 @@ jobs:
12674
container:
12775
image: mcr.microsoft.com/azurelinux/base/core:3.0
12876
options: --user root
129-
needs: [benchmark_pr, benchmark_main]
77+
needs: [benchmark_pr]
13078
if: *check_trigger_conditions
13179
steps:
13280
- name: Setup container dependencies
13381
run: |
13482
gpg --import /etc/pki/rpm-gpg/MICROSOFT-RPM-GPG-KEY
135-
tdnf -y update && tdnf -y install ca-certificates git
83+
tdnf -y update && tdnf -y install ca-certificates git gh
13684
13785
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
13886

13987
- name: Download artifacts
14088
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
14189
with:
142-
pattern: benchmark-*-${{ github.run_id }}
143-
merge-multiple: false
90+
pattern: benchmark-pr-${{ github.run_id }}
91+
path: benchmark-pr-${{ github.run_id }}
92+
merge-multiple: true
14493

145-
- name: Generate comparison
94+
- name: Restore main perf results
14695
run: |
147-
echo "# Benchmark Comparison: main vs PR" > report.md
148-
echo "" >> report.md
149-
echo "**PR Commit:** \`${{ github.event.pull_request.head.sha }}\`" >> report.md
150-
echo "**Base Commit:** \`${{ github.event.pull_request.base.sha }}\`" >> report.md
151-
echo "**Run ID:** [${{ github.run_id }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})" >> report.md
152-
echo "" >> report.md
153-
echo '```' >> report.md
154-
python3 scripts/compare_bencher_ab.py \
155-
benchmark-main-${{ github.run_id }}/bencher.json \
156-
benchmark-pr-${{ github.run_id }}/bencher.json \
157-
--label1 "main" --label2 "PR" >> report.md
158-
echo '```' >> report.md
96+
set -euo pipefail
97+
mkdir -p perf
98+
rm -f perf/*.json
99+
git config --global --add safe.directory "$GITHUB_WORKSPACE"
100+
run_ids=$(gh api "repos/${{ github.repository }}/actions/artifacts?name=perf-bench-virtual-main&per_page=100" \
101+
--jq '[.artifacts[] | select(.expired == false)] | sort_by(.created_at) | reverse | .[0:10] | .[].workflow_run.id')
102+
restored=false
103+
for run_id in $run_ids; do
104+
download_dir="prev_artifact/$run_id"
105+
mkdir -p "$download_dir"
106+
if gh run download "$run_id" --name "perf-bench-virtual-main" --dir "$download_dir"; then
107+
if [[ -n "$(find "$download_dir" -name '*.json' -print -quit)" ]]; then
108+
restored=true
109+
fi
110+
fi
111+
done
112+
if [[ "$restored" != "true" ]]; then
113+
echo "No main perf artifacts restored"
114+
exit 1
115+
fi
116+
find prev_artifact -name '*.json' -exec cp {} perf/ \;
117+
env:
118+
GH_TOKEN: ${{ github.token }}
159119

160-
cat report.md >> $GITHUB_STEP_SUMMARY
120+
- name: Generate comparison
121+
run: |
122+
./scripts/bench-ab.sh report \
123+
--main-perf-dir perf \
124+
--results-dir benchmark-pr-${{ github.run_id }} \
125+
--label PR \
126+
--output report.md \
127+
--summary

scripts/bench-ab.sh

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
#!/bin/bash
2+
# Copyright (c) Microsoft Corporation. All rights reserved.
3+
# Licensed under the Apache 2.0 License.
4+
5+
set -euo pipefail
6+
7+
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
8+
ROOT_DIR=$( dirname "$SCRIPT_DIR" )
9+
10+
usage() {
11+
cat <<'EOF'
12+
Usage: scripts/bench-ab.sh [run|report|local] [options]
13+
14+
No command means local: restore main perf, run benchmarks, and write a report.
15+
16+
Options:
17+
--build-dir DIR Build directory. Default: build-bench-ab
18+
--results-dir DIR Benchmark JSON directory. Default: build/bench-ab
19+
--main-perf-dir DIR Main-branch perf JSON directory. Default: build/bench-ab/main
20+
--output FILE Markdown report path. Default: RESULTS_DIR/report.md
21+
--iterations N Benchmark repetitions. Default: 3
22+
--label LABEL Report label. Default: run
23+
--summary Append report to GITHUB_STEP_SUMMARY
24+
EOF
25+
}
26+
27+
abs_path() {
28+
if [[ "$1" == /* ]]; then
29+
echo "$1"
30+
else
31+
echo "$ROOT_DIR/$1"
32+
fi
33+
}
34+
35+
cmd=local
36+
case "${1:-}" in
37+
-h|--help)
38+
usage
39+
exit 0
40+
;;
41+
run|report|local)
42+
cmd=$1
43+
shift
44+
;;
45+
esac
46+
47+
build_dir=build-bench-ab
48+
results_dir=build/bench-ab
49+
main_perf_dir=build/bench-ab/main
50+
output_file=
51+
iterations=3
52+
label=run
53+
summary=false
54+
repository=${GITHUB_REPOSITORY:-microsoft/CCF}
55+
main_artifact=perf-bench-virtual-main
56+
57+
while [[ $# -gt 0 ]]; do
58+
opt=$1
59+
case "$opt" in
60+
--build-dir) build_dir=${2:?$opt requires a value}; shift 2 ;;
61+
--results-dir) results_dir=${2:?$opt requires a value}; shift 2 ;;
62+
--main-perf-dir) main_perf_dir=${2:?$opt requires a value}; shift 2 ;;
63+
--output) output_file=${2:?$opt requires a value}; shift 2 ;;
64+
--iterations) iterations=${2:?$opt requires a value}; shift 2 ;;
65+
--label) label=${2:?$opt requires a value}; shift 2 ;;
66+
--summary) summary=true; shift ;;
67+
*) echo "Unknown argument: $opt" >&2; usage >&2; exit 1 ;;
68+
esac
69+
done
70+
71+
if ! [[ "$iterations" =~ ^[1-9][0-9]*$ ]]; then
72+
echo "--iterations must be a positive integer" >&2
73+
exit 1
74+
fi
75+
76+
output_file=${output_file:-"$results_dir/report.md"}
77+
78+
build_dir=$(abs_path "$build_dir")
79+
results_dir=$(abs_path "$results_dir")
80+
main_perf_dir=$(abs_path "$main_perf_dir")
81+
output_file=$(abs_path "$output_file")
82+
83+
restore_main_perf() {
84+
mkdir -p "$main_perf_dir"
85+
tmp_dir=$(mktemp -d)
86+
restored=false
87+
88+
echo "Restoring latest main perf results..."
89+
if command -v gh >/dev/null 2>&1; then
90+
run_ids=$(gh api "repos/$repository/actions/artifacts?name=$main_artifact&per_page=100" \
91+
--jq '[.artifacts[] | select(.expired == false)] | sort_by(.created_at) | reverse | .[0:10] | .[].workflow_run.id') || run_ids=
92+
93+
for run_id in $run_ids; do
94+
download_dir="$tmp_dir/$run_id"
95+
mkdir -p "$download_dir"
96+
if gh run download "$run_id" --repo "$repository" --name "$main_artifact" --dir "$download_dir" >/dev/null 2>&1; then
97+
[[ -n "$(find "$download_dir" -name '*.json' -print -quit)" ]] && restored=true
98+
fi
99+
done
100+
elif command -v curl >/dev/null 2>&1 && command -v unzip >/dev/null 2>&1; then
101+
artifacts_json="$tmp_dir/artifacts.json"
102+
if curl -fsSL \
103+
-H "Accept: application/vnd.github+json" \
104+
"https://api.github.com/repos/$repository/actions/artifacts?name=$main_artifact&per_page=100" \
105+
-o "$artifacts_json"; then
106+
python3 - "$artifacts_json" > "$tmp_dir/urls" <<'PY'
107+
import json
108+
import sys
109+
110+
with open(sys.argv[1], encoding="utf-8") as f:
111+
data = json.load(f)
112+
113+
artifacts = [
114+
artifact
115+
for artifact in data.get("artifacts", [])
116+
if not artifact.get("expired")
117+
]
118+
for artifact in sorted(
119+
artifacts, key=lambda artifact: artifact.get("created_at", ""), reverse=True
120+
)[:10]:
121+
print(artifact["archive_download_url"])
122+
PY
123+
124+
index=0
125+
while IFS= read -r url; do
126+
index=$((index + 1))
127+
archive="$tmp_dir/$index.zip"
128+
download_dir="$tmp_dir/$index"
129+
mkdir -p "$download_dir"
130+
if curl -fsSL -L "$url" -o "$archive" &&
131+
unzip -q "$archive" -d "$download_dir"; then
132+
[[ -n "$(find "$download_dir" -name '*.json' -print -quit)" ]] && restored=true
133+
fi
134+
done < "$tmp_dir/urls"
135+
fi
136+
fi
137+
138+
if [[ "$restored" == "true" ]]; then
139+
rm -f "$main_perf_dir"/*.json
140+
find "$tmp_dir" -name '*.json' -exec cp {} "$main_perf_dir"/ \;
141+
echo "Main perf results saved in $main_perf_dir"
142+
else
143+
echo "Could not restore main perf results; using $main_perf_dir" >&2
144+
fi
145+
rm -rf "$tmp_dir"
146+
}
147+
148+
run_benchmarks() {
149+
mkdir -p "$build_dir" "$results_dir"
150+
rm -f "$results_dir"/bencher-pr-*.json
151+
log_dir="$results_dir/logs"
152+
mkdir -p "$log_dir"
153+
rm -f "$log_dir"/*.log
154+
155+
build_log="$log_dir/build.log"
156+
echo "Building..."
157+
{
158+
cmake -S "$ROOT_DIR" -B "$build_dir" -GNinja -DWORKER_THREADS=2 &&
159+
cmake --build "$build_dir"
160+
} > "$build_log" 2>&1 || {
161+
echo "Build failed. See $build_log" >&2
162+
exit 1
163+
}
164+
165+
pushd "$build_dir" >/dev/null
166+
for ((i = 1; i <= iterations; i++)); do
167+
echo "Running $i/$iterations..."
168+
run_log="$log_dir/iteration-${i}.log"
169+
rm -f bencher.json
170+
{
171+
./tests.sh -VV -L benchmark &&
172+
./tests.sh -VV -L perf -C perf &&
173+
PYTHONPATH="$ROOT_DIR/tests" env/bin/python convert_pico_to_bencher.py
174+
} > "$run_log" 2>&1 || {
175+
echo "Iteration $i failed. See $run_log" >&2
176+
exit 1
177+
}
178+
result="$results_dir/bencher-pr-${i}.json"
179+
mv bencher.json "$result"
180+
done
181+
popd >/dev/null
182+
183+
echo "Benchmark results saved in $results_dir"
184+
echo "Logs saved in $log_dir"
185+
}
186+
187+
write_report() {
188+
mkdir -p "$( dirname "$output_file" )"
189+
python3 "$ROOT_DIR/scripts/compare_bencher_ab.py" \
190+
"$main_perf_dir" \
191+
"$results_dir" \
192+
--label2 "$label" > "$output_file"
193+
194+
if [[ "$summary" == "true" ]]; then
195+
if [[ -z "${GITHUB_STEP_SUMMARY:-}" ]]; then
196+
echo "GITHUB_STEP_SUMMARY is not set" >&2
197+
exit 1
198+
fi
199+
cat "$output_file" >> "$GITHUB_STEP_SUMMARY"
200+
fi
201+
202+
echo "Report saved in $output_file"
203+
}
204+
205+
case "$cmd" in
206+
run) run_benchmarks ;;
207+
report) write_report ;;
208+
local) restore_main_perf; run_benchmarks; write_report; echo "Done." ;;
209+
esac

0 commit comments

Comments
 (0)