Zapis i dołączanie tablic float do jedynego zbioru danych w pliku hdf5 w C++

Przetwarzam liczbę plików, każde przetwarzanie pliku wyprowadzi kilka tysięcy tablic float i będę przechowywać dane wszystkich plików w jednym ogromnym zestawie danych w jednym hdf5 do dalszego przetwarzania.

Rzecz w tym, że obecnie jestem zdezorientowany, jak dołączyć moje dane do pliku hdf5. (komentarz w kodzie powyżej) w 2 dla pętli powyżej, jak widać, chcę dołączyć 1 wymiarową tablicę float do hdf5 na raz, a nie jako całość. Moje dane jest w terabajtach i możemy tylko dołączyć dane do pliku.

Jest kilka pytań:

  1. Jak dołączyć dane w tym przypadku? Jakiej funkcji muszę użyć?
  2. w tej chwili mam fdim[0] = 928347543, próbowałem umieścić flagę nieskończoności HDF5, ale wykonanie runtime narzeka. Jest na to sposób? Nie chcę obliczać danych, które mam za każdym razem; czy jest sposób, aby po prostu kontynuować dodawanie danych, bez dbania o wartość fdim?
Czy to niemożliwe?

EDIT:

Podążałem za sugestią Simona, a obecnie oto mój zaktualizowany kod:

hid_t desFi5;
hid_t fid1;
hid_t propList;
hsize_t fdim[2];

desFi5 = H5Fcreate(saveFilePath, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);

fdim[0] = 3;
fdim[1] = 1;//H5S_UNLIMITED;

fid1 = H5Screate_simple(2, fdim, NULL);

cout << "----------------------------------Space done\n";

propList = H5Pcreate( H5P_DATASET_CREATE);

H5Pset_layout( propList, H5D_CHUNKED );

int ndims = 2;
hsize_t chunk_dims[2];
chunk_dims[0] = 3;
chunk_dims[1] = 1;

H5Pset_chunk( propList, ndims, chunk_dims );

cout << "----------------------------------Property done\n";

hid_t dataset1 = H5Dcreate( desFi5, "des", H5T_NATIVE_FLOAT, fid1, H5P_DEFAULT, propList, H5P_DEFAULT);

cout << "----------------------------------Dataset done\n";

bufi = new float*[1];
bufi[0] = new float[3];
bufi[0][0] = 0;
bufi[0][1] = 1;
bufi[0][2] = 2;

//hyperslab
hsize_t start[2] = {0,0};
hsize_t stride[2] = {1,1};
hsize_t count[2] = {1,1};
hsize_t block[2] = {1,3};

H5Sselect_hyperslab( fid1, H5S_SELECT_OR, start, stride, count, block);     
cout << "----------------------------------hyperslab done\n";   

H5Dwrite(dataset1, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, *bufi);

fdim[0] = 3;
fdim[1] = H5S_UNLIMITED;    // COMPLAINS HERE
H5Dset_extent( dataset1, fdim );

cout << "----------------------------------extent done\n";

//hyperslab2
hsize_t start2[2] = {1,0};
hsize_t stride2[2] = {1,1};
hsize_t count2[2] = {1,1};
hsize_t block2[2] = {1,3};

H5Sselect_hyperslab( fid1, H5S_SELECT_OR, start2, stride2, count2, block2);     
cout << "----------------------------------hyperslab2 done\n";  

H5Dwrite(dataset1, H5T_NATIVE_FLOAT, H5S_ALL, H5S_ALL, H5P_DEFAULT, *bufi);

cout << "----------------------------------H5Dwrite done\n";        
H5Dclose(dataset1);
cout << "----------------------------------dataset closed\n";   
H5Pclose( propList );   
cout << "----------------------------------property list closed\n"; 
H5Sclose(fid1); 
cout << "----------------------------------dataspace fid1 closed\n";    
H5Fclose(desFi5);       
cout << "----------------------------------desFi5 closed\n";    

Moje obecne wyjście to:

bash-3.2$ ./hdf5AppendTest.out
----------------------------------Space done
----------------------------------Property done
----------------------------------Dataset done
----------------------------------hyperslab done
HDF5-DIAG: Error detected in HDF5 (1.8.10) thread 0:
  #000: /home/hdftest/snapshots-bin-hdf5_1_8_10/current/src/H5D.c line 1103 in H5Dset_extent(): unable to set extend dataset
    major: Dataset
    minor: Unable to initialize object
  #001: /home/hdftest/snapshots-bin-hdf5_1_8_10/current/src/H5Dint.c line 2179 in H5D__set_extent(): unable to modify size of data space
    major: Dataset
    minor: Unable to initialize object
  #002: /home/hdftest/snapshots-bin-hdf5_1_8_10/current/src/H5S.c line 1874 in H5S_set_extent(): dimension cannot exceed the existing maximal size (new: 18446744073709551615 max: 1)
    major: Dataspace
    minor: Bad value
