Skip to content

Commit ad6bf5c

Browse files
authored
Adding PyGILState_Check() in object_api<>::operator(). (#2919)
* Adding PyGILState_Check() in object_api<>::operator(). * Enabling PyGILState_Check() for Python >= 3.6 only. Possibly, this explains why PyGILState_Check() cannot safely be used with Python 3.4 and 3.5: python/cpython#10267 (comment) * Adding simple micro benchmark. * Reducing test time to minimum (purely for coverage, not for accurate results). * Fixing silly oversight. * Minor code organization improvement in test. * Adding example runtimes. * Removing capsys (just run with `-k test_callback_num_times -s` and using `.format()`.
1 parent f676782 commit ad6bf5c

File tree

3 files changed

+43
-0
lines changed

3 files changed

+43
-0
lines changed

include/pybind11/cast.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1348,6 +1348,11 @@ unpacking_collector<policy> collect_arguments(Args &&...args) {
13481348
template <typename Derived>
13491349
template <return_value_policy policy, typename... Args>
13501350
object object_api<Derived>::operator()(Args &&...args) const {
1351+
#if !defined(NDEBUG) && PY_VERSION_HEX >= 0x03060000
1352+
if (!PyGILState_Check()) {
1353+
pybind11_fail("pybind11::object_api<>::operator() PyGILState_Check() failure.");
1354+
}
1355+
#endif
13511356
return detail::collect_arguments<policy>(std::forward<Args>(args)...).call(derived().ptr());
13521357
}
13531358

tests/test_callbacks.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,4 +172,10 @@ TEST_SUBMODULE(callbacks, m) {
172172
for (auto i : work)
173173
start_f(py::cast<int>(i));
174174
});
175+
176+
m.def("callback_num_times", [](py::function f, std::size_t num) {
177+
for (std::size_t i = 0; i < num; i++) {
178+
f();
179+
}
180+
});
175181
}

tests/test_callbacks.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import pytest
33
from pybind11_tests import callbacks as m
44
from threading import Thread
5+
import time
56

67

78
def test_callbacks():
@@ -146,3 +147,34 @@ def test_async_async_callbacks():
146147
t = Thread(target=test_async_callbacks)
147148
t.start()
148149
t.join()
150+
151+
152+
def test_callback_num_times():
153+
# Super-simple micro-benchmarking related to PR #2919.
154+
# Example runtimes (Intel Xeon 2.2GHz, fully optimized):
155+
# num_millions 1, repeats 2: 0.1 secs
156+
# num_millions 20, repeats 10: 11.5 secs
157+
one_million = 1000000
158+
num_millions = 1 # Try 20 for actual micro-benchmarking.
159+
repeats = 2 # Try 10.
160+
rates = []
161+
for rep in range(repeats):
162+
t0 = time.time()
163+
m.callback_num_times(lambda: None, num_millions * one_million)
164+
td = time.time() - t0
165+
rate = num_millions / td if td else 0
166+
rates.append(rate)
167+
if not rep:
168+
print()
169+
print(
170+
"callback_num_times: {:d} million / {:.3f} seconds = {:.3f} million / second".format(
171+
num_millions, td, rate
172+
)
173+
)
174+
if len(rates) > 1:
175+
print("Min Mean Max")
176+
print(
177+
"{:6.3f} {:6.3f} {:6.3f}".format(
178+
min(rates), sum(rates) / len(rates), max(rates)
179+
)
180+
)

0 commit comments

Comments
 (0)