为什么读取整个HDF5数据集比切片更快?

7

我正在尝试弄清楚为什么会发生这种情况:

In [1]: import time, h5py as h5
In [2]: f = h5.File('myfile.hdf5', 'r')                                                                                                                                    
In [3]: st = time.time(); data = f["data"].value[0,:,1,...]; elapsed = time.time() - st;
In [4]: elapsed
Out[4]: 11.127676010131836
In [5]: st = time.time(); data = f["data"][0,:,1,...]; elapsed2 = time.time() - st;
In [6]: elapsed2
Out[6]: 59.810582399368286
In [7]: f["data"].shape
Out[7]: (1, 4096, 6, 16, 16, 16, 16)
In [8]: f["data"].chunks
Out[8]: (1, 4096, 1, 16, 16, 16, 16)

正如您所看到的,将整个数据集加载到内存中,然后对其进行切片要比从数据集中取出相同的切片更快。
块大小与切片匹配,因此它应该是连续的内存,对吧?那么为什么速度慢这么多呢?
数据集使用gzip压缩(opts=2)。
根据安德鲁的评论,我运行了它,在两次读取之间清除了缓存:
elapsed1: 11.001180410385132
elapsed2: 43.19723725318909
48.61user 4.45system 0:54.65elapsed 97%CPU (0avgtext+0avgdata 8431596maxresident)k
479584inputs+0outputs (106major+3764414minor)pagefaults 0swaps

(这次运行在两次读取之间有10秒的延迟,以清除缓存)
elapsed1: 11.46790862083435
elapsed2: 43.438515186309814

48.54user 4.66system 1:05.71elapsed 80%CPU (0avgtext+0avgdata 8431944maxresident)k
732504inputs+0outputs (220major+3764449minor)pagefaults 0swaps

你的操作系统是什么?如果是Linux,尝试在/usr/bin/time /your/python/here下运行不同版本的Python代码。这将显示CPU时间花费在内核/系统空间还是用户空间,并提供一些线索以了解情况。此外,在Linux上,您可以使用strace查看两个版本中进行的系统调用。我怀疑“将所有内容读入内存”的方法只解压缩数据一次,而较慢的方法则需要多次寻找和解压缩。 - Andrew Henle
此外,如果您使用的是Linux,请在运行每个不同版本之前清除页面缓存 - Andrew Henle
2
你正在使用高级切片技术。https://dev59.com/i6jja4cB1Zd3GeqP-WvX#48405220 我在“最简单的高级索引形式”中提到了这种情况。你的块也非常大。如果你使用适当的块缓存大小,这并不是必要的。 - max9111
还有比gzip更快的压缩算法可用。如果在多种语言之间移植性不是问题(gzip随每个HDF5安装而来,而比gzip快得多的blosc则不然),请考虑使用blosc。如果您的数据提供了高压缩比并存储在HDD上,则通常比未压缩的数据集更快。例如,从HDD中获得900MB/s -> https://dev59.com/TKjka4cB1Zd3GeqPFd9V#48997927 - max9111
我想知道 data[0][:,1] 是怎么样的。 - hpaulj
1个回答

1

首先我进行了自己的测试。由于没有你的HDF5文件,所以使用了我的一个测试文件。我的测试表数据集有大约54,000行(似乎比你的更大)。
使用.value[]的时间结果为

>>> elapsed
0.15540122985839844

使用NumPy索引的时间结果如下:
>>> elapsed2
0.12980079650878906

因此,我没有看到性能上的太大差别。也许这与我们测试的数据集大小或数据表的复杂性有关?

最近的h5py文档中有一些有趣的评论关于Dataset.value(来自2018年6月5日发布的版本2.8.0; 强调是我的):
现在已弃用Dataset.value属性。
此属性将整个数据集转储到NumPy数组中。使用.value的代码应更新为使用NumPy索引,使用适当的mydataset[...]mydataset[()]

您的计时测试似乎与上述突出观察相反。

我认为您需要向h5py开发人员询问性能差异(以及数据存储位置-内存还是磁盘)。您是否已经与h5py用户组进行了确认?

编辑: 发布后,我发现了这个SO问答。它有很多好的评论,并包括来自h5py开发者的回复:
h5py:正确切片数组数据集的方法


OP的矩阵具有超过10亿个元素。 - dshin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接