----------------------------------extent done
----------------------------------hyperslab2 done
----------------------------------H5Dwrite done
----------------------------------dataset closed
----------------------------------property list closed
----------------------------------dataspace fid1 closed
----------------------------------desFi5 closed

Obecnie widzę, że ustawienie rzeczy w unlimited za pomocą H5Dset_extent nadal powoduje problem podczas wykonywania. (problematyczna funkcja jest oznaczona //COMPLAINS HERE w powyższym kodzie.) Mam już dane kawałkowe określone przez Simona, więc w czym problem?

Na drugiej h5dset_extent, mogę napisać tablicę testową [0, 1, 2], ale jak możemy sprawić, że kod nad wyjściem stanie się tablicą testową do pliku w taki sposób:

[0, 1, 2]
[0, 1, 2]
[0, 1, 2]
[0, 1, 2]
...
...

Przypomnijmy: to jest tylko Tablica testowa, prawdziwe dane są większe i nie mogę trzymać całości w pamięci RAM, więc muszę umieszczać dane po części po części na raz.

EDIT 2:

Posłuchałem więcej sugestii Simona. Oto część krytyczna:
hsize_t n = 3, p = 1;
float *bufi_data = new float[n * p];
float ** bufi = new float*[n];
for (hsize_t i = 0; i < n; ++i){
    bufi[i] = &bufi_data[i * n];
}

bufi[0][0] = 0.1;
bufi[0][1] = 0.2;
bufi[0][2] = 0.3;

//hyperslab
hsize_t start[2] = {0,0};
hsize_t count[2] = {3,1};

H5Sselect_hyperslab( fid1, H5S_SELECT_SET, start, NULL, count, NULL);
cout << "----------------------------------hyperslab done\n";   

H5Dwrite(dataset1, H5T_NATIVE_FLOAT, H5S_ALL, fid1, H5P_DEFAULT, *bufi);

bufi[0][0] = 0.4;
bufi[0][1] = 0.5;
bufi[0][2] = 0.6;

hsize_t fdimNew[2];
fdimNew[0] = 3;
fdimNew[1] = 2;
H5Dset_extent( dataset1, fdimNew );

cout << "----------------------------------extent done\n";

//hyperslab2
hsize_t start2[2] = {0,0}; //PROBLEM
hsize_t count2[2] = {3,1};

H5Sselect_hyperslab( fid1, H5S_SELECT_SET, start2, NULL, count2, NULL);     
cout << "----------------------------------hyperslab2 done\n";  

H5Dwrite(dataset1, H5T_NATIVE_FLOAT, H5S_ALL, fid1, H5P_DEFAULT, *bufi);

Z powyższego, otrzymałem następujące wyjście dla hdf5:

0.4 0.5 0.6
  0   0   0

Po dalszym eksperymencie z start2 i count2, widzę, że te zmienne wpływają tylko na indeks początkowy i indeks inkrementacyjny dla bufi. W ogóle nie przesuwa pozycji indeksu zapisu mojego zbioru danych.

Przypomnienie: ostateczny wynik musi być:

0.1 0.2 0.3
0.4 0.5 0.6

Również, to musi być bufi zamiast *bufi dla H5Dwrite, Simon, ponieważ bufi daje mi całkowicie losowe liczby.

UPDATE 3:

Do części selekcyjnej sugerowanej przez Simon:

hsize_t start[2] = {0, 0};
hsize_t count[2] = {1, 3};

hsize_t start[2] = {1, 0};
hsize_t count[2] = {1, 3};

Będą wyświetlać następujący błąd:

HDF5-DIAG: Error detected in HDF5 (1.8.10) thread 0:
  #000: /home/hdftest/snapshots-bin-hdf5_1_8_10/current/src/H5Dio.c line 245 in H5Dwrite(): file selection+offset not within extent
    major: Dataspace
    minor: Out of range

count[2] powinno być {3,1}, a nie {1,3}, jak sądzę? I dla start[2], jeśli nie ustawię go jako {0,0}, zawsze wykrzyczy powyższy błąd.

Jesteś pewien, że to prawda?
Author: Guillaume Jacquenot, 2013-03-13

1 answers

Jak dołączyć dane w tym przypadku? Jakiej funkcji muszę użyć?

Musisz użyć hyperslabs . To jest to, co trzeba napisać tylko część zbioru danych. Funkcją do tego celu jest H5Sselect_hyperslab. Użyj go na fd1 i użyj fd1 jako przestrzeni danych pliku w wywołaniu H5Dwrite.

Próbowałem umieścić flagę nieskończoności HDF5 w, ale wykonanie runtime narzeka.

Musisz utworzyć zbiór danych W aby móc ustawić jego maksymalny rozmiar na nieskończoność. Utwórz listę właściwości tworzenia zbiorów danych i użyj H5Pset_layout żeby było grube. Użycie H5Pset_chunk aby ustawić rozmiar kawałka. Następnie utwórz swój zestaw danych za pomocą tej listy właściwości.

Nie chcę obliczać danych, które mam za każdym razem; czy jest sposób, aby po prostu kontynuować dodawanie danych, bez dbania o wartość fdim?

You can do two rzeczy:

  1. Wstępnie Oblicz ostateczny rozmiar, aby można było utworzyć zestaw danych wystarczająco duży. Wygląda na to, że to właśnie robisz.

  2. Rozszerzaj swój zestaw danych podczas korzystania z niego H5Dset_extent. W tym celu musisz ustawić maksymalne wymiary na nieskończoność, więc potrzebujesz chunked dataset(patrz powyżej).

W obu przypadkach należy wybrać hyperslab w przestrzeni danych pliku w wywołaniu H5Dwrite (patrz wyżej).

Walkthrough working kod

#include <iostream>
#include <hdf5.h>

// Constants
const char saveFilePath[] = "test.h5";
const hsize_t ndims = 2;
const hsize_t ncols = 3;

int main()
{

Najpierw Utwórz plik hdf5.

    hid_t file = H5Fcreate(saveFilePath, H5F_ACC_TRUNC, H5P_DEFAULT, H5P_DEFAULT);
    std::cout << "- File created" << std::endl;

Następnie utwórz przestrzeń danych 2D. Wielkość pierwszego wymiaru jest nieograniczona. Ustawiamy go początkowo na 0, aby pokazać, w jaki sposób można rozszerzyć zbiór danych na każdym kroku. Możesz również ustawić go na przykład na rozmiar pierwszego bufora, który zamierzasz zapisać. Rozmiar drugiego wymiaru jest stały.

    hsize_t dims[ndims] = {0, ncols};
    hsize_t max_dims[ndims] = {H5S_UNLIMITED, ncols};
    hid_t file_space = H5Screate_simple(ndims, dims, max_dims);
    std::cout << "- Dataspace created" << std::endl;

Następnie utwórz listę właściwości tworzenia zestawu danych. Układ zbioru danych musi być chunked podczas korzystania z nieograniczonej wymiary. Wybór rozmiaru kawałka wpływa na wydajność, zarówno w czasie, jak i przestrzeni dyskowej. Jeśli kawałki są bardzo małe, będziesz miał dużo nad głową. Jeśli są one zbyt duże, możesz przydzielić miejsce, którego nie potrzebujesz, a Twoje pliki mogą być zbyt duże. Jest to przykład zabawki, więc wybierzemy kawałki jednej linii.

    hid_t plist = H5Pcreate(H5P_DATASET_CREATE);
    H5Pset_layout(plist, H5D_CHUNKED);
    hsize_t chunk_dims[ndims] = {2, ncols};
    H5Pset_chunk(plist, ndims, chunk_dims);
    std::cout << "- Property list created" << std::endl;

Utwórz zbiór danych.

    hid_t dset = H5Dcreate(file, "dset1", H5T_NATIVE_FLOAT, file_space, H5P_DEFAULT, plist, H5P_DEFAULT);
    std::cout << "- Dataset 'dset1' created" << std::endl;

Zamknij zasoby. Zbiór danych jest teraz tworzony, więc nie potrzebujemy już listy właściwości. Nie potrzebujemy pliku. dataspace już, ponieważ gdy zbiór danych zostanie rozszerzony, stanie się nieważny, ponieważ nadal będzie trzymał poprzedni zakres. Więc i tak będziemy musieli pobrać zaktualizowaną przestrzeń danych pliku.

    H5Pclose(plist);
    H5Sclose(file_space);

Dodamy teraz dwa bufory do końca zbioru danych. Pierwsza będzie miała dwie linie. Druga będzie miała trzy linie.

Pierwszy bufor

Tworzymy bufor 2D (sąsiadujący w pamięci, rząd główny). Przydzielimy wystarczającą ilość pamięci do przechowywania 3 linii, więc może ponownie użyć bufora. Stwórzmy tablicę wskaźników, abyśmy mogli użyć notacji b[i][j] zamiast buffer[i * ncols + j]. To jest czysto estetyczne.

    hsize_t nlines = 3;
    float *buffer = new float[nlines * ncols];
    float **b = new float*[nlines];
    for (hsize_t i = 0; i < nlines; ++i){
        b[i] = &buffer[i * ncols];
    }

Początkowe wartości w buforze do zapisania w zbiorze danych:

    b[0][0] = 0.1;
    b[0][1] = 0.2;
    b[0][2] = 0.3;
    b[1][0] = 0.4;
    b[1][1] = 0.5;
    b[1][2] = 0.6;

Tworzymy przestrzeń danych pamięci, aby wskazać rozmiar naszego bufora w pamięci. Pamiętaj, że pierwszy bufor ma tylko dwie linie.

    dims[0] = 2;
    dims[1] = ncols;
    hid_t mem_space = H5Screate_simple(ndims, dims, NULL);
    std::cout << "- Memory dataspace created" << std::endl;

Musimy teraz rozszerzyć zbiór danych. Ustawiamy początkowy rozmiar zbioru danych na 0x3, dlatego musimy go najpierw rozszerzyć. Uwaga że rozszerzamy sam zbiór danych, a nie jego przestrzeń danych. Pamiętaj, że pierwszy bufor ma tylko dwie linie.

    dims[0] = 2;
    dims[1] = ncols;
    H5Dset_extent(dset, dims);
    std::cout << "- Dataset extended" << std::endl;

Wybierz hyperslab na zbiorze danych pliku.

    file_space = H5Dget_space(dset);
    hsize_t start[2] = {0, 0};
    hsize_t count[2] = {2, ncols};
    H5Sselect_hyperslab(file_space, H5S_SELECT_SET, start, NULL, count, NULL);
    std::cout << "- First hyperslab selected" << std::endl;

Zapis bufora do zbioru danych. mem_space i file_space powinny teraz mieć taką samą liczbę wybranych elementów. Zauważ, że buffer i &b[0][0] są równoważne.

    H5Dwrite(dset, H5T_NATIVE_FLOAT, mem_space, file_space, H5P_DEFAULT, buffer);
    std::cout << "- First buffer written" << std::endl;

Możemy teraz zamknąć przestrzeń danych pliku. Możemy teraz zamknąć przestrzeń danych pamięci i utworzyć nowy dla drugiego bufora, ale po prostu zaktualizujemy jego rozmiar.

    H5Sclose(file_space);

Drugi bufor

Nowe wartości w buforze, które mają być dołączone do zbioru danych:

    b[0][0] = 1.1;
    b[0][1] = 1.2;
    b[0][2] = 1.3;
    b[1][0] = 1.4;
    b[1][1] = 1.5;
    b[1][2] = 1.6;
    b[2][0] = 1.7;
    b[2][1] = 1.8;
    b[2][2] = 1.9;

Zmień rozmiar przestrzeni danych pamięci, aby wskazać nowy Rozmiar naszego bufora. Drugi bufor ma trzy linie długości.

    dims[0] = 3;
    dims[1] = ncols;
    H5Sset_extent_simple(mem_space, ndims, dims, NULL);
    std::cout << "- Memory dataspace resized" << std::endl;

Rozszerz zbiór danych. Zauważ, że w tym prostym przykładzie wiemy, że 2 + 3 = 5. Ogólnie można odczytać bieżący zakres z pliku dataspace i dodaj do niego żądaną liczbę linii.

    dims[0] = 5;
    dims[1] = ncols;
    H5Dset_extent(dset, dims);
    std::cout << "- Dataset extended" << std::endl;

Wybierz hyperslab na pliku zestaw danych. Ponownie w tym prostym przykładzie wiemy, że 0 + 2 = 2. Ogólnie rzecz biorąc, można odczytać bieżący zakres z przestrzeni danych pliku. Drugi bufor ma trzy linie długości.

    file_space = H5Dget_space(dset);
    start[0] = 2;
    start[1] = 0;
    count[0] = 3;
    count[1] = ncols;
    H5Sselect_hyperslab(file_space, H5S_SELECT_SET, start, NULL, count, NULL);
    std::cout << "- Second hyperslab selected" << std::endl;

Dołączenie bufora do zbioru danych

    H5Dwrite(dset, H5T_NATIVE_FLOAT, mem_space, file_space, H5P_DEFAULT, buffer);
    std::cout << "- Second buffer written" << std::endl;

Koniec: zamknijmy wszystkie zasoby:

    delete[] b;
    delete[] buffer;
    H5Sclose(file_space);
    H5Sclose(mem_space);
    H5Dclose(dset);
    H5Fclose(file);
    std::cout << "- Resources released" << std::endl;
}

NB: usunąłem poprzednie aktualizacje, ponieważ odpowiedź była zbyt długa. Jeśli jesteś zainteresowany, Przejrzyj historię.

 34
Author: Simon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-03-20 21:15:07