Skip to content

Commit 85a3620

Browse files
committed
Output SVG files
1 parent f445206 commit 85a3620

13 files changed

+69
-40
lines changed

DEVELOPER.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ With this base, there are files with the following suffixes:
104104

105105
- `.json`: The raw results from `pyperformance`.
106106
- `-vs-{base}.md`: A table comparing the results against the given base, as returned by `pyperf compare_to`.
107-
- `-vs-{base}.png`: A set of violin plots with the distribution of differences for each benchmark.
107+
- `-vs-{base}.svg`: A set of violin plots with the distribution of differences for each benchmark.
108108
- `-pystats.json`: The raw results from a pystats run.
109109
- `-pystats.md`: The results of a pystats run, summarized in human-readable form.
110110

bench_runner/plot.py

+25-6
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@
77
from operator import attrgetter
88
from pathlib import Path
99
import re
10+
import tempfile
1011
from typing import Callable, Iterable
1112

1213

1314
from matplotlib import pyplot as plt
1415
import matplotlib
1516
import numpy as np
1617
import rich_argparse
18+
from scour import scour
1719
import simdjson
1820

1921

@@ -46,6 +48,26 @@
4648
}
4749

4850

51+
def savefig(output_filename: Path, **kwargs):
52+
class Options:
53+
quiet = True
54+
remove_descriptive_elements = True
55+
enable_comment_stripping = True
56+
indent_type = "none"
57+
strip_ids = True
58+
shorten_ids = True
59+
digits = 3
60+
61+
plt.savefig(output_filename, **kwargs)
62+
if output_filename.suffix == ".svg":
63+
with tempfile.NamedTemporaryFile(delete=False) as tmp:
64+
with open(output_filename) as fd:
65+
scour.start(Options(), fd, tmp)
66+
output_filename.unlink()
67+
Path(tmp.name).rename(output_filename)
68+
plt.close()
69+
70+
4971
@functools.cache
5072
def get_plot_config():
5173
with Path("plotconfig.json").open() as fd:
@@ -153,8 +175,7 @@ def plot_diff(
153175
axs.grid()
154176
axs.set_title(title)
155177

156-
plt.savefig(output_filename)
157-
plt.close()
178+
savefig(output_filename)
158179

159180

160181
def get_micro_version(version: str) -> str:
@@ -323,8 +344,7 @@ def get_comparison_value(ref, r, base):
323344

324345
fig.suptitle(title)
325346

326-
plt.savefig(output_filename, dpi=150)
327-
plt.close()
347+
savefig(output_filename, dpi=150)
328348

329349
with data_cache.open("w") as fd:
330350
simdjson.dump(data, fd, indent=2)
@@ -419,8 +439,7 @@ def get_comparison_value(ref, r):
419439
for ax in axs:
420440
ax.set_xlim((minx, maxx))
421441

422-
plt.savefig(output_filename, dpi=150)
423-
plt.close()
442+
savefig(output_filename, dpi=150)
424443

425444
with data_cache.open("w") as fd:
426445
simdjson.dump(data, fd, indent=2)

bench_runner/result.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -104,9 +104,9 @@ def get_files(self) -> Iterable[tuple[Callable, str, str]]:
104104
if self.base_filename is None:
105105
return
106106
yield (self.write_table, ".md", "table")
107-
yield (self.write_timing_plot, ".png", "time plot")
107+
yield (self.write_timing_plot, ".svg", "time plot")
108108
if self.head.system != "windows" and self.base == "base":
109-
yield (self.write_memory_plot, "-mem.png", "memory plot")
109+
yield (self.write_memory_plot, "-mem.svg", "memory plot")
110110

111111
@functools.cached_property
112112
def _contents(self) -> str | None:
@@ -534,9 +534,9 @@ def result_info(self) -> tuple[str | None, str | None]:
534534
return ("pystats raw", None)
535535
case (["vs", base], ".md"):
536536
return ("table", base)
537-
case (["vs", base], ".png"):
537+
case (["vs", base], ".svg"):
538538
return ("time plot", base)
539-
case (["vs", base, "mem"], ".png"):
539+
case (["vs", base, "mem"], ".svg"):
540540
return ("memory plot", base)
541541
raise ValueError(
542542
f"Unknown result type (extra={self.extra} suffix={self.suffix})"

bench_runner/scripts/generate_results.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -401,17 +401,17 @@ def _main(repo_dir: Path, force: bool = False, bases: Sequence[str] | None = Non
401401
[
402402
(
403403
plot.longitudinal_plot,
404-
(benchmarking_results, repo_dir / "longitudinal.png"),
404+
(benchmarking_results, repo_dir / "longitudinal.svg"),
405405
{},
406406
),
407407
(
408408
plot.flag_effect_plot,
409-
(benchmarking_results, repo_dir / "configs.png"),
409+
(benchmarking_results, repo_dir / "configs.svg"),
410410
{},
411411
),
412412
(
413413
plot.longitudinal_plot,
414-
(memory_benchmarking_results, repo_dir / "memory_long.png"),
414+
(memory_benchmarking_results, repo_dir / "memory_long.svg"),
415415
dict(
416416
getter=lambda r: r.memory_change_float,
417417
differences=("less", "more"),
@@ -420,7 +420,7 @@ def _main(repo_dir: Path, force: bool = False, bases: Sequence[str] | None = Non
420420
),
421421
(
422422
plot.flag_effect_plot,
423-
(memory_benchmarking_results, repo_dir / "memory_configs.png"),
423+
(memory_benchmarking_results, repo_dir / "memory_configs.svg"),
424424
dict(
425425
getter=lambda r: r.memory_change_float,
426426
differences=("less", "more"),

bench_runner/scripts/profiling_plot.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -350,7 +350,7 @@ def _main(input_dir: Path, output_prefix: Path):
350350
ax.legend(bbox_to_anchor=(1.05, 1.0), loc="upper left")
351351
ax.set_xlim([0, 1])
352352

353-
fig.savefig(output_prefix.with_suffix(".png"))
353+
fig.savefig(output_prefix.with_suffix(".svg"))
354354

355355
fig, ax = plt.subplots(figsize=(5, 3), layout="constrained")
356356
values = [x[0] for x in sorted_categories]
@@ -373,7 +373,7 @@ def _main(input_dir: Path, output_prefix: Path):
373373
values, labels=labels, colors=colors, hatch=hatches, textprops={"fontsize": 6}
374374
)
375375

376-
fig.savefig(output_prefix.with_suffix(".pie.png"), dpi=200)
376+
fig.savefig(output_prefix.with_suffix(".pie.svg"), dpi=200)
377377

378378

379379
def main():

bench_runner/templates/README.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,12 @@ Here are some recent and important revisions. 👉 [Complete list of results](RE
1212

1313
`*` indicates that the exact same versions of pyperformance was not used.
1414

15-
![Longitudinal speed improvement](/longitudinal.png)
15+
![Longitudinal speed improvement](/longitudinal.svg)
1616

1717
Improvement of the geometric mean of key merged benchmarks, computed with `pyperf compare`.
1818
The results have a resolution of 0.01 (1%).
1919

20-
![Configuration speed improvement](/configs.png)
20+
![Configuration speed improvement](/configs.svg)
2121

2222
## Documentation
2323

@@ -45,7 +45,7 @@ When the benchmarking is complete, the results are published to this repository
4545
Each set of benchmarks will have:
4646

4747
- The raw `.json` results from pyperformance.
48-
- Comparisons against important reference releases, as well as the merge base of the branch if `benchmark_base` was selected. These include
48+
- Comparisons against important reference releases, as well as the merge base of the branch if `benchmark_base` was selected. These include
4949
- A markdown table produced by `pyperf compare_to`.
5050
- A set of "violin" plots showing the distribution of results for each benchmark.
5151

bench_runner/templates/_generate.src.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,5 +48,5 @@ jobs:
4848
uses: EndBug/add-and-commit@v9
4949
if: ${{ !inputs.dry_run }}
5050
with:
51-
add: "['results', 'README.md', 'RESULTS.md', 'longitudinal.png', 'longitudinal.json', 'configs.png', 'configs.json', 'memory_long.png', 'memory_long.json', 'memory_configs.png', 'memory_configs.json']"
51+
add: "['results', 'README.md', 'RESULTS.md', 'longitudinal.svg', 'longitudinal.json', 'configs.svg', 'configs.json', 'memory_long.svg', 'memory_long.json', 'memory_configs.svg', 'memory_configs.json']"
5252
message: Benchmarking results for @${{ github.actor }}

bench_runner/util.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,6 @@ def get_benchmark_hash() -> str:
1919

2020
def apply_suffix(path: Path, suffix: str) -> Path:
2121
"""
22-
Like Path.with_suffix but allows adding things like "-mem.png".
22+
Like Path.with_suffix but allows adding things like "-mem.svg".
2323
"""
2424
return path.parent / (path.stem + suffix)

pyproject.toml

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ dependencies = [
2121
"rich==13.7.1",
2222
"rich-argparse==1.5.2",
2323
"ruamel.yaml==0.18.6",
24+
"scour==0.38.2",
2425
"wheel",
2526
]
2627
dynamic = ["version"]

tests/test_compare.py

+7-7
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def test_compare_1_to_n(tmp_path, monkeypatch):
2727
assert len(files) == 7
2828
expected_files = {"README.md"}
2929
for hash in hashes[1:]:
30-
for suffix in [".md", ".png", "-mem.png"]:
30+
for suffix in [".md", ".svg", "-mem.svg"]:
3131
expected_files.add(f"linux-{hash}-vs-{hashes[0]}{suffix}")
3232
assert set(x.name for x in files) == expected_files
3333

@@ -38,8 +38,8 @@ def test_compare_1_to_n(tmp_path, monkeypatch):
3838
3939
| commit | change |
4040
| -- | -- |
41-
| eb0004c (eb0004c) | 1.57x ↑[📄](linux-eb0004c-vs-9d38120.md)[📈](linux-eb0004c-vs-9d38120.png)[🧠](linux-eb0004c-vs-9d38120-mem.png) |
42-
| b0e1f9c (b0e1f9c) | 1.70x ↑[📄](linux-b0e1f9c-vs-9d38120.md)[📈](linux-b0e1f9c-vs-9d38120.png)[🧠](linux-b0e1f9c-vs-9d38120-mem.png) |
41+
| eb0004c (eb0004c) | 1.57x ↑[📄](linux-eb0004c-vs-9d38120.md)[📈](linux-eb0004c-vs-9d38120.svg)[🧠](linux-eb0004c-vs-9d38120-mem.svg) |
42+
| b0e1f9c (b0e1f9c) | 1.70x ↑[📄](linux-b0e1f9c-vs-9d38120.md)[📈](linux-b0e1f9c-vs-9d38120.svg)[🧠](linux-b0e1f9c-vs-9d38120-mem.svg) |
4343
""" # noqa
4444
).strip()
4545
assert expected in content
@@ -61,7 +61,7 @@ def test_compare_n_to_n(tmp_path, monkeypatch):
6161
for hash2 in hashes:
6262
if hash1 == hash2:
6363
continue
64-
for suffix in [".md", ".png", "-mem.png"]:
64+
for suffix in [".md", ".svg", "-mem.svg"]:
6565
expected_files.add(f"linux-{hash2}-vs-{hash1}{suffix}")
6666
assert set(x.name for x in files) == expected_files
6767

@@ -72,9 +72,9 @@ def test_compare_n_to_n(tmp_path, monkeypatch):
7272
7373
| | 9d38120 (9d38120) | eb0004c (eb0004c) | b0e1f9c (b0e1f9c) |
7474
| -- | -- | -- | -- |
75-
| 9d38120 | | 1.57x ↑[📄](linux-eb0004c-vs-9d38120.md)[📈](linux-eb0004c-vs-9d38120.png)[🧠](linux-eb0004c-vs-9d38120-mem.png) | 1.70x ↑[📄](linux-b0e1f9c-vs-9d38120.md)[📈](linux-b0e1f9c-vs-9d38120.png)[🧠](linux-b0e1f9c-vs-9d38120-mem.png) |
76-
| eb0004c | 1.57x ↓[📄](linux-9d38120-vs-eb0004c.md)[📈](linux-9d38120-vs-eb0004c.png)[🧠](linux-9d38120-vs-eb0004c-mem.png) | | 1.08x ↑[📄](linux-b0e1f9c-vs-eb0004c.md)[📈](linux-b0e1f9c-vs-eb0004c.png)[🧠](linux-b0e1f9c-vs-eb0004c-mem.png) |
77-
| b0e1f9c | 1.70x ↓[📄](linux-9d38120-vs-b0e1f9c.md)[📈](linux-9d38120-vs-b0e1f9c.png)[🧠](linux-9d38120-vs-b0e1f9c-mem.png) | 1.08x ↓[📄](linux-eb0004c-vs-b0e1f9c.md)[📈](linux-eb0004c-vs-b0e1f9c.png)[🧠](linux-eb0004c-vs-b0e1f9c-mem.png) | |
75+
| 9d38120 | | 1.57x ↑[📄](linux-eb0004c-vs-9d38120.md)[📈](linux-eb0004c-vs-9d38120.svg)[🧠](linux-eb0004c-vs-9d38120-mem.svg) | 1.70x ↑[📄](linux-b0e1f9c-vs-9d38120.md)[📈](linux-b0e1f9c-vs-9d38120.svg)[🧠](linux-b0e1f9c-vs-9d38120-mem.svg) |
76+
| eb0004c | 1.57x ↓[📄](linux-9d38120-vs-eb0004c.md)[📈](linux-9d38120-vs-eb0004c.svg)[🧠](linux-9d38120-vs-eb0004c-mem.svg) | | 1.08x ↑[📄](linux-b0e1f9c-vs-eb0004c.md)[📈](linux-b0e1f9c-vs-eb0004c.svg)[🧠](linux-b0e1f9c-vs-eb0004c-mem.svg) |
77+
| b0e1f9c | 1.70x ↓[📄](linux-9d38120-vs-b0e1f9c.md)[📈](linux-9d38120-vs-b0e1f9c.svg)[🧠](linux-9d38120-vs-b0e1f9c-mem.svg) | 1.08x ↓[📄](linux-eb0004c-vs-b0e1f9c.md)[📈](linux-eb0004c-vs-b0e1f9c.svg)[🧠](linux-eb0004c-vs-b0e1f9c-mem.svg) | |
7878
""" # noqa
7979
).strip()
8080
assert expected in content

tests/test_generate_results.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,13 @@ def _run_for_bases(bases, repo_path, force=False, has_base=[], check_readmes=Tru
3535

3636
if any(base in dirpath.name for base in has_base):
3737
assert files_by_type[".md"] == 4
38-
assert files_by_type[".png"] == 4
38+
assert files_by_type[".svg"] == 4
3939
elif any(base in dirpath.name for base in bases):
4040
assert files_by_type[".md"] in (2, 3)
41-
assert files_by_type[".png"] in (1, 2)
41+
assert files_by_type[".svg"] in (1, 2)
4242
else:
4343
assert files_by_type[".md"] in (3, 4)
44-
assert files_by_type[".png"] in (2, 3)
44+
assert files_by_type[".svg"] in (2, 3)
4545

4646
# Make sure all files in the directory have a link
4747
contents = (dirpath / "README.md").read_text()

tests/test_result.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,14 @@
1414

1515
def _copy_results(tmp_path):
1616
results_path = tmp_path / "results"
17+
shutil.copyfile(DATA_PATH / "bases.txt", tmp_path / "bases.txt")
1718
shutil.copytree(DATA_PATH / "results", tmp_path / "results")
1819
return results_path
1920

2021

21-
def test_load_all_results(tmp_path):
22+
def test_load_all_results(tmp_path, monkeypatch):
2223
results_path = _copy_results(tmp_path)
24+
monkeypatch.chdir(tmp_path)
2325

2426
results = mod_result.load_all_results(["3.10.4", "3.11.0b3"], results_path)
2527

tests/test_run_benchmarks.py

+14-7
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,9 @@ def test_run_benchmarks(benchmarks_checkout):
147147
assert returncode == 1
148148

149149

150-
def test_should_run_exists_noforce(benchmarks_checkout, capsys):
150+
def test_should_run_exists_noforce(benchmarks_checkout, capsys, monkeypatch):
151151
repo = _copy_repo(benchmarks_checkout)
152+
monkeypatch.chdir(repo)
152153

153154
should_run._main(
154155
False,
@@ -166,8 +167,9 @@ def test_should_run_exists_noforce(benchmarks_checkout, capsys):
166167
assert (repo / "results" / "bm-20220323-3.10.4-9d38120").is_dir()
167168

168169

169-
def test_should_run_diff_machine_noforce(benchmarks_checkout, capsys):
170+
def test_should_run_diff_machine_noforce(benchmarks_checkout, capsys, monkeypatch):
170171
repo = _copy_repo(benchmarks_checkout)
172+
monkeypatch.chdir(repo)
171173

172174
should_run._main(
173175
False,
@@ -185,8 +187,9 @@ def test_should_run_diff_machine_noforce(benchmarks_checkout, capsys):
185187
assert len(list((repo / "results" / "bm-20220323-3.10.4-9d38120").iterdir())) == 1
186188

187189

188-
def test_should_run_all_noforce(benchmarks_checkout, capsys):
190+
def test_should_run_all_noforce(benchmarks_checkout, capsys, monkeypatch):
189191
repo = _copy_repo(benchmarks_checkout)
192+
monkeypatch.chdir(repo)
190193

191194
should_run._main(
192195
False,
@@ -204,8 +207,9 @@ def test_should_run_all_noforce(benchmarks_checkout, capsys):
204207
assert len(list((repo / "results" / "bm-20220323-3.10.4-9d38120").iterdir())) == 1
205208

206209

207-
def test_should_run_noexists_noforce(benchmarks_checkout, capsys):
210+
def test_should_run_noexists_noforce(benchmarks_checkout, capsys, monkeypatch):
208211
repo = _copy_repo(benchmarks_checkout)
212+
monkeypatch.chdir(repo)
209213
shutil.rmtree(repo / "results" / "bm-20220323-3.10.4-9d38120")
210214

211215
should_run._main(
@@ -226,6 +230,7 @@ def test_should_run_noexists_noforce(benchmarks_checkout, capsys):
226230

227231
def test_should_run_exists_force(benchmarks_checkout, capsys, monkeypatch):
228232
repo = _copy_repo(benchmarks_checkout)
233+
monkeypatch.chdir(repo)
229234

230235
removed_paths = []
231236

@@ -253,14 +258,15 @@ def remove(repo, path):
253258
assert captured.out.splitlines()[-1].strip() == "should_run=true"
254259
assert (repo / "results" / "bm-20220323-3.10.4-9d38120").is_dir()
255260
assert set(x.name for x in removed_paths) == {
256-
"bm-20220323-linux-x86_64-python-main-3.10.4-9d38120-vs-3.11.0b3.png",
261+
"bm-20220323-linux-x86_64-python-main-3.10.4-9d38120-vs-3.11.0b3.svg",
257262
"README.md",
258263
"bm-20220323-linux-x86_64-python-main-3.10.4-9d38120-vs-3.11.0b3.md",
259264
}
260265

261266

262-
def test_should_run_noexists_force(benchmarks_checkout, capsys):
267+
def test_should_run_noexists_force(benchmarks_checkout, capsys, monkeypatch):
263268
repo = _copy_repo(benchmarks_checkout)
269+
monkeypatch.chdir(repo)
264270
shutil.rmtree(repo / "results" / "bm-20220323-3.10.4-9d38120")
265271

266272
should_run._main(
@@ -279,8 +285,9 @@ def test_should_run_noexists_force(benchmarks_checkout, capsys):
279285
assert not (repo / "results" / "bm-20220323-3.10.4-9d38120").is_dir()
280286

281287

282-
def test_should_run_checkout_failed(tmp_path, capsys):
288+
def test_should_run_checkout_failed(tmp_path, capsys, monkeypatch):
283289
repo = _copy_repo(tmp_path)
290+
monkeypatch.chdir(repo)
284291
cpython_path = tmp_path / "cpython"
285292
cpython_path.mkdir()
286293
subprocess.check_call(["git", "init"], cwd=cpython_path)

0 commit comments

Comments
 (0)