5
from cython.parallel import prange, threadid
7
from libc.stdlib cimport malloc, free
9
openmp.omp_set_nested(1)
11
cdef int forward(int x) nogil:
18
cdef int maxthreads = openmp.omp_get_max_threads()
19
cdef int *buf = <int *> malloc(sizeof(int) * maxthreads)
24
with nogil, cython.parallel.parallel():
25
buf[threadid()] = threadid()
26
# Recognise threadid() also when it's used in a function argument.
27
# See https://github.com/cython/cython/issues/3594
28
buf[forward(cython.parallel.threadid())] = forward(threadid())
30
for i in range(maxthreads):
35
cdef int get_num_threads() noexcept with gil:
36
print "get_num_threads called"
39
cdef bint check_size(int size) nogil:
42
def test_num_threads(int size):
44
>>> test_num_threads(6)
46
get_num_threads called
48
get_num_threads called
50
get_num_threads called
52
get_num_threads called
54
>>> test_num_threads(4)
56
get_num_threads called
58
get_num_threads called
60
get_num_threads called
62
get_num_threads called
65
cdef int dyn = openmp.omp_get_dynamic()
67
cdef int *p = &num_threads
69
openmp.omp_set_dynamic(0)
71
with nogil, cython.parallel.parallel(num_threads=1):
72
p[0] = openmp.omp_get_num_threads()
76
with nogil, cython.parallel.parallel(num_threads=get_num_threads(), use_threads_if=size > 5):
77
p[0] = openmp.omp_get_num_threads()
81
# Checks that temporary variables are released properly
82
with nogil, cython.parallel.parallel(num_threads=get_num_threads(), use_threads_if=check_size(size)):
83
p[0] = openmp.omp_get_num_threads()
88
# Checks that temporary variables are released properly
89
for i in prange(1, nogil=True, num_threads=get_num_threads(), use_threads_if=check_size(size)):
90
p[0] = openmp.omp_get_num_threads()
96
for i in prange(1, nogil=True, num_threads=get_num_threads(), use_threads_if=size > 5):
97
p[0] = openmp.omp_get_num_threads()
100
openmp.omp_set_dynamic(dyn)
105
def test_parallel_catch():
107
>>> test_parallel_catch()
110
cdef int i, j, num_threads
113
for i in prange(100, nogil=True, num_threads=4):
114
num_threads = openmp.omp_get_num_threads()
118
for j in prange(100, nogil=True):
121
raise Exception("try and catch me if you can!")
126
print len(exceptions) == num_threads
127
assert len(exceptions) == num_threads, (len(exceptions), num_threads)
131
cdef void parallel_exception_checked_function(int* ptr, int id) except * nogil:
132
# requires the GIL after each call
135
cdef void parallel_call_exception_checked_function_impl(int* arr, int num_threads) nogil:
136
# Inside a nogil function, parallel can't be sure that the GIL has been released.
137
# Therefore Cython must release the GIL itself.
138
# Otherwise, we can experience cause lock-ups if anything inside it acquires the GIL
139
# (since if any other thread has finished, it will be holding the GIL).
141
# An equivalent test with prange is in "sequential_parallel.pyx"
142
with cython.parallel.parallel(num_threads=num_threads):
143
parallel_exception_checked_function(arr+threadid(), threadid())
146
def test_parallel_call_exception_checked_function():
148
test_parallel_call_exception_checked_function()
150
cdef int maxthreads = openmp.omp_get_max_threads()
151
cdef int *buf = <int *> malloc(sizeof(int) * maxthreads)
157
# Note we *don't* release the GIL here
158
parallel_call_exception_checked_function_impl(buf, maxthreads)
160
for i in range(maxthreads):
166
OPENMP_PARALLEL = True
167
include "sequential_parallel.pyx"