Skip to content

Commit 68068a4

Browse files
committed
DEV: add spin bench command
1 parent b5cbf30 commit 68068a4

File tree

2 files changed

+160
-0
lines changed

2 files changed

+160
-0
lines changed

.spin/cmds.py

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
import os
2+
import sys
3+
4+
import click
5+
import spin
6+
from spin.cmds import meson
7+
8+
9+
@click.command()
10+
@click.option(
11+
'--tests', '-t',
12+
default=None, metavar='TESTS', multiple=True,
13+
help="Which tests to run"
14+
)
15+
@click.option(
16+
'--compare', '-c',
17+
is_flag=True,
18+
default=False,
19+
help="Compare benchmarks between the current branch and main "
20+
"(unless other branches specified). "
21+
"The benchmarks are each executed in a new isolated "
22+
"environment."
23+
)
24+
@click.option(
25+
'--verbose', '-v', is_flag=True, default=False
26+
)
27+
@click.option(
28+
'--quick', '-q', is_flag=True, default=False,
29+
help="Run each benchmark only once (timings won't be accurate)"
30+
)
31+
@click.option(
32+
'--factor', '-f', default=1.05,
33+
help="The factor above or below which a benchmark result is "
34+
"considered reportable. This is passed on to the asv command."
35+
)
36+
@click.argument(
37+
'commits', metavar='',
38+
required=False,
39+
nargs=-1
40+
)
41+
@meson.build_dir_option
42+
@click.pass_context
43+
def bench(ctx, tests, compare, verbose, quick, factor, commits, build_dir):
44+
"""🏋 Run benchmarks.
45+
46+
\b
47+
Examples:
48+
49+
\b
50+
$ spin bench -t dwt_benchmarks
51+
$ spin bench -t swt_benchmarks.Swt2TimeSuite.time_swt2
52+
53+
Two benchmark runs can be compared.
54+
By default, `HEAD` is compared to `main`.
55+
You can also specify the branches/commits to compare:
56+
57+
\b
58+
$ spin bench --compare
59+
$ spin bench --compare main
60+
$ spin bench --compare main HEAD
61+
62+
You can also choose which benchmarks to run in comparison mode:
63+
64+
$ spin bench -t Swt2TimeSuite --compare
65+
66+
For a quicker but less accurate check to see if benchmarks work:
67+
68+
$ spin bench --quick
69+
70+
"""
71+
if not commits:
72+
commits = ('main', 'HEAD')
73+
elif len(commits) == 1:
74+
commits = commits + ('HEAD',)
75+
elif len(commits) > 2:
76+
raise click.ClickException(
77+
'Need a maximum of two revisions to compare'
78+
)
79+
80+
bench_args = []
81+
for t in tests:
82+
bench_args += ['--bench', t]
83+
84+
if verbose:
85+
bench_args = ['-v'] + bench_args
86+
87+
if quick:
88+
bench_args = ['--quick'] + bench_args
89+
90+
if not compare:
91+
# No comparison requested; we build and benchmark the current version
92+
93+
click.secho(
94+
"Invoking `build` prior to running benchmarks:",
95+
bold=True, fg="bright_green"
96+
)
97+
ctx.invoke(meson.build)
98+
99+
meson._set_pythonpath(build_dir)
100+
# Some weird bug, not sure what's going on here, but it seems necessary
101+
# on Python 3.14
102+
if not os.environ['PYTHONPATH'].endswith(os.sep):
103+
os.environ['PYTHONPATH'] += os.sep
104+
105+
p = spin.util.run(
106+
[sys.executable, '-c', 'import pywt; print(pywt.__version__)'],
107+
cwd='benchmarks',
108+
echo=False,
109+
output=False
110+
)
111+
pywt_ver = p.stdout.strip().decode('ascii')
112+
113+
click.secho(
114+
f'Running benchmarks on PyWavelets {pywt_ver}',
115+
bold=True, fg="bright_green"
116+
)
117+
cmd = [
118+
'asv', 'run', '--dry-run', '--show-stderr', '--python=same'
119+
] + bench_args
120+
121+
os.chdir('..')
122+
spin.util.run(cmd, cwd='benchmarks', env=os.environ)
123+
else:
124+
# Ensure that we don't have uncommitted changes
125+
commit_a, commit_b = (_commit_to_sha(c) for c in commits)
126+
127+
if commit_b == 'HEAD' and _dirty_git_working_dir():
128+
click.secho(
129+
"WARNING: you have uncommitted changes --- "
130+
"these will NOT be benchmarked!",
131+
fg="red"
132+
)
133+
134+
cmd_compare = [
135+
'asv', 'continuous', '--factor', str(factor),
136+
] + bench_args + [commit_a, commit_b]
137+
spin.util.run(cmd_compare, cwd='benchmarks')
138+
139+
140+
def _commit_to_sha(commit):
141+
p = spin.util.run(['git', 'rev-parse', commit], output=False, echo=False)
142+
if p.returncode != 0:
143+
raise (
144+
click.ClickException(
145+
f'Could not find SHA matching commit `{commit}`'
146+
)
147+
)
148+
149+
return p.stdout.decode('ascii').strip()
150+
151+
152+
def _dirty_git_working_dir():
153+
# Changes to the working directory
154+
p0 = spin.util.run(['git', 'diff-files', '--quiet'])
155+
156+
# Staged changes
157+
p1 = spin.util.run(['git', 'diff-index', '--quiet', '--cached', 'HEAD'])
158+
159+
return (p0.returncode != 0 or p1.returncode != 0)

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,4 @@ Environments = [
9898
'spin.cmds.meson.python',
9999
]
100100
Documentation = ["spin.cmds.meson.docs"]
101+
Metrics = [".spin/cmds.py:bench"]

0 commit comments

Comments
 (0)