Skip to content

Commit b8003c5

Browse files
feat: use instrument_hooks markers in walltime
1 parent 92100fb commit b8003c5

3 files changed

Lines changed: 74 additions & 13 deletions

File tree

src/pytest_codspeed/instruments/hooks/__init__.py

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,21 @@
1515

1616
from .dist_instrument_hooks import InstrumentHooksPointer, LibType
1717

18-
# Feature flags for instrument hooks
19-
FEATURE_DISABLE_CALLGRIND_MARKERS = 0
18+
19+
def _load_lib_constant(name: str) -> int:
20+
from .dist_instrument_hooks import lib # type: ignore
21+
22+
return getattr(lib, name)
23+
24+
25+
# Constants are defined in instrument-hooks/includes/core.h and exposed via cffi.
26+
FEATURE_DISABLE_CALLGRIND_MARKERS = _load_lib_constant(
27+
"FEATURE_DISABLE_CALLGRIND_MARKERS"
28+
)
29+
MARKER_TYPE_SAMPLE_START = _load_lib_constant("MARKER_TYPE_SAMPLE_START")
30+
MARKER_TYPE_SAMPLE_END = _load_lib_constant("MARKER_TYPE_SAMPLE_END")
31+
MARKER_TYPE_BENCHMARK_START = _load_lib_constant("MARKER_TYPE_BENCHMARK_START")
32+
MARKER_TYPE_BENCHMARK_END = _load_lib_constant("MARKER_TYPE_BENCHMARK_END")
2033

2134

2235
class InstrumentHooks:
@@ -79,6 +92,30 @@ def set_executed_benchmark(self, uri: str, pid: int | None = None) -> None:
7992
if ret != 0:
8093
warnings.warn("Failed to set executed benchmark", RuntimeWarning)
8194

95+
@staticmethod
96+
def current_timestamp() -> int:
97+
"""Return a monotonic timestamp in nanoseconds from the native library."""
98+
from .dist_instrument_hooks import lib # type: ignore
99+
100+
return lib.instrument_hooks_current_timestamp()
101+
102+
def add_marker(
103+
self, marker_type: int, timestamp: int, pid: int | None = None
104+
) -> None:
105+
"""Emit a single marker at the given timestamp."""
106+
if pid is None:
107+
pid = os.getpid()
108+
ret = self.lib.instrument_hooks_add_marker(
109+
self.instance, pid, marker_type, timestamp
110+
)
111+
if ret != 0:
112+
warnings.warn("Failed to add marker", RuntimeWarning)
113+
114+
def add_benchmark_timestamps(self, start: int, end: int) -> None:
115+
"""Emit a BenchmarkStart/BenchmarkEnd marker pair around a captured window."""
116+
self.add_marker(MARKER_TYPE_BENCHMARK_START, start)
117+
self.add_marker(MARKER_TYPE_BENCHMARK_END, end)
118+
82119
def set_integration(self, name: str, version: str) -> None:
83120
"""Set the integration name and version."""
84121
ret = self.lib.instrument_hooks_set_integration(

src/pytest_codspeed/instruments/hooks/build.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@
3535
void callgrind_start_instrumentation();
3636
void callgrind_stop_instrumentation();
3737
38+
typedef enum {
39+
FEATURE_DISABLE_CALLGRIND_MARKERS = 0,
40+
} instrument_hooks_feature_t;
41+
3842
void instrument_hooks_set_feature(uint64_t feature, bool enabled);
3943
4044
uint8_t instrument_hooks_set_environment(InstrumentHooks *, const char *section_name,

src/pytest_codspeed/instruments/walltime.py

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ def get_instrument_config_str_and_warns(self) -> tuple[str, list[str]]:
183183
)
184184
return config_str, []
185185

186-
def measure(
186+
def measure( # noqa: C901
187187
self,
188188
marker_options: BenchmarkMarkerOptions,
189189
name: str,
@@ -232,21 +232,31 @@ def __codspeed_root_frame__() -> T:
232232
# Benchmark
233233
iter_range = range(iter_per_round)
234234
run_start = perf_counter_ns()
235-
if self.instrument_hooks:
236-
self.instrument_hooks.start_benchmark()
235+
hooks = self.instrument_hooks
236+
if hooks:
237+
hooks.start_benchmark()
237238
for _ in range(rounds):
239+
instrument_hooks_start = hooks.current_timestamp() if hooks else None
238240
start = perf_counter_ns()
241+
239242
for _ in iter_range:
240243
__codspeed_root_frame__()
244+
241245
end = perf_counter_ns()
246+
if hooks and instrument_hooks_start is not None:
247+
instrument_hooks_end = hooks.current_timestamp()
248+
hooks.add_benchmark_timestamps(
249+
instrument_hooks_start, instrument_hooks_end
250+
)
251+
242252
times_per_round_ns.append(end - start)
243253

244254
if end - run_start > benchmark_config.max_time_ns:
245255
# TODO: log something
246256
break
247-
if self.instrument_hooks:
248-
self.instrument_hooks.stop_benchmark()
249-
self.instrument_hooks.set_executed_benchmark(uri)
257+
if hooks:
258+
hooks.stop_benchmark()
259+
hooks.set_executed_benchmark(uri)
250260
benchmark_end = perf_counter_ns()
251261
total_time = (benchmark_end - run_start) / 1e9
252262

@@ -290,20 +300,30 @@ def __codspeed_root_frame__(*args, **kwargs) -> T:
290300
# Benchmark
291301
times_per_round_ns: list[float] = []
292302
benchmark_start = perf_counter_ns()
293-
if self.instrument_hooks:
294-
self.instrument_hooks.start_benchmark()
303+
hooks = self.instrument_hooks
304+
if hooks:
305+
hooks.start_benchmark()
295306
for _ in range(pedantic_options.rounds):
296307
args, kwargs = pedantic_options.setup_and_get_args_kwargs()
308+
instrument_hooks_start = hooks.current_timestamp() if hooks else None
297309
start = perf_counter_ns()
310+
298311
for _ in iter_range:
299312
__codspeed_root_frame__(*args, **kwargs)
313+
300314
end = perf_counter_ns()
315+
if hooks and instrument_hooks_start is not None:
316+
instrument_hooks_end = hooks.current_timestamp()
317+
hooks.add_benchmark_timestamps(
318+
instrument_hooks_start, instrument_hooks_end
319+
)
320+
301321
times_per_round_ns.append(end - start)
302322
if pedantic_options.teardown is not None:
303323
pedantic_options.teardown(*args, **kwargs)
304-
if self.instrument_hooks:
305-
self.instrument_hooks.stop_benchmark()
306-
self.instrument_hooks.set_executed_benchmark(uri)
324+
if hooks:
325+
hooks.stop_benchmark()
326+
hooks.set_executed_benchmark(uri)
307327
benchmark_end = perf_counter_ns()
308328
total_time = (benchmark_end - benchmark_start) / 1e9
309329
stats = BenchmarkStats.from_list(

0 commit comments

Comments
 (0)