我正在尝试在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 to和another one involving pointers未使用。
PS-无论我将a
指定为多维数组还是展平数组(例如np.zeros(224*224*3)
)似乎都没有什么区别。如果我更改数据类型(例如,将float转换为int),这似乎也没有什么不同;同样失败了。
进一步的更新:即使在original post中的代码中设置“ size = 224”,也会在具有不同numpy版本的两台不同的Ubuntu计算机上造成段错误,但在Mac上运行良好。
答案 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
)
py bla.py n 1
在A
上分配3204,对所有B
分配29323 ob 0<n<193
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)
却没有。但这不是重点。
a
的malloc保持一致,仅略微取决于维度,而根本不取决于n
(对于测试的数字)。b
的malloc除了形状2较高以外,还随n
的增加而略有增加,但是随着形状的变化,它们的分布也大不相同。(n,n,n)
上的内存分配在发生故障之前接近于n
个相关的数字-但对于n+1
,我们再次确定。似乎在160左右约为46k,在224左右约为56k。我没有很好的解释,但是对n
的依赖使我认为分配需要很好地适应某些位结构,有时会中断。
我猜测使用225
可以解决您的尺寸问题,这是一种解决方法。