python - 用于读取行的最佳 HDF5 数据集 block 形状

标签 python performance dataset hdf5 h5py

我有一个合理大小(18GB 压缩)的 HDF5 数据集,我希望优化阅读行以提高速度。形状是 (639038, 10000)。我将多次读取位于数据集中的选定行(比如 ~1000 行)。所以我不能使用 x:(x+1000) 对行进行切片。

使用 h5py 从内存不足的 HDF5 中读取行已经很慢了,因为我必须传递一个排序列表并求助于花哨的索引。有没有办法避免花哨的索引,或者我可以使用更好的 block ​​形状/大小?

我已经阅读了一些经验法则,例如 1MB-10MB 的 block 大小和选择与我正在阅读的内容一致的形状。然而,构建大量具有不同 block 形状的 HDF5 文件进行测试在计算上非常昂贵且非常慢。

对于大约 1,000 行的每个选择,我立即将它们相加以获得长度为 10,000 的数组。我当前的数据集如下所示:

'10000': {'chunks': (64, 1000),
          'compression': 'lzf',
          'compression_opts': None,
          'dtype': dtype('float32'),
          'fillvalue': 0.0,
          'maxshape': (None, 10000),
          'shape': (639038, 10000),
          'shuffle': False,
          'size': 2095412704}

我已经尝试过的:

  • 重写 block 形状 (128, 10000) 的数据集(我计算为 ~5MB)非常慢。
  • 我查看了 dask.array 以进行优化,但由于大约 1,000 行很容易放入内存中,我看不到任何好处。

最佳答案

找到合适的 block 缓存大小

首先我想讨论一些一般性的事情。 知道每个单独的 block 只能作为一个整体来读取或写入是非常重要的。 h5py 的标准 block 缓存大小可以避免过多的磁盘 I/O,默认情况下仅为 1 MB,在许多情况下应该增加,这将在后面讨论。

举个例子:

  • 我们有一个形状为 (639038, 10000)、float32(25.5 GB 未压缩)的数据集
  • 我们想按列写入数据 dset[:,i]=arr 并按行读取数据 arr=dset[i,:]
  • 我们为这种类型的工作选择了一个完全错误的 block 形状,即 (1,10000)

在这种情况下,读取速度不会太差(尽管 block 大小有点小),因为我们只读取我们正在使用的数据。但是当我们在该数据集上写入时会发生什么?如果我们访问一列,则写入每个 block 的一个 float 。这意味着我们实际上是在每次迭代时写入整个数据集(25.5 GB),并且每隔一次读取整个数据集。这是因为如果你修改一个 block ,如果它没有被缓存,你必须先读取它(我在这里假设 block 缓存大小低于 25.5 GB)。

那么我们可以在这里改进什么? 在这种情况下,我们必须在写入/读取速度和 block 缓存使用的内存之间做出折衷。

一个可以同时提供良好/读写速度的假设:

  • 我们选择 block 大小为 (100, 1000)
  • 如果我们想要遍历第一个维度,我们至少需要 (1000*639038*4 ->2,55 GB) 缓存以避免如上所述的额外 IO 开销和 (100*10000*4 -> 0, 4MB)。
  • 所以我们应该在这个例子中提供至少 2.6 GB 的 block 数据缓存。

结论 通常没有正确的 block 大小或形状,这在很大程度上取决于使用哪一个的任务。切勿在未考虑 block 缓存的情况下选择 block 大小或形状。在随机读/写方面,RAM 比最快的 SSD 快几个数量级。

关于您的问题 我会简单地读取随机行,不正确的 chunk-cache-size 才是你真正的问题。

将以下代码的性能与您的版本进行比较:

import h5py as h5
import time
import numpy as np

def ReadingAndWriting():
    File_Name_HDF5='Test.h5'

    #shape = (639038, 10000)
    shape = (639038, 1000)
    chunk_shape=(100, 1000)
    Array=np.array(np.random.rand(shape[0]),np.float32)

    #We are using 4GB of chunk_cache_mem here ("rdcc_nbytes")
    f = h5.File(File_Name_HDF5, 'w',rdcc_nbytes =1024**2*4000,rdcc_nslots=1e7)
    d = f.create_dataset('Test', shape ,dtype=np.float32,chunks=chunk_shape,compression="lzf")

    #Writing columns
    t1=time.time()
    for i in range(0,shape[1]):
        d[:,i:i+1]=np.expand_dims(Array, 1)

    f.close()
    print(time.time()-t1)

    # Reading random rows
    # If we read one row there are actually 100 read, but if we access a row
    # which is already in cache we would see a huge speed up.
    f = h5.File(File_Name_HDF5,'r',rdcc_nbytes=1024**2*4000,rdcc_nslots=1e7)
    d = f["Test"]
    for j in range(0,639):
        t1=time.time()
        # With more iterations it will be more likely that we hit a already cached row
        inds=np.random.randint(0, high=shape[0]-1, size=1000)
        for i in range(0,inds.shape[0]):
            Array=np.copy(d[inds[i],:])
        print(time.time()-t1)
    f.close()

花式切片的最简单形式

我在评论中写道,我在最近的版本中看不到这种行为。我错了。比较以下内容:

def 写作(): File_Name_HDF5='测试.h5'

#shape = (639038, 10000)
shape = (639038, 1000)
chunk_shape=(100, 1000)
Array=np.array(np.random.rand(shape[0]),np.float32)

# Writing_1 normal indexing
###########################################
f = h5c.File(File_Name_HDF5, 'w',chunk_cache_mem_size=1024**2*4000)
d = f.create_dataset('Test', shape ,dtype=np.float32,chunks=chunk_shape,compression="lzf")

t1=time.time()
for i in range(shape[1]):
    d[:,i:i+1]=np.expand_dims(Array, 1)

f.close()
print(time.time()-t1)

# Writing_2 simplest form of fancy indexing
###########################################
f = h5.File(File_Name_HDF5, 'w',rdcc_nbytes =1024**2*4000,rdcc_nslots=1e7)
d = f.create_dataset('Test', shape ,dtype=np.float32,chunks=chunk_shape,compression="lzf")

#Writing columns
t1=time.time()
for i in range(shape[1]):
    d[:,i]=Array

f.close()
print(time.time()-t1)

第一个版本在我的硬盘上有 34 秒,第二个版本有 78 秒。

关于python - 用于读取行的最佳 HDF5 数据集 block 形状,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48385256/

相关文章:

python - 如果某些行值与第二个 DataFrame 中的行值相同,则有效地从 DataFrame 中删除行

C# 无法读取数据集中的 xml

python - 在MySQL上执行插入,没有错误但也没有插入

python - 输出到文本文件

python - 提供图像的首选方法是什么

mysql - 使用更新增加列或创建更新触发器 - mysql

python - 将矩阵的每个元素作为一个 block 重复到新矩阵中

python - 在 Python 中从 OrderedDict 访问数据并对其进行循环

azure - 使用应用程序注册获取 Microsoft 不记名 token 并从数据集中读取

python - 无法加载 CIFAR-10 数据集 : Invalid load key '\x1f'