给出一个ctypes指针,例如double**
:
import ctypes
data=(ctypes.POINTER(ctypes.c_double)*4)() # results in [NULL, NULL, NULL, NULL]
是否可以获得描述data
的内存布局的format string?
现在,我创建一个memoryview来获取此信息,这听起来有些愚蠢:
view=memoryview(data)
print(view.format) # prints: &<d
是否有更直接的方式,开销更少?也许通过使用C-API?
如果有帮助的话,可以用有意义的值填充data
:
import ctypes
data=(ctypes.POINTER(ctypes.c_double)*2)(
(ctypes.c_double*2)(1.0,2.0),
(ctypes.c_double*1)(3.0))
# results in [
# ptr0 -> [1,2],
# ptr1 -> [3]
# ]
print(data[1][0]) # prints 3.0
答案 0 :(得分:0)
似乎没有什么比memoryview(data).format
更好的了。但是,可以使用C-API加快这一步。
格式字符串(如in PEP3118所述扩展了结构格式字符串语法)是递归计算的,并存储在format
-member of the StgDictObject
-object中,可以在tp_dict
中找到-现场
ctypes-arrays / pointers:
typedef struct {
PyDictObject dict; /* first part identical to PyDictObject */
...
/* pep3118 fields, pointers neeed PyMem_Free */
char *format;
int ndim;
Py_ssize_t *shape;
...
} StgDictObject;
仅在递归计算期间和a buffer is exported时访问此format
字段-这就是memoryview
获取此信息的方式:
static int PyCData_NewGetBuffer(PyObject *myself, Py_buffer *view, int flags)
{
...
/* use default format character if not set */
view->format = dict->format ? dict->format : "B";
...
return 0;
}
现在,我们可以使用C-API填充缓冲区(无需创建实际的memoryview
),此处使用Python实现:
%%cython
from cpython cimport buffer
def get_format_via_buffer(obj):
cdef buffer.Py_buffer view
buffer.PyObject_GetBuffer(obj, &view, buffer.PyBUF_FORMAT|buffer.PyBUF_ANY_CONTIGUOUS)
cdef bytes format = view.format
buffer.PyBuffer_Release(&view)
return format
此版本比通过memoryview
快3倍:
import ctypes
c=(ctypes.c_int*3)()
%timeit get_format_via_buffer(c) # 295 ns ± 10.3
%timeit memoryview(c).format # 936 ns ± 7.43 ns
在我的机器上,调用def
函数大约需要160 ns的开销,而创建字节对象大约需要50
毫秒。
即使由于不可避免的开销而对它进行进一步优化没有太大意义,但至少在理论上仍对如何提高它有兴趣。
如果一个人真的也想节省填写Py_buffer-struct的成本,那么没有干净的方法:ctypes-module不是Python-C-API的一部分(它不在include-目录中) ),因此前进的方式是重复the solution Cython uses with the array.array
,即对对象的内存布局进行硬编码(这会使该解决方案变得脆弱,因为the memory-layout of StgDictObject
可能会不同步)。
在这里使用Cython,并且没有错误检查:
%%cython -a
from cpython cimport PyObject
# emulate memory-layout (i.e. copy definitions from ctypes.h)
cdef extern from *:
"""
#include <Python.h>
typedef struct _ffi_type
{
size_t size;
unsigned short mem[2];
struct _ffi_type **elements;
} ffi_type;
typedef struct {
PyDictObject dict; /* first part identical to PyDictObject */
Py_ssize_t size[3]; /* number of bytes,alignment requirements,number of fields */
ffi_type ffi_type_pointer;
PyObject *proto; /* Only for Pointer/ArrayObject */
void *setfunc[3];
/* Following fields only used by PyCFuncPtrType_Type instances */
PyObject *argtypes[4];
int flags; /* calling convention and such */
/* pep3118 fields, pointers neeed PyMem_Free */
char *format;
int ndim;
} StgDictObject;
"""
ctypedef struct StgDictObject:
char *format
def get_format_via_hack(obj):
cdef PyObject *p =<PyObject *>obj
cdef StgDictObject *dict = <StgDictObject *>(p.ob_type.tp_dict)
return dict.format
而且速度很快:
%timeit get_format_via_hack(c) # 243 ns ± 14.5 ns