2017-10-08 58 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);