【发布时间】:2016-09-08 08:47:19
【问题描述】:
我一直在编写一个 Python 扩展,它从 C 中写入一个 NumPy 数组。在测试期间,我注意到某些非常大的数组在我尝试访问它们的一些元素时会生成一个 segfault。
具体来说,以下代码段的最后一行以segfault 失败:
// Size of buffer we will write to
npy_intp buffer_len_alt = BUFFER_LENGTH;
//
PyArray_Descr * dtype;
dtype = PyArray_DescrFromType(NPY_BYTE);
PyObject* column = PyArray_Zeros(1, &buffer_len_alt, dtype, 0);
//Check that array creation succeeds
if (column == NULL){
// This exit point is not reached, so it looks like everything is OK
return (PyObject *) NULL;
}
// Get the array's internal buffer so we can write to it
output_buffer = PyArray_BYTES((PyArrayObject *)column);
// Try writing to the buffer
output_buffer[0] = 'x'; //No segfault
output_buffer[((int) buffer_len_alt) - 1] = 'x'; // Segfault here
我检查发现只有当我尝试分配一个大约 3GB 的数组时才会出现错误(即BUFFER_LENGTH 大约是 3*2^30)。这是not surprising,即使 Python 使用它的自定义分配器,这种大小的分配也会失败。我真正担心的是NumPy 没有引发错误或以其他方式表明数组创建没有按计划进行。
我已经尝试在返回的数组上检查PyArray_ISCONTIGUOUS,并使用PyArray_GETCONTIGUOUS 确保它是单个内存段,但segfault 仍然会出现。 NPY_ARRAY_DEFAULT 创建了连续的数组,所以无论如何都不需要这样做。
我应该检查一些错误标志吗?以后如何检测/预防这种情况? 将BUFFER_LENGTH 设置为较小的值显然有效,但该值是在运行时确定的,我想知道确切的界限。
编辑:
正如@DavidW 所指出的,错误源于将buffer_len_alt 转换为int,因为npy_intp 可以是64 位数字。将转换为 int 的转换为 'unsigned long' 可以解决我的问题。
【问题讨论】:
-
这是否与转换为 int (
((int) buffer_len_alt) - 1) 的转换有关,它可能已签名,但无法保持BUFFER_LENGTH? (假设 32 位整数) -
@DavidW 你应该这样回答。
buffer_len_alt的类型为npy_intp,在大多数平台上是一个 64 位有符号整数。如果平台上的int类型为32位,那么buffer_len_alt为3*(2**30)时(int) buffer_len_alt为负整数。 -
@DavidW 感谢您的建议!我能够确认这确实是问题所在。如果您发布答案,我一定会将其标记为已接受。
标签: python c numpy python-c-api