Написание и добавление массивов float к единственному набору данных в файле hdf5 в С++

Я обрабатываю количество файлов, каждая обработка файла будет выводить несколько тысяч массивов float, и я буду хранить данные всех файлов в одном огромном наборе данных в одном hdf5 для дальнейшей обработки,

В настоящее время я смущен тем, как добавить свои данные в файл hdf5. (комментарий в коде выше). В 2 для циклов выше, как вы можете видеть, я хочу добавить одномерный массив float в hdf5 за раз, а не как все это. Мои данные находятся в терабайтах, и мы можем добавлять эти данные только в файл.

Есть несколько вопросов:

  • Как добавить данные в этом случае? Какую функцию я должен использовать?
  • Прямо сейчас, у меня есть fdim [0] = 928347543, я попытался установить флаг бесконечности HDF5, но выполнение выполнения жалуется. Есть ли способ сделать это? Я не хочу вычислять данные, которые я имею каждый раз; есть ли способ просто продолжать добавлять данные, не заботясь о значении fdim?

Или это невозможно?

EDIT:

Я слежу за предложением Саймона, и в настоящее время здесь есть мой обновленный код:

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";    

Мой текущий выход:

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

В настоящее время я вижу, что установка элементов без ограничений с помощью H5Dset_extent все еще вызывает проблемы во время выполнения. (проблемная функция отмечена //COMPLAINS HERE в коде выше). Я уже получил данные chunk, как указано Саймоном, так что проблема здесь?

С другой стороны, без H5Dset_extent я могу просто написать тестовый массив из [0, 1, 2], но как мы можем сделать код выше вывода тестового массива в файл следующим образом:

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

Вспомните: это всего лишь тестовый массив, реальные данные больше, и я не могу удержать все это в ОЗУ, поэтому я должен поместить данные частично по первой части за раз.

ИЗМЕНИТЬ 2:

Я следовал за Саймоном. Вот критическая часть:

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);

Из вышесказанного я получил следующий вывод для hdf5:

0.4 0.5 0.6
  0   0   0

После дополнительного эксперимента с start2 и count2, я вижу, что эти переменные влияют только на начальный индекс и индекс инкремента для bufi. Он вообще не перемещает позицию индекса записи моего набора данных.

Напомним: окончательный результат должен быть:

0.1 0.2 0.3
0.4 0.5 0.6

Кроме того, он должен быть bufi вместо *bufi для H5Dwrite, Simon, потому что bufi дает мне полностью случайные числа.

ОБНОВЛЕНИЕ 3:

Для части выбора, предложенной Саймоном:

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

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

Они выдадут следующую ошибку:

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] должен быть {3,1}, а не {1,3}, я полагаю? И для start[2], если я не устанавливаю его как {0,0}, он всегда будет выкрикивать ошибку выше.

Вы уверены, что это правильно?

Ответы

Ответ 1

Как добавить данные в этом случае? Какую функцию я должен использовать?

Вы должны использовать гиперслои. Это то, что вам нужно написать только часть набора данных. Функция для этого H5Sselect_hyperslab. Используйте его на fd1 и используйте fd1 в качестве файла dataspace в своем вызове H5Dwrite.

Я попытался поставить флаг бесконечности HDF5, но выполнение выполнения жалуется.

Вам нужно создать chunked dataset, чтобы установить максимальный размер до бесконечности. Создайте список свойств создания набора данных и используйте H5Pset_layout, чтобы сделать его фрагментированным. Используйте H5Pset_chunk, чтобы установить размер куска. Затем создайте свой набор данных, используя этот список свойств.

Я не хочу вычислять данные, которые я имею каждый раз; есть ли способ просто просто добавлять данные, не заботясь о значении fdim?

Вы можете сделать две вещи:

  • Предварительно скопируйте окончательный размер, чтобы создать набор данных достаточно большой. Это похоже на то, что вы делаете.

  • Расширьте свой набор данных при использовании H5Dset_extent. Для этого вам нужно установить максимальные размеры до бесконечности, чтобы вам понадобился пакетный набор данных (см. Выше).

В обоих случаях вам нужно выбрать гиперссылку в файле dataspace в вашем вызове H5Dwrite (см. выше).

Прохождение рабочий код

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

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

int main()
{

Сначала создайте файл hdf5.

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

Затем создайте 2D-данные. Размер первого измерения неограничен. Мы первоначально устанавливаем его на 0, чтобы показать, как вы можете расширить набор данных на каждом шаге. Вы также можете установить его на размер первого буфера, который вы собираетесь писать, например. Размер второго измерения фиксирован.

    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;

Затем создайте список свойств создания набора данных. При использовании неограниченных размеров компоновку набора данных необходимо разбить. Выбор размера блока влияет на производительность, как во времени, так и на диске. Если куски очень маленькие, у вас будет много накладных расходов. Если они слишком велики, вы можете выделить пространство, которое вам не нужно, и ваши файлы могут оказаться слишком большими. Это пример игрушки, поэтому мы выберем куски одной строки.

    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;

Создайте набор данных.

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

Закрыть ресурсы. Теперь набор данных создан, поэтому нам больше не нужен список свойств. Нам больше не нужен файл dataspace, потому что, когда набор данных будет расширен, он станет недействительным, так как он все равно будет удерживать предыдущую степень. Поэтому нам все равно придется захватить обновленный файл dataspace.

    H5Pclose(plist);
    H5Sclose(file_space);

Теперь мы добавим два буфера в конец набора данных. Первая из них будет длиной две строки. Второй будет длиной в три строки.

Первый буфер

Мы создаем 2D-буфер (смежный по памяти, основной порядок строк). Мы будем выделять достаточно памяти для хранения 3 строк, чтобы мы могли повторно использовать буфер. Создадим массив указателей, чтобы мы могли использовать нотацию b[i][j] вместо buffer[i * ncols + j]. Это чисто эстетично.

    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];
    }

Начальные значения в буфере, которые должны быть записаны в наборе данных:

    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;

Мы создаем память, чтобы указать размер нашего буфера в памяти. Помните, что первый буфер имеет длину всего две строки.

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

Теперь нам нужно расширить набор данных. Мы устанавливаем начальный размер набора данных в 0x3, поэтому нам нужно его сначала расширить. Обратите внимание, что мы расширяем набор данных, а не его dataspace. Помните, что первый буфер имеет длину всего две строки.

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

Выберите гиперссылку в наборе данных файла.

    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;

Записать буфер в набор данных. mem_space и file_space теперь должны иметь одинаковое количество элементов. Обратите внимание, что buffer и &b[0][0] эквивалентны.

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

Теперь мы можем закрыть файл dataspace. Мы могли бы закрыть память dataspace сейчас и создать новую для второго буфера, но мы просто обновим его размер.

    H5Sclose(file_space);

Второй буфер

Новые значения в буфере, добавляемые к набору данных:

    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;

Измените размер памяти, чтобы указать новый размер нашего буфера. Второй буфер длиной три строки.

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

Расширить набор данных. Заметим, что в этом простом примере мы знаем, что 2 + 3 = 5. В общем, вы можете прочитать текущую степень из файла dataspace и добавьте к нему нужное количество строк.

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

Выберите гиперссылку в наборе данных файла. Опять же в этом простом примере мы знаем, что 0 + 2 = 2. В общем, вы можете прочитать текущую степень из файла dataspace. Второй буфер длиной три строки.

    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;

Добавить буфер в набор данных

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

Конец: пусть все ресурсы будут закрыты:

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

NB: Я удалил предыдущие обновления, потому что ответ был слишком длинным. Если вам интересно, просмотрите историю.