Multithreading cython.parallel:如何初始化线程本地ndarray缓冲区?

Multithreading cython.parallel:如何初始化线程本地ndarray缓冲区?,multithreading,numpy,openmp,cython,Multithreading,Numpy,Openmp,Cython,我正在努力用cython.parallel初始化线程本地Ndarray: 伪代码: cdef: ndarray buffer with nogil, parallel(): buffer = np.empty(...) for i in prange(n): with gil: print "Thread %d: data address: 0x%x" % (threadid(), <uintptr_t>buffer

我正在努力用cython.parallel初始化线程本地Ndarray:

伪代码:

cdef:
    ndarray buffer

with nogil, parallel():
    buffer = np.empty(...)

    for i in prange(n):
        with gil:
            print "Thread %d: data address: 0x%x" % (threadid(), <uintptr_t>buffer.data)

        some_func(buffer.data)  # use thread-local buffer

cdef void some_func(char * buffer_ptr) nogil:
    (... works on buffer contents...)
cdef:
缓冲区
使用nogil,parallel():
buffer=np.empty(…)
对于prange中的i(n):
与gil合作:
打印“线程%d:数据地址:0x%x”%(threadid(),buffer.data)
一些_func(buffer.data)#使用线程本地缓冲区
cdef void some_func(char*buffer_ptr)nogil:
(…适用于缓冲区内容…)
我的问题是,在所有线程
缓冲区中,数据
指向相同的地址。即上次分配的
缓冲区的线程地址

尽管在
parallel()
(或者
prange
)块中分配了
buffer
,cython不会使
buffer
成为
private
或线程局部变量,而是将其作为
shared
变量保留

结果,
buffer.data
指向同一个内存区域,对我的算法造成了严重破坏

这不仅仅是ndarray对象的问题,似乎是所有
cdef类
定义的对象的问题


如何解决这个问题?

我想我终于找到了我喜欢的解决这个问题的方法。 简短的版本是创建具有以下形状的数组:

(线程数,…)
然后,调用openmp.omp_get_thread_num并使用该函数对数组进行索引,以获取“thread local”子数组。这避免了每个循环索引都有一个单独的数组(这可能是巨大的),但也防止了线程相互覆盖

以下是我所做工作的大致版本:

import numpy as np
import multiprocessing

from cython.parallel cimport parallel
from cython.parallel import prange
cimport openmp

cdef extern from "stdlib.h":
    void free(void* ptr)
    void* malloc(size_t size)
    void* realloc(void* ptr, size_t size)

...

cdef int num_items = ...
num_threads = multiprocessing.cpu_count()
result_array = np.zeros((num_threads, num_items), dtype=DTYPE) # Make sure each thread uses separate memory
cdef c_numpy.ndarray result_cn
cdef CDTYPE ** result_pointer_arr
result_pointer_arr = <CDTYPE **> malloc(num_threads * sizeof(CDTYPE *))
for i in range(num_threads):
    result_cn = result_array[i]
    result_pointer_arr[i] = <CDTYPE*> result_cn.data

cdef int thread_number
for i in prange(num_items, nogil=True, chunksize=1, num_threads=num_threads, schedule='static'):
    thread_number = openmp.omp_get_thread_num()
    some_function(result_pointer_arr[thread_number])
将numpy导入为np
导入多处理
从cython.parallel cimport parallel
来自cython.平行进口prange
cimport openmp
来自“stdlib.h”的cdef外部:
无空隙(空隙*ptr)
void*malloc(大小)
void*realloc(void*ptr,大小)
...
cdef int num_项=。。。
num\u threads=多处理.cpu\u计数()
result_array=np.zeros((num_线程,num_项),dtype=dtype)#确保每个线程使用单独的内存
cdef c_numpy.ndarray结果\u cn
cdef CDTYPE**结果\u指针\u arr
结果\u指针\u arr=malloc(num\u threads*sizeof(CDTYPE*))
对于范围内的i(num_线程):
结果\u cn=结果\u数组[i]
结果\u指针\u arr[i]=结果\u cn.data
cdef int螺纹号
对于prange中的i(num_items,nogil=True,chunksize=1,num_threads=num_threads,schedule='static'):
thread\u number=openmp.omp\u get\u thread\u num()
某些函数(结果\u指针\u arr[线程\u编号])

你能在没有gil的情况下调用
np.empty
吗?也许会带来你想要的…@BiRico这是一个反问句:)?不,您绝对不能在
nogil
块中实例化numpy数组(或memoryview)(否则数组将不会在Python的托管内存中分配,也无法进行垃圾收集等)。据我所知,Cython不允许线程私有numpy数组或MemoryView(如果这种情况已经改变,那么我很想听听!)。正如Saullo所建议的,您最好的选择可能是在并行块之外分配一个大数组,然后向每个工作线程传递一个指向要使用的数组段的指针?如果您使用内存视图,它甚至不允许您编译。“Memoryview切片只能在并行分区中共享”