为什么 NumPy-C api 不警告我分配失败?
Why doesn't the NumPy-C api warn me about failed allocations?
我一直在写一个 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
创建连续的数组,所以这无论如何都不是必需的。
我应该检查一些错误标志吗?我以后怎么能detect/prevent这种情况呢? 把BUFFER_LENGTH
设置成一个较小的值显然是可行的,但是这个值是在运行时确定的,我想知道确切的范围。
编辑:
正如@DavidW 指出的那样,错误源于将 buffer_len_alt
转换为 int
,因为 npy_intp
可以是 64 位数字。将转换为 int
的转换替换为转换为 'unsigned long' 可以解决我的问题。
问题(在评论中诊断)实际上是数组查找而不是数组分配。您的代码包含行
output_buffer[((int) buffer_len_alt) - 1] = 'x'
当 buffer_len_alt
(大约值 3000000000)被转换为(32 位)int(最大值 2147483647)时,您得到了一个无效地址,可能是一个很大的负数。
解决方法就是使用
output_buffer[buffer_len_alt - 1] = 'x'
(也就是说,我根本不明白你为什么需要演员表)。
我一直在写一个 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
创建连续的数组,所以这无论如何都不是必需的。
我应该检查一些错误标志吗?我以后怎么能detect/prevent这种情况呢? 把BUFFER_LENGTH
设置成一个较小的值显然是可行的,但是这个值是在运行时确定的,我想知道确切的范围。
编辑:
正如@DavidW 指出的那样,错误源于将 buffer_len_alt
转换为 int
,因为 npy_intp
可以是 64 位数字。将转换为 int
的转换替换为转换为 'unsigned long' 可以解决我的问题。
问题(在评论中诊断)实际上是数组查找而不是数组分配。您的代码包含行
output_buffer[((int) buffer_len_alt) - 1] = 'x'
当 buffer_len_alt
(大约值 3000000000)被转换为(32 位)int(最大值 2147483647)时,您得到了一个无效地址,可能是一个很大的负数。
解决方法就是使用
output_buffer[buffer_len_alt - 1] = 'x'
(也就是说,我根本不明白你为什么需要演员表)。