Skip to content
This repository was archived by the owner on May 5, 2025. It is now read-only.

Commit 88ced51

Browse files
authored
Add various benchmarks for the Report classes (#512)
This benchmarks the following scenarios: - Shallow parsing, he various `Report`s try to parse as little as possible initially - Iterating over all the files and their lines within a report, which will parse the whole report - Processing the `totals`, which internally iterates over everything and sums up things - Report filtering and iterating over everything in a filtered Report - Serializing - Merging - Creating a carryforward, which is a different way of creating a *fully filtered* report (the other filtering does things on-demand)
1 parent 6f28470 commit 88ced51

File tree

6 files changed

+206
-0
lines changed

6 files changed

+206
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ coverage.xml
5151
.hypothesis/
5252
.pytest_cache/
5353
cover/
54+
.codspeed
5455

5556
# Translations
5657
*.mo

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ dev-dependencies = [
5252
"pre-commit>=2.11.1",
5353
"psycopg2-binary>=2.9.2",
5454
"pytest-asyncio>=0.14.0",
55+
"pytest-codspeed>=3.2.0",
5556
"pytest-cov>=5.0.0",
5657
"pytest-django>=4.7.0",
5758
"pytest-mock>=1.13.0",
57.7 KB
Binary file not shown.
7.86 KB
Binary file not shown.

tests/benchmarks/test_report.py

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import orjson
2+
import pytest
3+
import zstandard as zstd
4+
5+
from shared.reports.carryforward import generate_carryforward_report
6+
from shared.reports.editable import EditableReport
7+
from shared.reports.readonly import ReadOnlyReport
8+
from shared.reports.resources import Report
9+
10+
11+
def read_fixture(name: str) -> bytes:
12+
with open(name, "rb") as f:
13+
data = f.read()
14+
15+
dctx = zstd.ZstdDecompressor()
16+
return dctx.decompress(data)
17+
18+
19+
READABLE_VARIANTS = [
20+
pytest.param(Report, False, id="Report"),
21+
pytest.param(ReadOnlyReport, False, id="ReadOnlyReport"),
22+
pytest.param(ReadOnlyReport, True, id="Rust ReadOnlyReport"),
23+
pytest.param(EditableReport, False, id="EditableReport"),
24+
]
25+
26+
EDITABLE_VARIANTS = [Report, EditableReport]
27+
28+
29+
def init_mocks(mocker, should_load_rust) -> tuple[bytes, bytes]:
30+
mocker.patch(
31+
"shared.reports.readonly.ReadOnlyReport.should_load_rust_version",
32+
return_value=should_load_rust,
33+
)
34+
35+
raw_chunks = read_fixture("tests/benchmarks/fixtures/worker_chunks.txt.zst")
36+
raw_report_json = read_fixture("tests/benchmarks/fixtures/worker_report.json.zst")
37+
38+
return raw_chunks, raw_report_json
39+
40+
41+
def do_parse(report_class, raw_report_json, raw_chunks):
42+
report_json = orjson.loads(raw_report_json)
43+
chunks = raw_chunks.decode()
44+
return report_class.from_chunks(
45+
chunks=chunks, files=report_json["files"], sessions=report_json["sessions"]
46+
)
47+
48+
49+
@pytest.mark.parametrize("report_class, should_load_rust", READABLE_VARIANTS)
50+
def test_report_parsing(report_class, should_load_rust, mocker, benchmark):
51+
raw_chunks, raw_report_json = init_mocks(mocker, should_load_rust)
52+
53+
def bench_fn():
54+
do_parse(report_class, raw_report_json, raw_chunks)
55+
56+
benchmark(bench_fn)
57+
58+
59+
@pytest.mark.parametrize("report_class, should_load_rust", READABLE_VARIANTS)
60+
def test_report_iterate_all(report_class, should_load_rust, mocker, benchmark):
61+
raw_chunks, raw_report_json = init_mocks(mocker, should_load_rust)
62+
63+
report = do_parse(report_class, raw_report_json, raw_chunks)
64+
65+
def bench_fn():
66+
for file in report:
67+
for _line in file:
68+
pass
69+
70+
benchmark(bench_fn)
71+
72+
73+
@pytest.mark.parametrize("report_class, should_load_rust", READABLE_VARIANTS)
74+
def test_report_process_totals(report_class, should_load_rust, mocker, benchmark):
75+
raw_chunks, raw_report_json = init_mocks(mocker, should_load_rust)
76+
77+
report = do_parse(report_class, raw_report_json, raw_chunks)
78+
79+
def bench_fn():
80+
report._process_totals()
81+
for file in report:
82+
file._process_totals()
83+
84+
benchmark(bench_fn)
85+
86+
87+
@pytest.mark.parametrize("report_class, should_load_rust", READABLE_VARIANTS)
88+
def test_report_filtering(report_class, should_load_rust, mocker, benchmark):
89+
raw_chunks, raw_report_json = init_mocks(mocker, should_load_rust)
90+
91+
report = do_parse(report_class, raw_report_json, raw_chunks)
92+
93+
def bench_fn():
94+
filtered = report.filter(paths=[".*"], flags=["unit"])
95+
filtered._process_totals()
96+
for file in report:
97+
file._process_totals()
98+
# the `FilteredReportFile` has no `__iter__`, and all the other have no `.lines`.
99+
# what they do have in common is `eof` and `get`:
100+
for ln in range(1, file.eof):
101+
report.get(ln)
102+
103+
benchmark(bench_fn)
104+
105+
106+
@pytest.mark.parametrize("report_class", EDITABLE_VARIANTS)
107+
def test_report_serialize(report_class, mocker, benchmark):
108+
raw_chunks, raw_report_json = init_mocks(mocker, False)
109+
110+
report = do_parse(report_class, raw_report_json, raw_chunks)
111+
112+
def bench_fn():
113+
report.to_database()
114+
report.to_archive()
115+
116+
benchmark(bench_fn)
117+
118+
119+
@pytest.mark.parametrize("report_class", EDITABLE_VARIANTS)
120+
def test_report_merge(report_class, mocker, benchmark):
121+
raw_chunks, raw_report_json = init_mocks(mocker, False)
122+
123+
report1 = do_parse(report_class, raw_report_json, raw_chunks)
124+
report2 = do_parse(report_class, raw_report_json, raw_chunks)
125+
126+
def bench_fn():
127+
report1.merge(report2)
128+
129+
benchmark(bench_fn)
130+
131+
132+
@pytest.mark.parametrize("report_class", EDITABLE_VARIANTS)
133+
def test_report_carryforward(report_class, mocker, benchmark):
134+
raw_chunks, raw_report_json = init_mocks(mocker, False)
135+
136+
report = do_parse(report_class, raw_report_json, raw_chunks)
137+
138+
def bench_fn():
139+
generate_carryforward_report(report, paths=[".*"], flags=["unit"])
140+
141+
benchmark(bench_fn)

uv.lock

Lines changed: 63 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)