Skip to content

Commit b53f11a

Browse files
mruegivanvc
authored andcommitted
Add benchmark tooling
This adds a way to compare benchmarks that we use in kube-state-metrics already, hopefully allowing for better comparisons when applying changes. Co-authored-by: Manuel Rüger <[email protected]> Signed-off-by: Ivan Valdes <[email protected]>
1 parent 6291f7a commit b53f11a

File tree

3 files changed

+133
-0
lines changed

3 files changed

+133
-0
lines changed

.github/workflows/benchmark-pr.yaml

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
---
2+
name: Benchmarks on AMD64
3+
permissions: read-all
4+
on: [pull_request]
5+
jobs:
6+
benchmark-pull-request:
7+
runs-on: ubuntu-latest-8-cores
8+
steps:
9+
- uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4
10+
with:
11+
fetch-depth: 0
12+
- id: goversion
13+
run: echo "goversion=$(cat .go-version)" >> "$GITHUB_OUTPUT"
14+
- uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
15+
with:
16+
go-version: ${{ steps.goversion.outputs.goversion }}
17+
- name: Run Benchmarks
18+
run: |
19+
BENCHSTAT_OUTPUT_FILE=result.txt make test-benchmark-compare REF=${{ github.event.pull_request.head.sha }}
20+
- run: |
21+
echo "\`\`\`" >> "$GITHUB_STEP_SUMMARY"
22+
cat output.txt >> "$GITHUB_STEP_SUMMARY"
23+
echo "\`\`\`" >> "$GITHUB_STEP_SUMMARY"
24+
cat <<EOL >> "$GITHUB_STEP_SUMMARY"
25+
<hr />
26+
27+
This section contains three tables generated by benchstat:
28+
29+
1. Seconds per operation.
30+
2. Bytes per operation.
31+
3. Allocations per operation.
32+
33+
The tables show the median and 75% confidence interval (CI) summaries for each benchmark comparing the HEAD and the BASE of the Pull Request, and an A/B comparison under "vs base". The last column shows the statistical p-value with three runs (n=3).
34+
35+
The last row has the Geometric Mean (geomean) for the given rows in the table.
36+
37+
Refer to [benchstat's documentation](https://pkg.go.dev/golang.org/x/perf/cmd/benchstat) for more help.
38+
EOL
39+
- name: Validate results under acceptable limit
40+
run: |
41+
export MAX_ACCEPTABLE_DIFFERENCE=5
42+
while IFS= read -r line; do
43+
# Get fourth value, which is the comparison with the base.
44+
value="$(echo "$line" | awk '{print $4}')"
45+
if [[ "$value" = +* ]] || [[ "$value" = -* ]]; then
46+
if (( $(echo "${value//[^0-9.]/}"'>'"$MAX_ACCEPTABLE_DIFFERENCE" | bc -l) )); then
47+
echo "::error::$value is above the maximum acceptable difference ($MAX_ACCEPTABLE_DIFFERENCE)"
48+
exit 1
49+
fi
50+
fi
51+
done < <(grep geomean output.txt)

Makefile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,3 +94,14 @@ test-failpoint:
9494
test-robustness: gofail-enable build
9595
sudo env PATH=$$PATH go test -v ${TESTFLAGS} ./tests/dmflakey -test.root
9696
sudo env PATH=$(PWD)/bin:$$PATH go test -v ${TESTFLAGS} ${ROBUSTNESS_TESTFLAGS} ./tests/robustness -test.root
97+
98+
.PHONY: test-benchmark-compare
99+
# Runs benchmark tests on the current git ref and the given REF, and compares
100+
# the two.
101+
test-benchmark-compare: install-benchstat
102+
@git fetch
103+
./scripts/compare_benchmarks.sh $(REF)
104+
105+
.PHONY: install-benchstat
106+
install-benchstat:
107+
go install golang.org/x/perf/cmd/benchstat@latest

scripts/compare_benchmarks.sh

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
#!/usr/bin/env bash
2+
# https://github.com/kubernetes/kube-state-metrics/blob/main/tests/compare_benchmarks.sh (originally written by mxinden)
3+
4+
# exit immediately when a command fails
5+
set -e
6+
# only exit with zero if all commands of the pipeline exit successfully
7+
set -o pipefail
8+
# error on unset variables
9+
set -u
10+
11+
[[ "$#" -eq 1 ]] || echo "One argument required, $# provided."
12+
13+
REF_CURRENT="$(git rev-parse --abbrev-ref HEAD)"
14+
BASE_TO_COMPARE=$1
15+
16+
RESULT_CURRENT="$(mktemp)-${REF_CURRENT}"
17+
RESULT_TO_COMPARE="$(mktemp)-${BASE_TO_COMPARE}"
18+
19+
TIMEOUT=${TIMEOUT:-30m}
20+
BENCH_COUNT=${BENCH_COUNT:-3}
21+
BENCHSTAT_CONFIDENCE_LEVEL=${BENCHSTAT_CONFIDENCE_LEVEL:-0.75}
22+
BENCHSTAT_FORMAT=${BENCHSTAT_FORMAT:-"text"}
23+
24+
if [[ "${BENCHSTAT_FORMAT}" == "csv" ]] && [[ -z "${BENCHSTAT_OUTPUT_FILE}" ]]; then
25+
echo "BENCHSTAT_FORMAT is set to csv, but BENCHSTAT_OUTPUT_FILE is not set."
26+
exit 1
27+
fi
28+
29+
function main() {
30+
echo ""
31+
echo "### Testing ${REF_CURRENT}"
32+
33+
go test -timeout="${TIMEOUT}" -count="${BENCH_COUNT}" -benchmem -run=NONE -bench=. ./... | tee "${RESULT_CURRENT}"
34+
35+
# Filter benchark lines, so benchstat can parse the output.
36+
grep ^Benchmark "${RESULT_CURRENT}" > "${RESULT_CURRENT}".tmp && mv "${RESULT_CURRENT}".tmp "${RESULT_CURRENT}"
37+
38+
echo ""
39+
echo "### Done testing ${REF_CURRENT}"
40+
41+
echo ""
42+
echo "### Testing ${BASE_TO_COMPARE}"
43+
44+
git checkout "${BASE_TO_COMPARE}"
45+
46+
go test -timeout="${TIMEOUT}" -count="${BENCH_COUNT}" -benchmem -run=NONE -bench=. ./... | tee "${RESULT_TO_COMPARE}"
47+
48+
# Filter benchark lines, so benchstat can parse the output.
49+
grep ^Benchmark "${RESULT_TO_COMPARE}" > "${RESULT_TO_COMPARE}".tmp && mv "${RESULT_TO_COMPARE}".tmp "${RESULT_TO_COMPARE}"
50+
51+
echo ""
52+
echo "### Done testing ${BASE_TO_COMPARE}"
53+
54+
git checkout -
55+
56+
echo ""
57+
echo "### Result"
58+
echo "base=${BASE_TO_COMPARE} head=${REF_CURRENT}"
59+
60+
if [[ "${BENCHSTAT_FORMAT}" == "csv" ]]; then
61+
benchstat -format=csv -confidence="${BENCHSTAT_CONFIDENCE_LEVEL}" BASE="${RESULT_TO_COMPARE}" HEAD="${RESULT_CURRENT}" 2>/dev/null 1>"${BENCHSTAT_OUTPUT_FILE}"
62+
else
63+
if [[ -z "${BENCHSTAT_OUTPUT_FILE}" ]]; then
64+
benchstat -confidence="${BENCHSTAT_CONFIDENCE_LEVEL}" BASE="${RESULT_TO_COMPARE}" HEAD="${RESULT_CURRENT}"
65+
else
66+
benchstat -confidence="${BENCHSTAT_CONFIDENCE_LEVEL}" BASE="${RESULT_TO_COMPARE}" HEAD="${RESULT_CURRENT}" 1>"${BENCHSTAT_OUTPUT_FILE}"
67+
fi
68+
fi
69+
}
70+
71+
main

0 commit comments

Comments
 (0)