h5py 的性能瓶颈在哪里?

Where it the h5py performance bottleneck?

我有一个带有 100 个“事件”的 HDF5。每个事件包含变量,但大约有 180 个称为“轨迹”的组,每个轨迹内部有 6 个数据集,这些数据集是 32 位浮点数的数组,每个大约 1000 个单元格长(这在事件之间略有变化,但在事件内部保持不变) .该文件是使用默认的 h5py 设置生成的(因此除非 h5py 自行执行,否则不会进行分块或压缩)。

读取速度不快。它比从 CERN ROOT TTrees 读取相同数据慢约 6 倍。我知道 HDF5 远不是市场上最快的格式,但如果你能告诉我速度在哪里丢失,我将不胜感激。

要读取跟踪中的数组,我这样做:

    d0keys = data["Run_0"].keys()
    for key_1 in d0keys:
        if("Event_" in key_1):
            d1 = data["Run_0"][key_1]
            d1keys = d1.keys()
            for key_2 in d1keys:
                if("Traces_" in key_2):
                    d2 = d1[key_2]
                    v1, v2, v3, v4, v5, v6 = d2['SimSignal_X'][0],d2['SimSignal_Y'][0],d2['SimSignal_Z'][0],d2['SimEfield_X'][0], d2['SimEfield_Y'][0],d2['SimEfield_Z'][0]

行分析器显示,~97% 的时间花在最后一行。现在,有两个问题:

  1. 读取单元格[0]和所有~1000个单元格[:]之间似乎没有区别。我知道 h5py 应该只能从磁盘读取一大块数据。为什么没有区别?
  2. 从 HDD(Linux,ext4)读取 100 个事件使用 h5py 需要大约 30 秒,使用 ROOT 需要大约 5 秒。 100 个事件的大小大约为 430 MB。这使得 HDF 中的读出速度为 ~14 MBps,而 ROOT 为~86 MBps。两者都很慢,但 ROOT 更接近我期望的 ~4 岁笔记本电脑硬盘的原始读出速度。

那么h5py到底是哪里丢了速度呢?我想纯粹的读数应该只是硬盘速度。因此,瓶颈是:

  1. 取消对数据集的 HDF5 地址引用(ROOT 不需要这样做)?
  2. 在 python 中分配内存?
  3. 还有别的吗?

如果能提供一些线索,我将不胜感激。

有很多 HDF5 I/O 问题需要考虑。我会尽量涵盖每一个。

根据我的测试,花在 I/O 上的时间主要是 reads/writes 数量的函数,而不是 read/write 的数据量(以 MB 为单位)。阅读此 SO post 了解更多详情: pytables writes much faster than h5py. Why? 注意:它显示了 I/O 固定数据量的性能,h5py 和 PyTables 的写入大小不同 I/O。基于此,大部分时间花在最后一行是有道理的——那是您将数据作为 NumPy 数组 (v1, v2, v3, v4, v5, v6).

从磁盘读取到内存的地方

关于您的问题:

  1. 阅读 d2['SimSignal_X'][0]d2['SimSignal_X'][:] 之间没有区别是有原因的。两者都将整个数据集读入内存(所有约 1000 个数据集值)。如果只想读取数据的一部分,则需要使用切片表示法。例如,d2['SimSignal_X'][0:100] 只读取前 100 个值(假设 d2['SimSignal_X'] 只有一个轴 -- shape=(1000,))。笔记;读取切片会减少所需的内存,但不会缩短 I/O 读取时间。 (事实上​​,读取切片可能会增加读取时间。)
  2. 我不熟悉 CERN ROOT,所以无法评论 h5py 与 ROOT 的性能。如果您想使用 Python,请考虑以下几点:
    • 您正在将 HDF5 数据读入内存(作为 NumPy 数组)。你不必那样做。您可以创建 h5py 数据集对象,而不是创建数组。这最初减少了 I/O 。然后你使用对象“好像”它们是 np.arrays。唯一的变化是最后一行代码——像这样:v1, v2, v3, v4, v5, v6 = d2['SimSignal_X'], d2['SimSignal_Y'], d2['SimSignal_Z'], d2['SimEfield_X'], d2['SimEfield_Y'], d2['SimEfield_Z']。请注意如何不使用切片符号([0][:])。
    • 我发现 PyTables 比 h5py 更快。您的代码可以轻松转换为使用 PyTables 读取。
    • HDF5 可以使用“分块”来提高 I/O 性能。你没有说你是否正在使用它。它可能会有所帮助(很难说,因为您的数据集不是很大)。您必须在创建 HDF5 文件时定义它,因此在您的情况下可能没有用。
    • 你也没有说写入数据时是否使用了压缩过滤器。这减少了磁盘文件的大小,但具有降低 I/O 性能(增加读取时间)的副作用。如果您不知道,请检查是否在创建文件时启用了压缩。