创建多处理数组时出现分段错误
Segmentation fault when creating multiprocessing array
我正在尝试使用多处理填充一个 numpy 数组,紧随 this post。我在 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.")
在 Ubuntu 16.04 上,使用 Python 3.6.5 和 numpy 1.15.4(与我的 Mac 上的版本相同),我得到了输出
Made a, now making b
Segmentation fault (core dumped)
现在,我可以稍微更改数组维度,在某些情况下它会起作用(例如,将前 224 更改为 100 并且它会起作用)。但主要是它会出现故障。
任何人都可以提供任何见解吗?
我看到 one post on a related topic from 2016 that no one responded to, and another one involving pointers 我没有使用。
PS- 将 a
指定为多维数组还是扁平化数组(例如 np.zeros(224*224*3)
).如果我更改数据类型(例如 float 到 int),它似乎也没有什么区别;它同样失败。
进一步更新: 即使在 original post 的代码中设置 "size=224" 也会导致两台不同的 Ubuntu 机器出现段错误numpy 的版本,但在 Mac 上运行良好。
最后的笔记
将我的测试留给后代,但 有答案。
备注
以下是在 Debian 上的测试结果。在 Ubuntu (WSL) 上测试确实差很多。在 Ubuntu n=193
上,任何形状都会崩溃(如果我将 3d n
替换为 1),以及上面的任何 n
。看起来像(见下文 bla.py
):
py bla.py n 1
在 A
上分配 3204,为 al 0<n<193
分配 29323 ob B
- 对于
n>=193
,在 B
上发生分段错误,并且在 A
上分配了 3208
。显然 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
mallocs 是一致的,仅略微依赖于维度,完全不依赖于 n
(对于测试的数字)。
b
mallocs 除了在形状 2 上很高之外,还随 n
略有增加,但随着形状的变化它们变化很大。
- 段错误是循环出现的!我的机器上形状
(n,n,n)
的内存分配在 sefault 之前接近一些 n
相关数字 - 但对于 n+1
我们再次确定。似乎是 ~46k 大约 160 和 ~56k 大约 224。
我没有很好的解释,但对 n
的依赖让我认为分配需要很好地适应某些位结构,有时会中断。
我猜测使用 225
作为您的尺寸会起作用 - 作为一种解决方法。
这与其说是答案,不如说是猜测,但您可能 运行 由于底层数据缓冲区的垃圾收集而遇到问题。这可以解释为什么似乎依赖于您尝试创建的数组的整体大小。
如果是这种情况,则解决方法是将您创建的 Numpy 零数组分配给它自己的变量。这将确保缓冲区 "lives" 通过创建 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,所以我不能自己测试。
其他分析和根本原因修复。
如上所述,这是垃圾收集错误的结果,这为我提供了修复方法的提示。
通过保持对原始 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
我正在尝试使用多处理填充一个 numpy 数组,紧随 this post。我在 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.")
在 Ubuntu 16.04 上,使用 Python 3.6.5 和 numpy 1.15.4(与我的 Mac 上的版本相同),我得到了输出
Made a, now making b
Segmentation fault (core dumped)
现在,我可以稍微更改数组维度,在某些情况下它会起作用(例如,将前 224 更改为 100 并且它会起作用)。但主要是它会出现故障。
任何人都可以提供任何见解吗?
我看到 one post on a related topic from 2016 that no one responded to, and another one involving pointers 我没有使用。
PS- 将 a
指定为多维数组还是扁平化数组(例如 np.zeros(224*224*3)
).如果我更改数据类型(例如 float 到 int),它似乎也没有什么区别;它同样失败。
进一步更新: 即使在 original post 的代码中设置 "size=224" 也会导致两台不同的 Ubuntu 机器出现段错误numpy 的版本,但在 Mac 上运行良好。
最后的笔记
将我的测试留给后代,但
备注
以下是在 Debian 上的测试结果。在 Ubuntu (WSL) 上测试确实差很多。在 Ubuntu n=193
上,任何形状都会崩溃(如果我将 3d n
替换为 1),以及上面的任何 n
。看起来像(见下文 bla.py
):
py bla.py n 1
在A
上分配 3204,为 al0<n<193
分配 29323 ob - 对于
n>=193
,在B
上发生分段错误,并且在A
上分配了3208
。显然ubuntu
. 某处存在一些硬内存限制
B
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
mallocs 是一致的,仅略微依赖于维度,完全不依赖于n
(对于测试的数字)。b
mallocs 除了在形状 2 上很高之外,还随n
略有增加,但随着形状的变化它们变化很大。- 段错误是循环出现的!我的机器上形状
(n,n,n)
的内存分配在 sefault 之前接近一些n
相关数字 - 但对于n+1
我们再次确定。似乎是 ~46k 大约 160 和 ~56k 大约 224。
我没有很好的解释,但对 n
的依赖让我认为分配需要很好地适应某些位结构,有时会中断。
我猜测使用 225
作为您的尺寸会起作用 - 作为一种解决方法。
这与其说是答案,不如说是猜测,但您可能 运行 由于底层数据缓冲区的垃圾收集而遇到问题。这可以解释为什么似乎依赖于您尝试创建的数组的整体大小。
如果是这种情况,则解决方法是将您创建的 Numpy 零数组分配给它自己的变量。这将确保缓冲区 "lives" 通过创建 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,所以我不能自己测试。
其他分析和根本原因修复。
如上所述,这是垃圾收集错误的结果,这为我提供了修复方法的提示。
通过保持对原始 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