Skip to content

Commit d7f5049

Browse files
committed
a PariThreadPool to handle multithreading via Python
1 parent e2c9824 commit d7f5049

File tree

9 files changed

+100
-3
lines changed

9 files changed

+100
-3
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
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

Lines changed: 1 addition & 0 deletions
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

Lines changed: 4 additions & 1 deletion
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

Lines changed: 6 additions & 0 deletions
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

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

cypari2/types.pxd

Lines changed: 2 additions & 1 deletion
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/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Welcome to CyPari2's documentation!
1616
closure
1717
handle_error
1818
convert
19+
threads
1920

2021

2122
Indices and tables

docs/source/threads.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.. automodule:: cypari2.threads
2+
:members:

tests/rundoctest.py

Lines changed: 1 addition & 1 deletion
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)