Skip to content

Commit c0d2391

Browse files
committed
a PariThreadPool to handle multithreading via Python
1 parent 2138dc2 commit c0d2391

File tree

8 files changed

+89
-3
lines changed

8 files changed

+89
-3
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ cypari2/handle_error.c
1313
cypari2/pari_instance.c
1414
cypari2/stack.c
1515
cypari2/string_utils.c
16+
cypari2/threads.c
1617

1718
# Byte-compiled / optimized / DLL files
1819
__pycache__/

cypari2/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
from .pari_instance import Pari
22
from .handle_error import PariError
33
from .gen import Gen
4+
from .threads import PariThreadPool

cypari2/stack.pyx

+4-1
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,10 @@ cdef Gen new_gen_noclear(GEN x):
209209
elif isclone(x):
210210
gclone_refc(x)
211211
return Gen_new(x, x)
212-
raise SystemError("new_gen() argument not on PARI stack, not on PARI heap and not a universal constant")
212+
else:
213+
# NOTE: it might be the case that x belongs to a local stack of a thread
214+
# In that case we copy it in the main stack
215+
x = gcopy(x)
213216

214217
z = Gen_stack_new(x)
215218

cypari2/threads.pxd

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from .types cimport *
2+
3+
cdef class PariThreadPool:
4+
cdef size_t nbthreads
5+
cdef pari_thread * pths
6+
cdef size_t ithread

cypari2/threads.pyx

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
r"""
2+
Multithreading from Python
3+
"""
4+
5+
from libc.stdlib cimport malloc, calloc, free
6+
7+
from .types cimport *
8+
from .paridecl cimport *
9+
from gen cimport Gen, objtogen
10+
11+
cdef class PariThreadPool:
12+
r"""
13+
Pari thread allocator
14+
15+
This class is intended to be used in conjunction with the multithreading
16+
capabilities of the ``ThreadPoolExecutor`` from the ``concurrent.futures``
17+
Python library.
18+
19+
Examples:
20+
21+
>>> from concurrent.futures import ThreadPoolExecutor, as_completed
22+
>>> from cypari2 import Pari, PariThreadPool
23+
>>> pari = Pari()
24+
>>> pari.default('nbthreads', 1)
25+
>>> max_workers = 4
26+
>>> pari_pool = PariThreadPool(max_workers)
27+
>>> square_free = []
28+
>>> with ThreadPoolExecutor(max_workers=max_workers, initializer=pari_pool.initializer) as executor:
29+
... futures = {executor.submit(pari.issquarefree, n): n for n in range(10**6, 10**6 + 1000)}
30+
... for future in as_completed(futures):
31+
... n = futures[future]
32+
... if future.result():
33+
... square_free.append(n)
34+
>>> square_free.sort()
35+
>>> square_free
36+
[1000001, 1000002, 1000003, 1000005, 1000006, ..., 1000994, 1000995, 1000997, 1000999]
37+
"""
38+
def __init__(self, size_t nbthreads, size_t size=8000000, size_t sizemax=0):
39+
r"""
40+
INPUT:
41+
42+
- ``nbthreads`` -- the number of threads to allocate
43+
44+
- ``size`` -- (default: 8000000) the number of bytes for the
45+
initial PARI stack (see notes below)
46+
47+
- ``sizemax`` -- (default: 0) the maximal number of bytes for the
48+
dynamically increasing PARI stack.
49+
"""
50+
cdef size_t i
51+
size = max(size, pari_mainstack.rsize)
52+
sizemax = max(max(size, pari_mainstack.vsize), sizemax)
53+
self.pths = <pari_thread *> calloc(nbthreads, sizeof(pari_thread))
54+
for i in range(nbthreads):
55+
pari_thread_valloc(self.pths + i, size, sizemax, NULL)
56+
self.ithread = 0
57+
self.nbthreads = nbthreads
58+
59+
def __dealloc__(self):
60+
cdef size_t i
61+
for i in range(self.ithread):
62+
pari_thread_free(self.pths + i)
63+
free(self.pths)
64+
65+
def __repr__(self):
66+
return 'Pari thread pool with {} threads'.format(self.nbthreads)
67+
68+
def initializer(self):
69+
if self.ithread >= self.nbthreads:
70+
raise ValueError('no more thread available')
71+
pari_thread_start(self.pths + self.ithread)
72+
self.ithread += 1

cypari2/types.pxd

+2-1
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,8 @@ cdef extern from "pari/pari.h":
125125
struct pariFILE
126126
struct pari_mt
127127
struct pari_stack
128-
struct pari_thread
128+
struct pari_thread:
129+
pass
129130
struct pari_timer
130131
struct GENbin
131132
struct hashentry

docs/source/threads.rst

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.. automodule:: cypari2.threads
2+
:members:

tests/rundoctest.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
attempted = 0
3030
for mod in [cypari2.closure, cypari2.convert, cypari2.gen,
3131
cypari2.handle_error, cypari2.pari_instance, cypari2.stack,
32-
cypari2.string_utils,
32+
cypari2.string_utils, cypari2.threads,
3333
autogen.doc, autogen.generator, autogen.parser,
3434
autogen.paths]:
3535

0 commit comments

Comments
 (0)