2017-10-08 59 views
-2

我有一个巨大的hdf5文件(〜100GB,连续存储),我需要随机访问不同的点。在python/h5py或C/H5Dread中使用索引似乎很慢,因此我想直接mmap数据。mmap hdf5数据集在C/C++

实际上,这在我的本地64位Fedora 25上的h5py/numpy中可用,跟在this之后。但在远程集群上,尽管python似乎是64位,并且使用C中的mmap对100GB文件进行了简单测试,但大型文件([Errno 12] Cannot allocate memory)的numpy/mmap失败。所以我的集群的Python可能有问题。

我看到的一个解决方案是在C中使用mmap。我写了一个小的test来创建一个带有1d数据集的小型hdf5,并使用`H5Dget_offset'获取数据集偏移量。但是,结果不正确。

以下是核心代码:根据本blog

/* Get dataset offset within file */ 
file_id = H5Fopen (FILE, H5F_ACC_RDONLY, H5P_DEFAULT); 
dataset_id = H5Dopen2(file_id, "/dset", H5P_DEFAULT); 
offset = H5Dget_offset(dataset_id); 

fd = open(FILE, O_RDONLY); 
// align with page size 
pa_offset = offset & ~(sysconf(_SC_PAGE_SIZE) - 1); 
length = NX * NY * sizeof(int); 
addr = mmap(NULL, length + offset - pa_offset, PROT_READ, 
      MAP_PRIVATE, fd, pa_offset); 

讨论提到朱莉娅实施通过H5Fget_vfd_handleH5Dget_offset来实现这一点,但我还没有找到一个详细的/容易解释。

  • 我通过蟒蛇有偏移/ h5py的dataset.id.get_offset是相同的,我通过H5Dget_offset在C.
  • 了,我认为我的核心问题是:如何使用C'S H5Dget_offset指定的偏移量的mmap数据集。
  • mmap应该比原来的hdf5访问速度快得多吗?
+0

为什么内存映射,而不是只是寻求和阅读? – tadman

+0

@tadman,“寻求和阅读”是指直接索引?我试过了,但那很慢。我不知道索引是否会带来额外的开销?对于我来说,每次我只能随机访问一个数据点并且不停地循环,而不是切片,这是hdf5使用的典型情况。 – Liang

+0

如果你在[无缓冲模式](https://stackoverflow.com/questions/20342772/buffered-and-unbuffered-inputs-in-c)中打开文件,那么你有相当直接的,原始的,低级别的访问文件。使用'fseek' /'fread',你可以从任何你想要的地方获取数据,随机存取。默认情况下,文件读取被缓冲,这可能会拖累性能,除非您正在进行线性读取。 – tadman

回答

0

问题的主要原因与HDF库无关。您没有映射HDF库告诉您与数据集对应的字节。

H5Dget_offset以字节为单位从文件的开始位置到相关数据集的开始处返回偏移量。但是你没有把这个值传递给mmap(2)。您计算的页面大小的倍数恰好低于实际的偏移量,然后使用作为您在mmap(2)调用中的文件偏移量。

相反的:

mmap(..., pa_offset); 

你应该有

mmap(..., offset); 

至于这是否将是任何更快。 HDF库很复杂。可能会有一些开销(边界检查,权限检查,其他库调用),但它也可能相当优化。判断内存映射是否更快的唯一合理方法是测量它。

+0

我认为'mmap'本身仍然需要每个Posix规范的'pa_offset'。错误在于返回的指针应该移回,例如'int * ptr =(int *)(addr + offset - pa_offset);'。实际上,直接在'mmap'中使用'offset'会在测试中出现分段错误。 – Liang

+0

@梁哇,老实说,我不知道'偏移量'必须是一个页面大小。我想我已经使用的实现不符合POSIX标准!您发布的解决方案与我尝试完成的事情完全相同,但顺从。 – bnaecker

+0

对。有趣的是,在我用小数据(15int)进行的测试中,因此可能很小的开销,“pa_offset”为零,因为实际的“偏移量”与页面大小相比太小。 – Liang

0

这是我自己对这个问题的回答。

继这个HDS实现后,我想出了一个我原来的代码中的错误,但是解决方案与@ bnaecker不同。

基本上,原来的mmap仍然需要pa_offset,根据mmap doc。但是返回的指针应该被移回,例如,

int * ptr = (int *) (addr + offset - pa_offset); 

对于以后的用户参考,我在这里粘贴三种类型访问方法的核心代码。特别是,tadman提到的setvbuf技巧可能会进一步提高随机访问的性能(虽然未经测试)。

FILE *fp; 

/* Get dataset offset within file */ 
file_id = H5Fopen(FNAME, H5F_ACC_RDONLY, H5P_DEFAULT); 
status = H5Fget_vfd_handle(file_id, H5P_DEFAULT, &fhandle); 
dataset_id = H5Dopen(file_id, "/dset", H5P_DEFAULT); 
offset = H5Dget_offset(dataset_id); 

/* Read through stdio */ 
fp = fopen(FNAME, "rb"); 
fseek(fp, offset, SEEK_SET); 
int x0[NX*NY]; 
fread(&x0, sizeof(int), NX*NY, fp); 

/* Get the file descriptor */ 
fd = *((int *)fhandle); 

/* Read through Posix */ 
int x1[NX*NY]; 
lseek(fd, offset, SEEK_SET); 
read(fd, x1, NX*NY*sizeof(int)); 

/* Read through mmap */ 
// page size-aligned offset for mmap 
pa_offset = offset & ~(sysconf(_SC_PAGE_SIZE) - 1); 
length = NX * NY * sizeof(int); 
addr = mmap(NULL, length + offset - pa_offset, PROT_READ, 
      MAP_PRIVATE, fd, pa_offset); 
// revert the align for correct access 
int * x2 = (int *) (addr + offset - pa_offset);