【问题标题】:How to speed up reading from compressed HDF5 files如何加快从压缩的 HDF5 文件中读取的速度
【发布时间】:2019-08-13 06:50:52
【问题描述】:

我有几个大的 HDF5 文件存储在 SSD 上(lzf 压缩文件大小为 10–15 GB,未压缩大小为 20–25 GB)。将此类文件中的内容读入 RAM 以进行进一步处理,每个文件大约需要 2 分钟。在此期间,仅使用一个内核(但达到 100%)。所以我猜解压部分在CPU上运行是瓶颈,而不是SSD的IO吞吐。

在我的程序开始时,它会将多个此类文件读入 RAM,这需要相当长的时间。 我喜欢通过利用更多内核和最终更多 RAM 来加速该过程,直到 SSD IO 吞吐量成为限制因素。我正在使用的机器有大量资源(20 个 CPU 内核 [+ 20 HT] 和 400 GB RAM),而且“浪费” RAM 没什么大不了的,只要节省时间是合理的。

我自己有两个想法:

1) 使用 python 的multiprocessing 模块将多个文件并行读入 RAM。这原则上可行,但由于在 multiprocessing 中使用了 Pickle(如 here 所述),我达到了 4 GB 的序列化限制:

OverflowError('无法序列化大于 4 GiB 的字节对象')。

2) 让多个进程(使用multiprocessing 模块中的Pool)打开同一个HDF5 文件(使用with h5py.File('foo.h5', 'r') as h_file:),从中读取单个块(chunk = h_file['label'][i : i + chunk_size])并返回该块。然后将收集的块连接起来。但是,这失败了

OSError: 无法读取数据(Fletcher32 校验和检测到数据错误)。

这是因为我在多个进程中打开同一个文件(如建议的here)吗?


所以我的最后一个问题是:如何将.h5 文件的内容更快地读入主内存?再次重申:为了节省时间而使用“浪费”RAM 是允许的。内容必须驻留在主存储器中,因此仅通过读取行​​或分数来规避问题不是一种选择。 我知道我可以只存储未压缩的 .h5 文件,但这只是我喜欢使用的最后一个选项,因为 SSD 上的空间稀缺。我更喜欢两者兼备,压缩文件和快速读取(最好通过更好地利用可用资源)。

元信息:我使用 python 3.5.2 和 h5py 2.8.0。


编辑:读取文件时,SSD 以 72 MB/s 的速度运行,远未达到最大值。 .h5 文件是使用 h5py 的 create_dataset 方法和 compression="lzf" 选项创建的。

编辑 2:这是(简化的)我用来读取(压缩)HDF5 文件内容的代码:

def opener(filename, label): # regular version
    with h5py.File(filename, 'r') as h_file:
        data = g_file[label][:]
    return data

def fast_opener(filename, label): # multiple processes version
    with h5py.File(filename, 'r') as h_file:
        length = len(h_file[label])
    pool = Pool() # multiprocessing.Pool and not multiprocessing.dummy.Pool
    args_iter = zip(
        range(0, length, 1000),
        repeat(filename),
        repeat(label),
    )
    chunks = pool.starmap(_read_chunk_at, args_iter)
    pool.close()
    pool.join()
    return np.concatenate(chunks)

def _read_chunk_at(index, filename, label):
    with h5py.File(filename, 'r') as h_file:
        data = h_file[label][index : index + 1000]
    return data

如你所见,解压是由h5py透明完成的。

【问题讨论】:

  • 如果您使用的是 Linux(假设),请查看 iotop。确保您的磁盘 IO 不会成为瓶颈。否则无论你创建多少进程都不会加快加载速度。
  • 解压是怎么做的?在我看来,问题在于 LZF 解压缩运行单核,独立于您使用 HDF5 文件这一事实。
  • 由于这是一个 IO 密集型工作负载,并且读取和(解)压缩可能发生在 C 代码中,请使用 threading.Threads(或 multiprocessing.dummy.Pool,如果您喜欢 multiprocessing API)。它不应该像 Python 线程那样受 GIL 的约束。
  • @user3389669 可以添加将文件加载到内存中的代码吗?你是显式解压还是由h5py处理?
  • 我认为方法 2 应该可行。创建工作人员时是否有可能在父进程中打开文件?我discovered 说工作人员最终可能会共享文件句柄,这会导致奇怪的错误,因为他们都尝试使用相同的文件描述符进行查找和读取。

标签: python python-3.x hdf5 h5py


【解决方案1】:

h5py 通过过滤器处理 LZF 文件的解压缩。过滤器的源代码,用 C 实现,是available on the h5py Github here。查看导致瓶颈的函数implementation of lzf_decompress,您可以看到它没有并行化(不知道它是否可以并行化,我将把判断留给更熟悉 LZF 内部工作的人)。

话虽如此,恐怕没有办法只获取您的巨大压缩文件并进行多线程解压缩。据我所知,您的选择是:

  • 将大文件分割成较小的单独压缩块,在单独的核心上并行解压缩每个块(multiprocessing 可能会有所帮助,但您需要注意进程间共享内存)并将所有内容重新组合在一起解压后。
  • 只使用未压缩的文件。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-15
    • 1970-01-01
    • 2018-03-26
    • 1970-01-01
    • 2021-08-20
    • 2020-10-06
    相关资源
    最近更新 更多