我正在使用 Cython 使用 64 个线程从 HDF5 文件中读取单个数据集。每个线程计算起始索引
start
和块大小 size
,并从该块读取到公共缓冲区 buf
,这是 NumPy 数组的内存视图。至关重要的是,每个线程都会打开自己的文件和数据集副本。这是代码:
def read_hdf5_dataset(const char* file_name, const char* dataset_name,
long[::1] buf, int num_threads):
cdef hsize_t base_size = buf.shape[0] // num_threads
cdef hsize_t start, size
cdef hid_t file_id, dataset_id, mem_space_id, file_space_id
cdef int thread
for thread in prange(num_threads, nogil=True):
start = base_size * thread
size = base_size + buf.shape[0] % num_threads \
if thread == num_threads - 1 else base_size
file_id = H5Fopen(file_name, H5F_ACC_RDONLY, H5P_DEFAULT)
dataset_id = H5Dopen2(file_id, dataset_name, H5F_ACC_RDONLY)
mem_space_id = H5Screate_simple(1, &size, NULL)
file_space_id = H5Dget_space(dataset_id)
H5Sselect_hyperslab(file_space_id, H5S_SELECT_SET, &start,
NULL, &size, NULL)
H5Dread(dataset_id, H5Dget_type(dataset_id), mem_space_id,
file_space_id, H5P_DEFAULT, <void*> &buf[start])
H5Sclose(file_space_id)
H5Sclose(mem_space_id)
H5Dclose(dataset_id)
H5Fclose(file_id)
尽管它正确读取数据集,但在约 100 亿个条目的 float32 数据集上,CPU 利用率最大为 100%,即使它使用所有 64 个 CPU(尽管由于 I/O 的原因,利用率仅为约 20-30%)瓶颈)在大约 1 亿个条目的 float32 数据集上。我在两个不同的计算集群上进行了尝试,得到了相同的结果。也许这与数据集的大小大于 INT32_MAX 有关?
是什么阻止了此代码在极大的数据集上并行运行,我该如何修复它?任何其他提高代码清晰度或效率的建议也将不胜感激。
正在发生的事情要么阻止 cython 的
prange
启动多个线程,要么阻止线程启动后到达任何地方。它可能与 hdf5 有直接关系,也可能没有关系。以下是一些可能的原因:
您是否在运行函数之前预先分配了足够大的
buf
来容纳整个数据集?如果是这样,则意味着您的程序正在分配 40+ GB 的内存(每个 float32
4 个字节)。您运行的节点有多少内存?您是唯一的用户吗?内存不足很容易导致您所描述的性能问题。
cython 和 hdf5 都需要某些编译标志才能正确支持并行性。在小型和大型数据集运行之间,您是否修改或重新编译了代码?
解释为什么你的程序 100% 使用单个 cpu 的一个简单方法是,在你的
read_hdf5_dataset
函数被调用之前,它就挂在某个地方了。您的程序中还有哪些其他代码首先运行,它是否会导致您看到的问题?
这里的部分问题是,该网站上的任何用户都很难重现您的确切问题,因为我们没有您的大部分程序,而且我至少没有任何 40 gig hdf5 文件躺在周围(在我研究生时代,太字节)。如果我的上述建议之一没有帮助,我认为您有两种前进的方法: