【问题标题】:Python hangs silently on large file writePython 在大文件写入时静默挂起
【发布时间】:2019-05-29 03:52:19
【问题描述】:

我正在尝试将大量 numpy nd_arrays 写入磁盘。

列表长约 50000 个元素

每个元素都是一个大小为 (~2048,2) 的整数的 nd_array。数组有不同的形状。

我(目前)使用的方法是

@staticmethod
def _write_with_yaml(path, obj):
    with io.open(path, 'w+', encoding='utf8') as outfile:
        yaml.dump(obj, outfile, default_flow_style=False, allow_unicode=True)

我也试过泡菜,它也给出了同样的问题

在小型列表(约 3400 长)上,这工作正常,完成速度足够快(

在大约 6000 个长列表中,大约 2 分钟后完成。

当列表变大时,该过程似乎没有做任何事情。 RAM 或磁盘活动没有变化。

30 分钟后我停止等待。

在强制停止进程后,文件突然变得非常大(~600MB)。 不知道写完没。

写这么大的列表的正确方法是什么,知道他是否写成功,如果可能的话,知道写/读什么时候完成?

如何调试进程似乎挂起时发生的情况?

我不想在我的代码中手动分解和组装列表,我希望序列化库能够为我做到这一点。

【问题讨论】:

    标签: python file serialization yaml pickle


    【解决方案1】:

    代码

    import numpy as np
    import yaml
    
    x = []
    for i in range(0,50000):
        x.append(np.random.rand(2048,2))
    print("Arrays generated")
    with open("t.yaml", 'w+', encoding='utf8') as outfile:
        yaml.dump(x, outfile, default_flow_style=False, allow_unicode=True)
    

    在我的系统(MacOSX、i7、16 GiB RAM、SSD)上使用 Python 3.7 和 PyYAML 3.13,完成时间为 61 分钟。在保存过程中,python 进程占用了大约 5 GB 的内存,最终文件大小为 2 GB。这也显示了文件格式的开销:由于数据大小为 50k * 2048 * 2 * 8(在 python 中浮点的大小通常为 64 位)= 1562 MBytes,这意味着 yaml 大约差 1.3 倍(并且序列化/反序列化也需要时间)。

    回答您的问题:

    1. 没有正确或不正确的方法。有一个进度更新和 估计完成时间并不容易(例如:其他任务可能 干扰估计,可以使用内存等资源 向上等)。您可以依赖支持或实现的库 自己的东西(正如其他答案所建议的那样)
    2. 不确定“调试”是正确的术语,因为在实践中它可能只是进程缓慢。进行性能分析并不容易,尤其是如果 使用多个/不同的库。我要开始的很清楚 要求:你想从保存的文件中得到什么?他们是否需要 是yaml?将 50k 数组保存为 yaml 似乎不是最好的解决方案 如果你关心性能。您是否应该首先问自己“哪种格式最适合我想要的东西?” (但你没有提供细节所以不能说......)

    编辑:如果您想要快速的东西,请使用泡菜。代码:

    import numpy as np
    import yaml
    import pickle
    
    x = []
    for i in range(0,50000):
        x.append(np.random.rand(2048,2))
    print("Arrays generated")
    pickle.dump( x, open( "t.yaml", "wb" ) )
    

    在 9 秒内完成,并生成一个 1.5GBytes 的文件(无开销)。当然,pickle 格式应该在与 yaml 非常不同的情况下使用......

    【讨论】:

    • numpy.save()scipy.io.savemat() 在这里也可能有用......虽然它们不会保留 listndarray 的区别,但否则应该非常快速和简单
    【解决方案2】:

    我不能说这就是答案,但可能就是这样。

    当我在开发需要快速循环的应用程序时,我发现代码中的某些内容非常慢。它正在打开/关闭 yaml 文件。

    使用 JSON 解决了​​。

    不要将 YAML 用作您不经常打开的某种配置。

    阵列保存的解决方案:

    np.save(path,array) # path = path+name+'.npy'
    

    如果您确实需要保存数组列表,我建议您使用数组路径保存列表(数组本身您将使用 np.save 保存在磁盘上)。在磁盘上保存 python 对象并不是你真正想要的。你想要的是用 np.save 保存 numpy 数组

    完整的解决方案(保存示例):

    for array_index in range(len(list_of_arrays)):
        np.save(array_index+'.npy',list_of_arrays[array_index])
        # path = array_index+'.npy'
    

    完整解决方案(加载示例):

    list_of_array_paths = ['1.npy','2.npy']
    list_of_arrays = []
    for array_path in list_of_array_paths:
        list_of_arrays.append(np.load(array_path))
    

    进一步的建议:

    Python 无法真正处理大型数组。此外,如果您在列表中加载了其中的几个。从速度和内存的角度来看,始终一次使用一两个数组。其余的必须在磁盘上等待。因此,不是对象引用,而是将引用作为路径,并在需要时从磁盘加载它。

    另外,您说您不想手动组装列表。

    可能的解决方案,我不建议这样做,但可能正是您正在寻找的解决方案

    >>> a = np.zeros(shape = [10,5,3])
    >>> b = np.zeros(shape = [7,7,9])
    >>> c = [a,b]
    >>> np.save('data.npy',c)
    >>> d = np.load('data.npy')
    >>> d.shape
    (2,)
    >>> type(d)
    <type 'numpy.ndarray'>
    >>> d.shape
    (2,)
    >>> d[0].shape
    (10, 5, 3)
    >>> 
    

    我相信我不需要评论上面提到的代码。但是,加载回来后,您将丢失列表,因为列表将被转换为 numpy 数组。

    【讨论】:

    • 谢谢!您的回答是正确的,并且可能是最佳实践,但我接受了 vladmihasima 的,因为它更适合我的需求,并且也不会丢失数据的列表结构,因此不需要我方面的特殊处理。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-10-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-22
    • 1970-01-01
    • 2016-12-06
    相关资源
    最近更新 更多