Обычный шаблон состоит в том, что каждый поток получает свой собственный контейнер, который является компромиссом между скоростью / сложностью и накладными расходами памяти:
- нет необходимости блокировать доступ к этому контейнерупотому что только один поток обращается к нему.
- намного меньше накладных расходов по сравнению с "собственным контейнером для каждой задачи (т. Е. Каждое
i
-значение)".
После параллельной секции данные должны быть собраны либов конечном контейнере на этапе постобработки (что также может происходить параллельно) или в последующих алгоритмах должна быть возможность обрабатывать коллекцию контейнеров.
Вот пример использования вектора c ++ - (который уже имеет памятьвстроенное управление и увеличение размера):
%%cython -+ -c=/openmp --link-args=/openmp
from cython.parallel import prange, threadid
from libcpp.vector cimport vector
cimport openmp
def calc_in_parallel(N):
cdef int i,k,tid
cdef int n = N
cdef vector[vector[int]] vecs
# every thread gets its own container
vecs.resize(openmp.omp_get_max_threads())
for i in prange(n, nogil=True):
tid = threadid()
for k in range(i):
# use container of the thread
vecs[tid].push_back(k) # dummy for calculation
return vecs
Использование omp_get_max_threads()
для количества потоков во многих случаях переоценивает реальное количество потоков. Вероятно, более надежно установить число потоков явно в prange
, т. Е.
...
NUM_THREADS = 2
vecs.resize(NUM_THREADS)
for i in prange(n, nogil=True, num_threads = NUM_THREADS):
...
Аналогичный подход может быть применен с использованием чистого C, но потребуется больший код платформы (управление памятью) вэто дело.