2013-03-04 58 views
8

我最近试过PyPy,并被这种方法所吸引。我有很多用于Python的C扩展,它们都使用PyArray_DATA()来获取指向numpy数组的数据部分的指针。不幸的是,PyPy似乎并没有在其cpyext模块中为其numpypy阵列等效导出,所以我尝试按照其网站上的建议使用​​。这将推动获取指向Python级别的任务。便携式/快速获取指向Numpy/Numpypy数据的指针

似乎有两种方式:

import ctypes as C 
p_t = C.POINTER(C.c_double) 

def get_ptr_ctypes(x): 
    return x.ctypes.data_as(p_t) 

def get_ptr_array(x): 
    return C.cast(x.__array_interface__['data'][0], p_t) 

只有第二个工作在PyPy,所以兼容性的选择是明确的。对于CPython来说,两者都很慢,对我的应用程序来说是一个完整的瓶颈!有没有一种快速便携的方式来获得这个指针?或者PyPy(可能没有文档)有相当于PyArray_DATA()

回答

4

我还没有找到一个完全令人满意的解决办法,但仍然有一些东西我们可以通过在CPython中获得更少的开销来获得指针。首先,上述两种方式如此之慢的原因是.ctypes.__array_interface__都是按需属性,其由array_ctypes_get()array_interface_get()numpy/numpy/core/src/multiarray/getset.c中设置。第一个导入ctypes并创建一个numpy.core._internal._ctypes实例,而第二个创建一个新的字典,并在数据指针之外添加大量不必要的东西。

没有什么人能在这个开销Python层面做,但可以写在C级的微型模块绕过大部分的开销:

#include <Python.h> 
#include <numpy/arrayobject.h> 

PyObject *_get_ptr(PyObject *self, PyObject *obj) { 
    return PyLong_FromVoidPtr(PyArray_DATA(obj)); 
} 

static PyMethodDef methods[] = { 
    {"_get_ptr", _get_ptr, METH_O, "Wrapper to PyArray_DATA()"}, 
    {NULL, NULL, 0, NULL} 
}; 

PyMODINIT_FUNC initaccel(void) { 
    Py_InitModule("accel", methods); 
} 

正常编译就可以作为扩展在setup.py,进口为

try: 
    from accel import _get_ptr 
    def get_ptr(x): 
     return C.cast(_get_ptr(x), p_t) 
except ImportError: 
    get_ptr = get_ptr_array 

在PyPy,from accel import _get_ptr将失败,get_ptr将回落至get_ptr_array,这与Numpypy工作。

就性能而言,对于轻量级的C函数调用,ctypes + accel._get_ptr()仍然比本地CPython扩展慢很多,后者基本没有开销。它当然比上面的get_ptr_ctypes()get_ptr_array()快得多,因此对于中等重量的C函数调用来说开销可能变得不重要。

一个已经获得了兼容PyPy,但我不得不说,花费相当多的时间试图评估PyPy我的科学计算应用程序后,我没有看到它的未来,只要他们(相当固执地)拒绝支持完整的CPython API。

更新

我发现,现在ctypes.cast()变得引进accel._get_ptr()后的瓶颈。通过将接口中的所有指针声明为ctypes.c_void_p,可以摆脱这些强制转换。这是我结束了:

def get_ptr_ctypes2(x): 
    return x.ctypes._data 

def get_ptr_array(x): 
    return x.__array_interface__['data'][0] 

try: 
    from accel import _get_ptr as get_ptr 
except ImportError: 
    get_ptr = get_ptr_array 

这里,get_ptr_ctypes2()通过直接访问隐藏ndarray.ctypes._data属性避免了演员。下面是用于调用重的重量和重量轻的C函数在Python一些时序结果:

       heavy C (few calls)  light C (many calls) 
ctypes + get_ptr_ctypes():   0.71 s     15.40 s 
ctypes + get_ptr_ctypes2():  0.68 s     13.30 s 
ctypes + get_ptr_array():   0.65 s     11.50 s 
ctypes + accel._get_ptr():   0.63 s     9.47 s 

native CPython:     0.62 s     8.54 s 
Cython (no decorators):   0.64 s     9.96 s 

所以,用accel._get_ptr()和无ctypes.cast() S,ctypes的是速度与天然CPython的延伸实际上具有竞争力。所以,我只是要等到有人改写h5pymatplotlibscipy与ctypes的是能够尝试PyPy对于事情的严重性...

0

这可能没有足够的答案,但希望有一个很好的提示。我在我的代码的某些部分使用scipy.weave.inline()。我不太了解接口本身的速度,因为我执行的函数很重,只依赖于几个指针/数组,但对我来说似乎很快。也许你可以从scipy.weave代码一些启发,特别是attempt_function_call

https://github.com/scipy/scipy/blob/master/scipy/weave/inline_tools.py#L390

如果你想看看由scipy.weave生成的C++代码,

  1. 产生从这里一个简单的例子:http://docs.scipy.org/doc/scipy/reference/tutorial/weave.html

  2. 运行的python脚本

  3. 得到scipy.weave缓存文件夹:

    import scipy.weave.catalog as ctl 
    ctl.default_dir() 
    Out[5]: '/home/user/.python27_compiled' 
    
  4. 有文件夹中看看生成的C++代码
+0

不幸的是,'scipy.weave'确实没有什么比产生使用CPython的API的C代码其他( '#include '),这不会与'PyPy'一起使用。在CPython API中,'PyArray_DATA()'是获得指向numpy数组数据部分的指针的最有效方法,但它不可移植到PyPy。 – Stefan 2013-03-28 10:19:32