Skip to content

Commit a282438

Browse files
authored
Add basic benchmarks (#141)
1 parent 194b1b5 commit a282438

4 files changed

Lines changed: 120 additions & 2 deletions

File tree

.github/workflows/dissect-ci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ jobs:
1212
ci:
1313
uses: fox-it/dissect-workflow-templates/.github/workflows/dissect-ci-template.yml@main
1414
secrets: inherit
15+
with:
16+
run-benchmarks: true
17+
1518

1619
publish:
1720
if: ${{ github.ref_name == 'main' || github.ref_type == 'tag' }}

tests/conftest.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,31 @@
1+
from __future__ import annotations
2+
3+
import importlib.util
4+
15
import pytest
26

37
from dissect.cstruct.cstruct import cstruct
48

9+
HAS_BENCHMARK = importlib.util.find_spec("pytest_benchmark") is not None
10+
11+
12+
def pytest_configure(config: pytest.Config) -> None:
13+
if not HAS_BENCHMARK:
14+
# If we don't have pytest-benchmark (or pytest-codspeed) installed, register the benchmark marker ourselves
15+
# to avoid pytest warnings
16+
config.addinivalue_line("markers", "benchmark: mark test for benchmarking (requires pytest-benchmark)")
17+
18+
19+
def pytest_runtest_setup(item: pytest.Item) -> None:
20+
if not HAS_BENCHMARK and item.get_closest_marker("benchmark") is not None:
21+
pytest.skip("pytest-benchmark is not installed")
22+
523

624
@pytest.fixture
725
def cs() -> cstruct:
826
return cstruct()
927

1028

11-
@pytest.fixture(params=[True, False])
29+
@pytest.fixture(params=[True, False], ids=["compiled", "interpreted"])
1230
def compiled(request: pytest.FixtureRequest) -> bool:
1331
return request.param

tests/test_benchmark.py

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
from __future__ import annotations
2+
3+
from typing import TYPE_CHECKING
4+
5+
import pytest
6+
7+
if TYPE_CHECKING:
8+
from pytest_benchmark.fixture import BenchmarkFixture
9+
10+
from dissect.cstruct.cstruct import cstruct
11+
12+
13+
@pytest.mark.benchmark
14+
def test_benchmark_basic(cs: cstruct, compiled: bool, benchmark: BenchmarkFixture) -> None:
15+
"""Benchmark the parsing of a simple struct with both the compiled and interpreted backends."""
16+
cdef = """
17+
struct test {
18+
uint32 a;
19+
uint64 b;
20+
uint16 c;
21+
uint8 d;
22+
};
23+
"""
24+
cs.load(cdef, compiled=compiled)
25+
26+
benchmark(lambda: cs.test(b"\x01\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x00\x04"))
27+
28+
29+
@pytest.mark.benchmark
30+
def test_benchmark_union(cs: cstruct, compiled: bool, benchmark: BenchmarkFixture) -> None:
31+
"""Benchmark the parsing of a simple union with both the compiled and interpreted backends."""
32+
cdef = """
33+
union test {
34+
uint32 a;
35+
uint64 b;
36+
uint16 c;
37+
uint8 d;
38+
};
39+
"""
40+
cs.load(cdef, compiled=compiled)
41+
42+
benchmark(lambda: cs.test(b"\x01\x02\x03\x04\x05\x06\x07\x08"))
43+
44+
45+
@pytest.mark.benchmark
46+
def test_benchmark_attribute_access(cs: cstruct, benchmark: BenchmarkFixture) -> None:
47+
"""Benchmark the attribute access of a parsed struct."""
48+
cdef = """
49+
struct test {
50+
uint32 a;
51+
uint64 b;
52+
uint16 c;
53+
uint8 d;
54+
};
55+
"""
56+
cs.load(cdef)
57+
obj = cs.test(b"\x01\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x03\x00\x04")
58+
59+
benchmark(lambda: (obj.a, obj.b, obj.c, obj.d))
60+
61+
62+
@pytest.mark.benchmark
63+
def test_benchmark_getattr_constants(cs: cstruct, benchmark: BenchmarkFixture) -> None:
64+
"""Benchmark the resolving of constants on the cstruct instance."""
65+
cdef = """
66+
#define CONST1 1
67+
"""
68+
cs.load(cdef)
69+
70+
benchmark(lambda: cs.CONST1)
71+
72+
73+
@pytest.mark.benchmark
74+
def test_benchmark_getattr_types(cs: cstruct, benchmark: BenchmarkFixture) -> None:
75+
"""Benchmark the resolving of types on the cstruct instance."""
76+
benchmark(lambda: cs.uint8)
77+
78+
79+
@pytest.mark.benchmark
80+
def test_benchmark_getattr_typedefs(cs: cstruct, benchmark: BenchmarkFixture) -> None:
81+
"""Benchmark the resolving of typedefs on the cstruct instance."""
82+
cdef = """
83+
typedef uint8 my_uint8;
84+
"""
85+
cs.load(cdef)
86+
87+
benchmark(lambda: cs.my_uint8)

tox.ini

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,20 @@ deps =
1818
coverage
1919
dependency_groups = test
2020
commands =
21-
pytest --basetemp="{envtmpdir}" {posargs:--color=yes --cov=dissect --cov-report=term-missing -v tests}
21+
pytest --basetemp="{envtmpdir}" --import-mode="append" {posargs:--color=yes --cov=dissect --cov-report=term-missing -v tests}
2222
coverage report
2323
coverage xml
2424

25+
[testenv:benchmark]
26+
deps =
27+
pytest-benchmark
28+
pytest-codspeed
29+
dependency_groups = test
30+
passenv =
31+
CODSPEED_ENV
32+
commands =
33+
pytest --basetemp="{envtmpdir}" --import-mode="append" -m benchmark {posargs:--color=yes -v tests}
34+
2535
[testenv:build]
2636
package = skip
2737
dependency_groups = build

0 commit comments

Comments
 (0)