Skip to content

Commit 6adf2f3

Browse files
committed
test(perf): guard import laziness and parallel coordinator scaling
1 parent 43f3651 commit 6adf2f3

1 file changed

Lines changed: 73 additions & 0 deletions

File tree

tests/test_perf_budget.py

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
from __future__ import annotations
22

3+
import concurrent.futures
34
import io
5+
import subprocess
46
import sys
57
import timeit
68

@@ -82,3 +84,74 @@ def test_iterator_overhead_budget() -> None:
8284
f'({clock_ns:.1f} ns) - likely a regression to per-iteration '
8385
f'clock reads'
8486
)
87+
88+
89+
def test_import_stays_lazy() -> None:
90+
# Deterministic, not timing-based: `import progressbar` must load
91+
# nothing beyond the package itself and the version module. Anything
92+
# else appearing here means an eager import crept into __init__.py
93+
# and the ~1.6 ms import time regressed for every consumer.
94+
out: str = subprocess.run(
95+
[
96+
sys.executable,
97+
'-c',
98+
'import sys, progressbar; '
99+
"print(','.join(sorted("
100+
"m for m in sys.modules if m.startswith('progressbar'))))",
101+
],
102+
capture_output=True,
103+
text=True,
104+
check=True,
105+
).stdout.strip()
106+
assert out == 'progressbar,progressbar.__about__', (
107+
f'import progressbar eagerly loaded: {out}'
108+
)
109+
110+
111+
def _noop(value: int) -> int:
112+
return value
113+
114+
115+
def _parallel_map_us_per_item(n: int) -> float:
116+
"""Per-item wall cost of `progressbar.map` on a shared thread pool.
117+
118+
The executor is created outside the measurement, so this isolates
119+
the coordinator itself: chunking, submission windowing, the
120+
done-queue and result assembly.
121+
"""
122+
import progressbar
123+
124+
with concurrent.futures.ThreadPoolExecutor(4) as executor:
125+
# Warm-up so pool spin-up and lazy imports land outside timing.
126+
progressbar.map(_noop, range(64), pool=executor, bar=False)
127+
elapsed: float = min(
128+
timeit.timeit(
129+
lambda: progressbar.map(
130+
_noop, range(n), pool=executor, bar=False
131+
),
132+
number=1,
133+
)
134+
for _ in range(3)
135+
)
136+
return elapsed / n * 1e6
137+
138+
139+
@pytest.mark.no_freezegun
140+
def test_parallel_map_overhead_scales_linearly() -> None:
141+
# Measure both before any early return so every line runs under
142+
# coverage (same pattern as the iterator budget above).
143+
small: float = _parallel_map_us_per_item(1_000)
144+
large: float = _parallel_map_us_per_item(10_000)
145+
if _coverage_active():
146+
return
147+
# Machine-independent guard for the done-queue design: per-item cost
148+
# must stay flat as the batch grows. The rejected coordinator design
149+
# (re-registering a waiter on every pending future each poll) scales
150+
# with batch size and blows past this immediately at 10x the items.
151+
# A 3x ceiling tolerates noisy runners without letting an O(n) tick
152+
# regime back in.
153+
assert large < 3 * small, (
154+
f'parallel map per-item cost grew from {small:.2f} us at 1k items '
155+
f'to {large:.2f} us at 10k items - the coordinator is no longer '
156+
f'O(1) per completion'
157+
)

0 commit comments

Comments
 (0)