numpy数组最快的保存和加载选项

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了numpy数组最快的保存和加载选项相关的知识,希望对你有一定的参考价值。

我有一个脚本生成二维numpy arrays与dtype=float和形状的(1e3, 1e6)。现在我正在使用np.savenp.load来对阵列执行IO操作。但是,每个阵列的这些功能需要几秒钟。是否有更快的方法来保存和加载整个数组(即,不对其内容进行假设并减少它们)?只要数据保留完全,我就可以在保存前将arrays转换为另一种类型。

答案

对于非常大的数组,我听说过几个解决方案,而且他们主要是在I / O上懒惰:

  • NumPy.memmap,将大数组映射为二进制形式 优点: 没有Numpy以外的依赖 透明替换ndarray(接受ndarray的任何类接受memmap) 缺点: 阵列的块数限制为2.5G 仍然受到Numpy吞吐量的限制
  • 使用Python绑定HDF5,这是一种支持大数据的文件格式,如PyTablesh5py 优点: 格式支持压缩,索引和其他超级好的功能 显然是最终的PetaByte大文件格式 缺点: 具有分层格式的学习曲线? 必须定义您的性能需求(见后文)
  • Python's pickling系统(在竞赛中,提到Pythonicity而不是速度) 优点: 这是Pythonic! (哈哈) 支持各种对象 缺点: 可能比其他人慢(因为针对任何对象而不是数组)

Numpy.memmap

来自NumPy.memmap的文档:

为存储在磁盘上的二进制文件中的数组创建内存映射。

内存映射文件用于访问磁盘上的大段文件,而无需将整个文件读入内存

memmap对象可以在接受ndarray的任何地方使用。鉴于任何memmap fpisinstance(fp, numpy.ndarray)返回True。


HDF5 arrays

来自h5py doc

允许您存储大量数值数据,并轻松操作NumPy中的数据。例如,您可以切片存储在磁盘上的多TB数据集,就好像它们是真正的NumPy数组一样。成千上万的数据集可以存储在一个文件中,无论您需要如何分类和标记。

该格式支持以各种方式压缩数据(为相同的I / O读取加载更多位),但这意味着数据变得不那么容易单独查询,但在您的情况下(纯粹加载/转储数组)它可能是有效的

另一答案

这是与PyTables的比较。

由于内存限制,我无法达到(int(1e3), int(1e6)。因此,我使用了一个较小的数组:

data = np.random.random((int(1e3), int(1e5)))

NumPy save

%timeit np.save('array.npy', data)
1 loops, best of 3: 4.26 s per loop

NumPy load

%timeit data2 = np.load('array.npy')
1 loops, best of 3: 3.43 s per loop

PyTables写作:

%%timeit
with tables.open_file('array.tbl', 'w') as h5_file:
    h5_file.create_array('/', 'data', data)

1 loops, best of 3: 4.16 s per loop

PyTables阅读:

 %%timeit
 with tables.open_file('array.tbl', 'r') as h5_file:
      data2 = h5_file.root.data.read()

 1 loops, best of 3: 3.51 s per loop

数字非常相似。因此PyTables在这里没有真正的好处。但我们非常接近我的SSD的最大写入和读取速率。

写作:

Maximum write speed: 241.6 MB/s
PyTables write speed: 183.4 MB/s

读:

Maximum read speed: 250.2
PyTables read speed: 217.4

由于数据的随机性,压缩并没有真正帮助:

%%timeit
FILTERS = tables.Filters(complib='blosc', complevel=5)
with tables.open_file('array.tbl', mode='w', filters=FILTERS) as h5_file:
    h5_file.create_carray('/', 'data', obj=data)
1 loops, best of 3: 4.08 s per loop

读取压缩数据变得有点慢:

%%timeit
with tables.open_file('array.tbl', 'r') as h5_file:
    data2 = h5_file.root.data.read()

1 loops, best of 3: 4.01 s per loop

这与常规数据不同:

 reg_data = np.ones((int(1e3), int(1e5)))

写作速度明显加快:

%%timeit
FILTERS = tables.Filters(complib='blosc', complevel=5)
with tables.open_file('array.tbl', mode='w', filters=FILTERS) as h5_file:
    h5_file.create_carray('/', 'reg_data', obj=reg_data)

1个循环,最佳3:849 ms每个循环

阅读也是如此:

%%timeit
with tables.open_file('array.tbl', 'r') as h5_file:
    reg_data2 = h5_file.root.reg_data.read()

1 loops, best of 3: 1.7 s per loop

结论:使用PyTables时,您的数据越频繁。

另一答案

根据我的经验,到目前为止,在硬盘和内存之间传输数据时,np.save()和np.load()是最快的解决方案。在我意识到这个结论之前,我非常依赖我在数据库和HDFS系统上的数据加载。我的测试表明:数据库数据加载(从硬盘到内存)带宽可能约为50 MBps(Byets / Second),但np.load()带宽几乎与我的硬盘最大带宽相同:2GBps(Byets /第二)。两个测试环境都使用最简单的数据结构。

并且我认为使用几秒钟加载具有形状的数组不是一个问题:(1e3,1e6)。例如。您的数组形状为(1000,1000000),其数据类型为float128,则纯数据大小为(128/8)* 1000 * 1,000,000 = 16,000,000,000 = 16GBytes,如果需要4秒,那么您的数据加载带宽为16GBytes / 4秒= 4GBps。 SATA3最大带宽为600MBps = 0.6GBps,您的数据加载带宽已经是它的6倍,您的数据加载性能几乎可以与DDR's maximum bandwidth竞争,您还想要什么?

所以我的最终结论是:

如果可以使用np.save()和np.load(),请不要使用python的Pickle,不要使用任何数据库,不要使用任何大数据系统将数据存储到硬盘中。到目前为止,这两个函数是在硬盘和内存之间传输数据的最快解决方案。

我还测试了HDF5,发现它比np.load()和np.save()慢,所以如果你的平台上有足够的DDR内存,请使用np.save()和np.load()。

以上是关于numpy数组最快的保存和加载选项的主要内容,如果未能解决你的问题,请参考以下文章

numpy 保存/加载损坏数组

如何保存 numpy 数组以便以后可以适当地加载它们?

从压缩字节加载 numpy 数组

保存片段状态操作栏选项卡

Numpy数组的保存与加载

在android中动态创建选项卡并使用传入的参数加载片段