Skip to content

Commit 3a734ca

Browse files
authored
feat: free-threaded pybind11 (#55)
Signed-off-by: Henry Schreiner <[email protected]>
1 parent 7cc8622 commit 3a734ca

File tree

5 files changed

+67
-2
lines changed

5 files changed

+67
-2
lines changed

projects/hello-free-threading/CMakeLists.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,7 @@ find_package(
1313

1414
python_add_library(_core MODULE src/freecomputepi/_core.cpp WITH_SOABI)
1515

16-
install(TARGETS _core DESTINATION freecomputepi)
16+
find_package(pybind11 REQUIRED)
17+
pybind11_add_module(_pybcore MODULE src/freecomputepi/_pybcore.cpp)
18+
19+
install(TARGETS _core _pybcore DESTINATION freecomputepi)

projects/hello-free-threading/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[build-system]
2-
requires = ["scikit-build-core >=0.9.5"]
2+
requires = ["scikit-build-core >=0.9.5", "pybind11 >=2.13.1"]
33
build-backend = "scikit_build_core.build"
44

55
[project]
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#include <pybind11/pybind11.h>
2+
#include <random>
3+
4+
namespace py = pybind11;
5+
6+
double pi(int n) {
7+
double sum = 0.0;
8+
9+
std::random_device r;
10+
std::default_random_engine e1(r());
11+
std::uniform_real_distribution<double> uniform_dist(-1, 1);
12+
13+
for (int i = 0; i < n; i++) {
14+
double x = uniform_dist(e1);
15+
double y = uniform_dist(e1);
16+
if (x * x + y * y <= 1.0) {
17+
sum += 1.0;
18+
}
19+
}
20+
return 4.0 * sum / n;
21+
}
22+
23+
PYBIND11_MODULE(_pybcore, m, py::mod_gil_not_used()) {
24+
m.def("pi", &pi);
25+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import argparse
2+
import statistics
3+
import time
4+
from concurrent.futures import ThreadPoolExecutor
5+
6+
from ._pybcore import pi
7+
8+
9+
def pi_in_threads(threads: int, trials: int) -> float:
10+
if threads == 0:
11+
return pi(trials)
12+
with ThreadPoolExecutor(max_workers=threads) as executor:
13+
return statistics.mean(executor.map(pi, [trials // threads] * threads))
14+
15+
16+
def main() -> None:
17+
parser = argparse.ArgumentParser()
18+
parser.add_argument("--threads", type=int, default=0)
19+
parser.add_argument("--trials", type=int, default=100_000_000)
20+
args = parser.parse_args()
21+
22+
start = time.monotonic()
23+
π = pi_in_threads(args.threads, args.trials)
24+
stop = time.monotonic()
25+
26+
print(f"{args.trials} trials, {π = }, {stop - start:.4} s")
27+
28+
29+
if __name__ == "__main__":
30+
main()

projects/hello-free-threading/tests/test_thread.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import freecomputepi.comp
22
import freecomputepi.pure
3+
import freecomputepi.pybind
34
import pytest
45

56

@@ -13,3 +14,9 @@ def test_pure(threads):
1314
def test_comp(threads):
1415
π = freecomputepi.comp.pi_in_threads(threads, 100_000_000)
1516
assert π == pytest.approx(3.1415926535, rel=0.01)
17+
18+
19+
@pytest.mark.parametrize("threads", [0, 1, 2, 4])
20+
def test_pybind(threads):
21+
π = freecomputepi.pybind.pi_in_threads(threads, 100_000_000)
22+
assert π == pytest.approx(3.1415926535, rel=0.01)

0 commit comments

Comments
 (0)