创建多处理数组时出现分段错误

时间:2018-12-13 08:35:07

标签: python numpy python-multiprocessing

我正在尝试在this post之后使用多处理来填充一个numpy数组。我在Mac上运行良好,但是当我将其移植到Ubuntu时,很多时候出现分段错误。

我已将代码简化为以下最小示例:

import numpy as np
from multiprocessing import sharedctypes

a = np.ctypeslib.as_ctypes(np.zeros((224,224,3)))
print("Made a, now making b")
b = sharedctypes.RawArray(a._type_, a)
print("Finished.")

在具有Python 3.6.5和numpy 1.15.4(与Mac相同的版本)的Ubuntu 16.04上,我得到了输出

Made a, now making b
Segmentation fault (core dumped)

现在,我可以稍微更改数组的尺寸,并且在某些情况下可以使用(例如,将第一个224更改为100即可使用)。但是大多数情况下它都是断层故障。

谁能提供任何见识?

我看到one post on a related topic from 2016 that no one responded toanother one involving pointers未使用。

PS-无论我将a指定为多维数组还是展平数组(例如np.zeros(224*224*3))似乎都没有什么区别。如果我更改数据类型(例如,将float转换为int),这似乎也没有什么不同;同样失败了。

进一步的更新:即使在original post中的代码中设置“ size = 224”,也会在具有不同numpy版本的两台不同的Ubuntu计算机上造成段错误,但在Mac上运行良好。

3 个答案:

答案 0 :(得分:4)

更多的是猜测,而不是答案,但是由于底层数据缓冲区的垃圾回收,您可能会遇到问题。这可能可以解释为什么您似乎要依赖要创建的数组的整体大小。

如果是这种情况,那么解决方法是将您创建的Numpy零数组分配给它自己的变量。这将确保通过创建RawArray来使缓冲区“存在”。代码如下:

zs = np.zeros((224,224,3))
a = np.ctypeslib.as_ctypes(zs)
print("Made a, now making b")
b = sharedctypes.RawArray(a._type_, a)
print("Finished.")

我现在只有一台Mac,所以我不能自己测试一下。

答案 1 :(得分:4)

其他分析和根本原因修复。

如上所述,这是垃圾回收错误的结果,这给了我如何修复的提示。

通过保留对原始np.zeros对象的引用,避免了该错误。 (对我来说)这意味着原始对象的集合破坏了结果数组。

