我尝试在cython中通过python对象数组(最终将传递给同一个函数)实现bsearch函数。迄今为止的准则是:
# cython: language_level=3
from cpython cimport array as arr
cimport cython
import array
from libc.stdlib cimport bsearch
cdef int CustCmp( const void *a, const void *b ) with gil:
cdef int a_v = (< int*>a)[0]
cdef int b_v = (< int*>b)[0]
if a_v < b_v: return -1
elif b_v < a_v: return 1
else: return 0
def Indexer():
cdef arr.array a = arr.array('I',(3,3,4,7,7,7,7,7,8,9))
cdef int *pa = < int*>a
cdef int x = 7
cdef int *p = < int*>bsearch( &x, pa, 10, sizeof( int ), &CustCmp )
if ( p != NULL ):
print( "{0}".format(p-pa) )
return p-pa
else:
return -1
但是,我从cdef int *pa = < int*>a
得到了“Python对象不能转换为基元类型的指针”。如何使bsearch与python对象一起工作?在
您正在将对象强制转换为指针类型。这只是给您一个地址,如the Cython docs中所述:
int*
也是一个指针类型,因此实际上并没有将Pythonarray
对象转换为真正的C数组。相反,您正在(尝试)将其转换为指向实际指向Python对象的无效int指针。Cython认识到这是非法的并阻止了它(这比C要慷慨得多,后者只允许强制转换,然后可能在运行时崩溃)。在正确的方法是使用类型化内存视图,正如文档在Pass data from a C function via pointer中详细描述的那样。但是TL;DR应该写下这样的东西:
请注意,可以省略文档中显示的
if not pa.flags['C_CONTIGUOUS']: ...
代码,因为:最后,你可能不需要你的比较器函数
with gil
,因为我看不到它做任何需要GIL的事情。在Kevin的解决方案是安全的,在更大的项目中应该是默认的,在这个项目中,您只是不知道您的函数将如何使用-因此,知道底层缓冲区是锁定的并且不能从另一个线程添加任何元素-这意味着我们传递给C例程的指针不会失效。在
如果函数具有带类型化内存视图的签名,甚至可以将其用于
array.array
和其他缓冲区,如numpy数组,例如:这个答案试图回答这样一个问题:与使用
^{pr2}$array.array
的不安全解决方案相比,类型化内存视图的开销是多少。为此,我们考虑以下示例,它更简单,但计时与原始函数相似:现在:
所以基本上安全功能要慢4倍,很明显,在功能中做的工作越多,它就越少。在
另一个有趣的观察结果是:documentation将}称为{},但差别并不大(甚至不清楚是否存在!)与不安全的使用相比。在
direct_memory
称为无开销,而将{如果我们将numpy数组传递给函数,则差别更大:
numpy数组的开销是}似乎是轻量级任务的更好选择。在
array.array
的两倍,因此{我的结论是:使用不安全的版本可能是值得的,但是我只会在确定类型化内存视图的开销确实是瓶颈并且只有一个线程的情况下才会这么做。在
相关问题 更多 >
编程相关推荐