Skip to content

Commit 1fd3af0

Browse files
authored
Write specific benchmarks for CodSpeed (#1306)
* Write specific benchmarks for CodSpeed * Add frozen benchmark * Remove instantiation noise * Put into separate dir like cattrs * Be consistent
1 parent 0a09c1a commit 1fd3af0

File tree

4 files changed

+125
-7
lines changed

4 files changed

+125
-7
lines changed

bench/test_benchmarks.py

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
"""
2+
Benchmark attrs using CodSpeed.
3+
"""
4+
5+
from __future__ import annotations
6+
7+
import pytest
8+
9+
import attrs
10+
11+
12+
pytestmark = pytest.mark.benchmark()
13+
14+
ROUNDS = 1_000
15+
16+
17+
def test_create_simple_class():
18+
"""
19+
Benchmark creating a simple class without any extras.
20+
"""
21+
for _ in range(ROUNDS):
22+
23+
@attrs.define
24+
class LocalC:
25+
x: int
26+
y: str
27+
z: dict[str, int]
28+
29+
30+
def test_create_frozen_class():
31+
"""
32+
Benchmark creating a frozen class without any extras.
33+
"""
34+
for _ in range(ROUNDS):
35+
36+
@attrs.frozen
37+
class LocalC:
38+
x: int
39+
y: str
40+
z: dict[str, int]
41+
42+
LocalC(1, "2", {})
43+
44+
45+
def test_create_simple_class_make_class():
46+
"""
47+
Benchmark creating a simple class using attrs.make_class().
48+
"""
49+
for i in range(ROUNDS):
50+
LocalC = attrs.make_class(
51+
f"LocalC{i}",
52+
{
53+
"x": attrs.field(type=int),
54+
"y": attrs.field(type=str),
55+
"z": attrs.field(type=dict[str, int]),
56+
},
57+
)
58+
59+
LocalC(1, "2", {})
60+
61+
62+
@attrs.define
63+
class C:
64+
x: int = 0
65+
y: str = "foo"
66+
z: dict[str, int] = attrs.Factory(dict)
67+
68+
69+
def test_instantiate_no_defaults():
70+
"""
71+
Benchmark instantiating a class without using any defaults.
72+
"""
73+
for _ in range(ROUNDS):
74+
C(1, "2", {})
75+
76+
77+
def test_instantiate_with_defaults():
78+
"""
79+
Benchmark instantiating a class relying on defaults.
80+
"""
81+
for _ in range(ROUNDS):
82+
C()
83+
84+
85+
def test_eq_equal():
86+
"""
87+
Benchmark comparing two equal instances for equality.
88+
"""
89+
c1 = C()
90+
c2 = C()
91+
92+
for _ in range(ROUNDS):
93+
c1 == c2
94+
95+
96+
def test_eq_unequal():
97+
"""
98+
Benchmark comparing two unequal instances for equality.
99+
"""
100+
c1 = C()
101+
c2 = C(1, "bar", {"baz": 42})
102+
103+
for _ in range(ROUNDS):
104+
c1 == c2
105+
106+
107+
@attrs.frozen
108+
class HashableC:
109+
x: int = 0
110+
y: str = "foo"
111+
z: tuple[str] = ("bar",)
112+
113+
114+
def test_hash():
115+
"""
116+
Benchmark hashing an instance.
117+
"""
118+
c = HashableC()
119+
120+
for _ in range(ROUNDS):
121+
hash(c)

pyproject.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,9 @@ ignore = [
220220
]
221221

222222
[tool.ruff.lint.per-file-ignores]
223+
"bench/**" = [
224+
"INP001", # Benchmarks don't have to be importable.
225+
]
223226
"**/test_*" = [
224227
"ARG005", # we need stub lambdas
225228
"S",

tests/test_functional.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,6 @@ class TestFunctional:
111111
Functional tests.
112112
"""
113113

114-
@pytest.mark.benchmark()
115114
@pytest.mark.parametrize("cls", [C2, C2Slots])
116115
def test_fields(self, cls):
117116
"""
@@ -146,7 +145,6 @@ def test_fields(self, cls):
146145
),
147146
) == attr.fields(cls)
148147

149-
@pytest.mark.benchmark()
150148
@pytest.mark.parametrize("cls", [C1, C1Slots])
151149
def test_asdict(self, cls):
152150
"""
@@ -182,7 +180,6 @@ class C3:
182180

183181
assert "C3(_x=1)" == repr(C3(x=1))
184182

185-
@pytest.mark.benchmark()
186183
@given(booleans(), booleans())
187184
def test_programmatic(self, slots, frozen):
188185
"""
@@ -340,7 +337,6 @@ def test_metaclass_preserved(self, cls):
340337
"""
341338
assert Meta is type(cls)
342339

343-
@pytest.mark.benchmark()
344340
def test_default_decorator(self):
345341
"""
346342
Default decorator sets the default and the respective method gets
@@ -544,7 +540,6 @@ class C:
544540
assert "itemgetter" == attr.fields(C).itemgetter.name
545541
assert "x" == attr.fields(C).x.name
546542

547-
@pytest.mark.benchmark()
548543
def test_auto_exc(self, slots, frozen):
549544
"""
550545
Classes with auto_exc=True have a Exception-style __str__, compare and
@@ -739,7 +734,6 @@ class D(C):
739734
assert "_setattr('y', y)" in src
740735
assert object.__setattr__ != D.__setattr__
741736

742-
@pytest.mark.benchmark()
743737
def test_unsafe_hash(self, slots):
744738
"""
745739
attr.s(unsafe_hash=True) makes a class hashable.

tox.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ pass_env =
5656
ARCH
5757
PYTHONHASHSEED
5858
PYTHONMALLOC
59-
commands = pytest --codspeed -n auto
59+
commands = pytest --codspeed -n auto bench/test_benchmarks.py
6060

6161

6262
[testenv:docs]

0 commit comments

Comments
 (0)