h5py 在通过类文件对象访问数据时执行许多小读取
h5py performs many small reads when accessing data over a file-like object
当我向 h5py
传递一个通过网络连接流式传输数据的 Python 类文件对象时,我有以下两个问题(类文件对象是有效的,例如不使用 smart_open
).
对远程读取执行完整文件扫描
h5py
似乎每次读取 ~115k 字节时都会对 read(...)
进行多次调用(每次读取相差几百字节)。
h5py
似乎在列主要模式下运行,尽管 this article says it's row major 有文档参考。
我能够通过将我的文件对象上的 read
函数设置为本地读取函数来计算读取调用,该函数使用 functools.partial
来计算调用和字节数。
我的数据是这样的:
print(h5file['table']) -> <HDF5 dataset "table": shape (1028, 7818600), type "<u2">
测试:
- A) 当我读取
h5file['table'][0, :1024]
时,有 20
个读取调用(6
其中 ~115k
),并且读取了 702,325
个字节总计
- B) 当我读取
h5file['table'][0, :2048]
时有 25
个读取调用(6
其中 ~115k
),并且读取了 702,325
个字节总计
- C) 当我读取
h5file['table'][0, :2048]
时有 25
个读取调用(11
其中 ~115k
,总共读取 1,278,470
个字节.
- D) 当我读取
h5file['table'][:, :2048]
时,有 25
个读取调用(11
其中 ~115k
,总共读取了 1,278,470
个字节.
- E) 当我读取
h5file['table'][0, :1000000]
时,有 64
个读取调用(50
其中 ~115k
,总共读取了 5,772,539
个字节.
结论:
- 数据不能按行存储,否则
A
和B
会不同(C
和D
也一样)。
- 数据块大小为
~115k
+/- 几百字节,因调用而异。
- 必须压缩数据,因为最终数组的总字节数大于传输的字节数。
问题:
- 我可以更改读取块大小以减少读取调用吗?网络延迟限制了我每秒大约 2 次读取,这些读取是 运行 串联的。这变得非常缓慢。大型单次读取操作的总带宽要快得多。
- 我可以确认数据是以列主要格式存储的吗?这是有道理的,因为这是对每个
1028
通道重复采样的传感器数据。但是自从我读到 hdf5 总是主要列会产生一个我想理解的差异。
压缩和分块 I/O 是 HDF5/h5py 的功能。 (使用 chunked I/O 时会自动执行压缩)。首先检查数据集 chunksize 属性:print(h5file['table'].chunks)
。这可能会确认您看到的 115k 值。块形状还应该指出为什么您将读取顺序视为列优先而不是行优先——因为块形状1 大于块形状[0].
如果块大小是导致问题的原因,您应该使用更大的块大小。但是,据我所知,您必须创建一个包含新数据集的新文件才能进行更改。
作为参考,这里有一个示例显示了一个简单的数组,其中定义了 3 个不同的块形状。文件关闭然后以读取模式重新打开。使用更大的块大小将数据读取和写入新文件。这解决了 group.copy()
的 h5py 限制(它在复制数据集时使用相同的块参数)。您还可以使用来自 HDF Group 的 'h5repack' 外部实用程序复制数据集并修改块 I/O 属性,参考:h5repack doc page.
import numpy as np
import h5py
size= 100
arr = np.random.random(size*size*size).reshape(size,size,size)
# Create file with small chunk size
with h5py.File('SO_69681364.h5_1','w') as h5file:
h5file.create_dataset('chunked_a0',data=arr,chunks=(1,size,size))
h5file.create_dataset('chunked_a1',data=arr,chunks=(size,1,size))
h5file.create_dataset('chunked_a2',data=arr,chunks=(size,size,1))
print(h5file['chunked_a0'].chunks)
print(h5file['chunked_a1'].chunks)
print(h5file['chunked_a2'].chunks)
# Open previous file and copy data to new file with larger chunk size
with h5py.File('SO_69681364_1.h5','r') as h5fr, \
h5py.File('SO_69681364_2.h5','w') as h5fw:
ds = h5fr['chunked_a0']
h5fw.create_dataset('chunked_a0',data=ds,chunks=(10,size,size))
h5fw.create_dataset('chunked_a1',data=ds,chunks=(size,10,size))
h5fw.create_dataset('chunked_a2',data=ds,chunks=(size,size,10))
print(h5fw['chunked_a0'].chunks)
print(h5fw['chunked_a1'].chunks)
print(h5fw['chunked_a2'].chunks)
当我向 h5py
传递一个通过网络连接流式传输数据的 Python 类文件对象时,我有以下两个问题(类文件对象是有效的,例如不使用 smart_open
).
h5py
似乎每次读取 ~115k 字节时都会对read(...)
进行多次调用(每次读取相差几百字节)。h5py
似乎在列主要模式下运行,尽管 this article says it's row major 有文档参考。
我能够通过将我的文件对象上的 read
函数设置为本地读取函数来计算读取调用,该函数使用 functools.partial
来计算调用和字节数。
我的数据是这样的:
print(h5file['table']) -> <HDF5 dataset "table": shape (1028, 7818600), type "<u2">
测试:
- A) 当我读取
h5file['table'][0, :1024]
时,有20
个读取调用(6
其中~115k
),并且读取了702,325
个字节总计 - B) 当我读取
h5file['table'][0, :2048]
时有25
个读取调用(6
其中~115k
),并且读取了702,325
个字节总计 - C) 当我读取
h5file['table'][0, :2048]
时有25
个读取调用(11
其中~115k
,总共读取1,278,470
个字节. - D) 当我读取
h5file['table'][:, :2048]
时,有25
个读取调用(11
其中~115k
,总共读取了1,278,470
个字节. - E) 当我读取
h5file['table'][0, :1000000]
时,有64
个读取调用(50
其中~115k
,总共读取了5,772,539
个字节.
结论:
- 数据不能按行存储,否则
A
和B
会不同(C
和D
也一样)。 - 数据块大小为
~115k
+/- 几百字节,因调用而异。 - 必须压缩数据,因为最终数组的总字节数大于传输的字节数。
问题:
- 我可以更改读取块大小以减少读取调用吗?网络延迟限制了我每秒大约 2 次读取,这些读取是 运行 串联的。这变得非常缓慢。大型单次读取操作的总带宽要快得多。
- 我可以确认数据是以列主要格式存储的吗?这是有道理的,因为这是对每个
1028
通道重复采样的传感器数据。但是自从我读到 hdf5 总是主要列会产生一个我想理解的差异。
压缩和分块 I/O 是 HDF5/h5py 的功能。 (使用 chunked I/O 时会自动执行压缩)。首先检查数据集 chunksize 属性:print(h5file['table'].chunks)
。这可能会确认您看到的 115k 值。块形状还应该指出为什么您将读取顺序视为列优先而不是行优先——因为块形状1 大于块形状[0].
如果块大小是导致问题的原因,您应该使用更大的块大小。但是,据我所知,您必须创建一个包含新数据集的新文件才能进行更改。
作为参考,这里有一个示例显示了一个简单的数组,其中定义了 3 个不同的块形状。文件关闭然后以读取模式重新打开。使用更大的块大小将数据读取和写入新文件。这解决了 group.copy()
的 h5py 限制(它在复制数据集时使用相同的块参数)。您还可以使用来自 HDF Group 的 'h5repack' 外部实用程序复制数据集并修改块 I/O 属性,参考:h5repack doc page.
import numpy as np
import h5py
size= 100
arr = np.random.random(size*size*size).reshape(size,size,size)
# Create file with small chunk size
with h5py.File('SO_69681364.h5_1','w') as h5file:
h5file.create_dataset('chunked_a0',data=arr,chunks=(1,size,size))
h5file.create_dataset('chunked_a1',data=arr,chunks=(size,1,size))
h5file.create_dataset('chunked_a2',data=arr,chunks=(size,size,1))
print(h5file['chunked_a0'].chunks)
print(h5file['chunked_a1'].chunks)
print(h5file['chunked_a2'].chunks)
# Open previous file and copy data to new file with larger chunk size
with h5py.File('SO_69681364_1.h5','r') as h5fr, \
h5py.File('SO_69681364_2.h5','w') as h5fw:
ds = h5fr['chunked_a0']
h5fw.create_dataset('chunked_a0',data=ds,chunks=(10,size,size))
h5fw.create_dataset('chunked_a1',data=ds,chunks=(size,10,size))
h5fw.create_dataset('chunked_a2',data=ds,chunks=(size,size,10))
print(h5fw['chunked_a0'].chunks)
print(h5fw['chunked_a1'].chunks)
print(h5fw['chunked_a2'].chunks)