如何有效设置将包含未知数据量的HDF5文件?

时间:2018-11-02 00:29:09

标签: python hdf5

我有一个可以任意长时间运行的模拟。为了存储模拟的输出,我天真地创建了一个可调整大小的HDF5文件,并在获取数据时不断将其存储到该文件中,如以下玩具示例所示:

import contextlib
import os
import time
import numpy as np
import h5py

num_timepoints = 18000
num_vertices = 16
num_info = 38
output_size = 10

t0 = "A:\\t0.hdf5"

with contextlib.suppress(FileNotFoundError):
    os.remove(t0)

st = time.time()

with h5py.File(t0, "a") as f:
    dset = f.create_dataset("test", (0, num_vertices, num_info), maxshape=(None, num_vertices, num_info))

for n in np.arange(18000/output_size):
    chunk = np.random.rand(output_size, 16, 38)
    with h5py.File(t0, "a") as f:
        dset = f["test"]

        orig_index = dset.shape[0]

        dset.resize(dset.shape[0] + chunk.shape[0], axis=0)
        dset[orig_index:, :, :] = chunk

et = time.time()

print("test0: time taken: {} s, size: {} kB".format(np.round(et - st, 2), int(os.path.getsize(t0))/1000))

请注意,平均而言,测试数据的大小与我从模拟中获得的数据大小相似(在最坏的情况下,我可能有2到3倍的测试时间点数)。

此测试的输出为:

test0: time taken: 2.02 s, size: 46332.856 kB

将此输出与提供数据大小的测试进行比较:

t1 = "A:\\t1.hdf5"

with contextlib.suppress(FileNotFoundError):
    os.remove(t1)

st = time.time()

data = np.random.rand(num_timepoints, num_vertices, num_info)
with h5py.File(t1, "a") as f:
    dset = f.create_dataset("test", data.shape)
    dset = data

et = time.time()

print("test1: time taken: {} s, size: {} kB".format(np.round(et - st, 2), int(os.path.getsize(t1))/1000))

具有以下输出:

test1: time taken: 0.09 s, size: 1.4 kB

如果我选择output_size(这反映了我一次从仿真中获得的大量数据)为1,则test0大约需要40秒,并创建一个大约700 MB的文件!

很显然,test0使用的是一种非常幼稚且效率低下的方法。我该如何改善?我完整的测试代码是:

import contextlib
import os
import time
import numpy as np
import h5py

# =================================================

num_timepoints = 18000
num_vertices = 16
num_info = 38
output_size = 10

t0 = "A:\\t0.hdf5"

with contextlib.suppress(FileNotFoundError):
    os.remove(t0)

st = time.time()

with h5py.File(t0, "a") as f:
    dset = f.create_dataset("test", (0, num_vertices, num_info), maxshape=(None, num_vertices, num_info))

for n in np.arange(18000/output_size):
    chunk = np.random.rand(output_size, 16, 38)
    with h5py.File(t0, "a") as f:
        dset = f["test"]

        orig_index = dset.shape[0]

        dset.resize(dset.shape[0] + chunk.shape[0], axis=0)
        dset[orig_index:, :, :] = chunk

et = time.time()

print("test0: time taken: {} s, size: {} kB".format(np.round(et - st, 2), int(os.path.getsize(t0))/1000))

# =================================================

t1 = "A:\\t1.hdf5"

with contextlib.suppress(FileNotFoundError):
    os.remove(t1)

st = time.time()

data = np.random.rand(num_timepoints, num_vertices, num_info)
with h5py.File(t1, "a") as f:
    dset = f.create_dataset("test", data.shape)
    dset = data

et = time.time()

print("test1: time taken: {} s, size: {} kB".format(np.round(et - st, 2), int(os.path.getsize(t1))/1000))

# =================================================

print("Done.")

1 个答案:

答案 0 :(得分:1)

我发现有些可以轻松提高性能的东西。首先,不要关闭然后重新打开文件以写入每个块:

with h5py.File(t0, "a") as f:
    dset = f["test"]
    for n in np.arange(18000/output_size):
        chunk = np.random.rand(output_size, 16, 38)

        orig_index = dset.shape[0]
        dset.resize(dset.shape[0] + chunk.shape[0], axis=0)
        dset[orig_index:, :, :] = chunk

这大约需要2秒到0.9秒。

第二,h5py为您的数据集猜测了一个相当奇怪的块形状(当我尝试时,为128 * 4 * 10)。您可以手动指定要添加的块的形状:

with h5py.File(t0, "a") as f:
    dset = f.create_dataset("test", (0, num_vertices, num_info),
                            maxshape=(None, num_vertices, num_info),
                            chunks=(output_size, num_vertices, num_info),
                           )

在这个例子中,我没有得到很大的提速(可能是0.9秒到0.8)。但是值得一看;可能会有所不同,具体取决于您的数据形状和存储空间。

最后,如果我一次写一个更大的块(output_size = 100),我看到的性能与一次完成的示例相同(或更胜一筹),大约0.5秒(一次完成一次)一旦固定了示例即可实际写入数据,请参阅我的评论。

当然,您不想更改模拟的操作,只是为了加快编写速度。但是,如果此加速速度很重要,则可以编写一些代码以对模拟中的数据进行批处理,并定期将更大的块写入HDF5。缺点是,如果模拟崩溃,您可能会丢失一些数据。

您还可以查看以较小的块进行大小调整的频率(例如,调整大小以增加100,然后在重新调整大小之前进行10次写入,每行10行)。 编辑:我尝试过,但实际上似乎并没有改善计时。