python-3.x - 如何获取 ctypes-pointer 数据的格式字符串
问题描述
给定一个 ctypes 指针,例如double**
:
import ctypes
data=(ctypes.POINTER(ctypes.c_double)*4)() # results in [NULL, NULL, NULL, NULL]
是否可以获得描述内存布局的格式字符串data
?
现在,我创建了一个 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
解决方案
似乎没有什么从根本上比memoryview(data).format
. 但是,可以通过使用 C-API 来加快这一进程。
格式字符串(扩展了结构格式字符串语法,如PEP3118 中所述)是递归计算的,并存储在-objectformat
的StgDictObject
-member中,可以在tp_dict
ctypes-arrays/pointers 的 -field 中找到:
typedef struct {
PyDictObject dict; /* first part identical to PyDictObject */
...
/* pep3118 fields, pointers neeed PyMem_Free */
char *format;
int ndim;
Py_ssize_t *shape;
...
} StgDictObject;
此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
这个版本比 via 快大约 3 倍memoryview
:
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
-function大约需要 160 ns 开销,50
创建字节对象大约需要 ms。
即使由于不可避免的开销而进一步优化它没有多大意义,但至少在理论上仍然存在如何加速它的兴趣。
如果一个人真的想削减填充 Py_buffer-struct 的成本,那么没有干净的方法:ctypes-module 不是 Python-C-API 的一部分(它不在包含目录中),所以前进的方法是重复Cython 使用的解决方案array.array
,即硬编码对象的内存布局(这使得该解决方案变得脆弱,因为内存布局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
推荐阅读
- windows - 在桌面上运行 MPI 程序?
- amazon-web-services - 使用 go aws sdk 从 S3 下载 460mb 文件到 AWS lambda 时内存不足
- google-cloud-platform - 有没有办法在不轮询 REST API 的情况下通知 Google AI Platform 训练作业的状态变化?
- ansible - 如何在 Ansible 中检查文件是否存在于带有扩展名的路径中?
- python - 用作神经网络输入的熊猫数据框?
- c# - 如何将 Node.js 中 fs 读取的文件转换为 C# byte[] 类型?
- docker - 有没有找到 Docker 镜像的基本镜像的命令?
- windows - Windows 上的 Devilbox 自动 DNS
- php - Symfony、继承实体和学说迁移
- windows - 如何修复“错误 1001。需要绝对路径信息。”?