查看as_ctypes的实现(摘自c52543e4a

def as_ctypes(obj):
    """Create and return a ctypes object from a numpy array.  Actually
    anything that exposes the __array_interface__ is accepted."""
    ai = obj.__array_interface__
    if ai["strides"]:
        raise TypeError("strided arrays not supported")
    if ai["version"] != 3:
        raise TypeError("only __array_interface__ version 3 supported")
    addr, readonly = ai["data"]
    if readonly:
        raise TypeError("readonly arrays unsupported")
    tp = _ctype_ndarray(_typecodes[ai["typestr"]], ai["shape"])
    result = tp.from_address(addr)
    result.__keep = ai
    return result

很明显,原始作者想到了这一点(指派.__keep来保持对原始对象的引用)。但是,似乎他们需要保留对原始对象的引用。

我写了a patch来做到这一点:

-        result.__keep = ai
+        result.__keep = obj

答案 2 :(得分:2)

最后的记录

让我进行后代测试,但是tel就是答案。

注意

以下测试结果适用于Debian。在Ubuntu(WSL)上进行测试确实要差得多。在Ubuntu n=193上,任何形状崩溃(以及如果我将3d n替换为1)以及上述任何n都会崩溃。看起来像(请参见下面的bla.py

  1. py bla.py n 1A上分配3204,对所有B分配29323 ob 0<n<193
  2. 对于n>=193,在B上发生了分段错误,并且在3208上分配了A。显然ubuntu中某处有一些硬盘限制。

Debian上的旧测试

经过一些测试,在我看来这是一个内存问题,它随着维度的内存分配出现了怪异的缩放。

只有2个尺寸的编辑对我来说不会崩溃,但3个会崩溃-我会以这种方式回答。

对我来说

b = sharedctypes.RawArray(a._type_, a)

在以下情况下不会崩溃:

a = np.ctypeslib.as_ctypes(np.zeros((224**3))) #Though generating b takes a while
a = np.ctypeslib.as_ctypes(np.zeros((100,100,100)))

因此似乎对内存的需求减少了,从而解决了这个问题,但是奇怪的是,在一维数组中相同数量的所需单元格可以正常工作-因此内存中更深层次的事情似乎还在继续。

当然,您正在使用指针。让我们尝试一些事情(bla.py):

import tracemalloc
import numpy as np
from sys import argv
from multiprocessing import sharedctypes

n,shape = (int (x) for x in argv[1:])
if shape == 1: shape = n
if shape == 2: shape = (n**2,n)
if shape == 3: shape = (n,n,n)

tracemalloc.start()
a = np.ctypeslib.as_ctypes(np.zeros(shape))
x=tracemalloc.take_snapshot().statistics('lineno')
print(len(x),sum((a.size for a in x)))
b = sharedctypes.RawArray(a._type_, a)
x=tracemalloc.take_snapshot().statistics('lineno')
print(len(x),sum((a.size for a in x)))

结果:

           n   shape    (a mallocs sum) (b mallocs sum)
>py bla.py 100 1     => 5 3478 76 30147
>py bla.py 100 2     => 5 5916 76 948313
>py bla.py 100 3     => 5 8200 76 43033
>py bla.py 150 1     => 5 3478 76 30195
>py bla.py 150 2     => 5 5916 76 2790461
>py bla.py 150 3     => 5 8200 76 45583
>py bla.py 159 1     => 5 3478 76 30195
>py bla.py 159 2     => 5 5916 76 2937854
>py bla.py 159 3     => 5 8200 76 46042
>py bla.py 160 1     => 5 3478 76 30195
>py bla.py 160 2     => 5 5916 72 2953989
>py bla.py 160 3     => 5 8200 Segmentation fault
>py bla.py 161 1     => 5 3478 76 30195
>py bla.py 161 2     => 5 5916 75 2971746
>py bla.py 161 3     => 5 8200 75 46116

>py bla.py 221 1     => 5 3478 76 30195
>py bla.py 221 2     => 5 5916 76 5759398
>py bla.py 221 3     => 5 8200 76 55348
>py bla.py 222 1     => 5 3478 76 30195
>py bla.py 222 2     => 5 5916 76 5782877
>py bla.py 222 3     => 5 8200 76 55399
>py bla.py 223 1     => 5 3478 76 30195
>py bla.py 223 2     => 5 5916 76 5806462
>py bla.py 223 3     => 5 8200 76 55450
>py bla.py 224 1     => 5 3478 76 30195
>py bla.py 224 2     => 5 5916 72 5829381
>py bla.py 224 3     => 5 8200 Segmentation fault
>py bla.py 225 1     => 5 3478 76 30195
>py bla.py 225 2     => 5 5916 76 5853950
>py bla.py 225 3     => 5 8200 76 55552

怪异的东西(n**2,n)在共享类型中分配了大量的分散,而n**3(n,n,n)却没有。但这不是重点。

  1. a的malloc保持一致,仅略微取决于维度,而根本不取决于n(对于测试的数字)。
  2. b的malloc除了形状2较高以外,还随n的增加而略有增加,但是随着形状的变化,它们的分布也大不相同。
  3. 分段错误循环出现!在我的机器上,形状(n,n,n)上的内存分配在发生故障之前接近于n个相关的数字-但对于n+1,我们再次确定。似乎在160左右约为46k,在224左右约为56k。

我没有很好的解释,但是对n的依赖使我认为分配需要很好地适应某些位结构,有时会中断。

我猜测使用225可以解决您的尺寸问题,这是一种解决方法。