Как я могу записать большой многомерный массив в файл HDF5 по частям?
Я использую HDF5DotNet в C#, и у меня есть очень большой массив (несколько ГБ), который я хочу записать в файл HDF5. Он слишком велик, чтобы хранить все это в памяти, поэтому я генерирую его области за раз и хочу выписать их, но все равно выгляжу как один большой массив при обратном чтении. Я знаю, что это возможно с HDF5, но документация по.NET API несколько скудна.
Я написал небольшой пример кода с массивом 5 x 3, заполненным значениями 1..15:
const int ROWS = 5;
const int COLS = 3;
static void Main(string[] args)
{
WriteWholeArray();
WriteArrayByRows();
ushort[,] array = ReadWholeArray();
}
static void WriteWholeArray()
{
H5FileId h5 = H5F.create(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "test.h5"), H5F.CreateMode.ACC_TRUNC);
H5DataSpaceId dsi = H5S.create_simple(2, new long[] { ROWS, COLS });
H5DataSetId dataset = H5D.create(h5, "array", new H5DataTypeId(H5T.H5Type.NATIVE_USHORT), dsi);
ushort[,] array = new ushort[ROWS, COLS];
ushort value = 1;
for(int i = 0; i < array.GetLength(0); i++)
{
for (int j = 0; j < array.GetLength(1); j++)
{
array[i, j] = value++;
}
}
H5D.write<ushort>(dataset, new H5DataTypeId(H5T.H5Type.NATIVE_USHORT), new H5Array<ushort>(array));
H5D.close(dataset);
H5F.close(h5);
}
static void WriteArrayByRows()
{
H5FileId h5 = H5F.create(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "test.h5"), H5F.CreateMode.ACC_TRUNC);
H5DataSpaceId dsi = H5S.create_simple(2, new long[] { ROWS, COLS });
H5DataSetId dataset = H5D.create(h5, "array", new H5DataTypeId(H5T.H5Type.NATIVE_USHORT), dsi);
ushort[,] array = new ushort[ROWS, COLS];
ushort value = 1;
for (int i = 0; i < array.GetLength(0); i++)
{
for (int j = 0; j < array.GetLength(1); j++)
{
array[i, j] = value++;
}
}
for(int i = 0; i < array.GetLength(0); i++)
{
H5S.selectHyperslab(dsi, H5S.SelectOperator.SET, new long[] { i, 0 }, new long[] { 1, array.GetLength(1) });
ushort[,] row = new ushort[1, array.GetLength(1)];
for(int j = 0; j < array.GetLength(1); j++)
{
row[0, j] = array[i, j];
}
H5D.write<ushort>(dataset, new H5DataTypeId(H5T.H5Type.NATIVE_USHORT), new H5Array<ushort>(row));
}
H5D.close(dataset);
H5F.close(h5);
}
static ushort[,] ReadWholeArray()
{
H5FileId h5 = H5F.open(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "test.h5"), H5F.OpenMode.ACC_RDONLY);
ushort[,] array = new ushort[ROWS, COLS];
H5Array<ushort> h5_array = new H5Array<ushort>(array);
H5DataSetId dataset = H5D.open(h5, "array");
H5D.read<ushort>(dataset, new H5DataTypeId(H5T.H5Type.NATIVE_USHORT), h5_array);
H5D.close(dataset);
H5F.close(h5);
return (array);
}
Когда я пишу весь массив сразу, он читает обратно в порядке. Когда я пытаюсь писать по строкам, у массива, в котором я читаю, есть некоторые правильные значения (в неправильных элементах), некоторые нули и некоторые сумасшедшие значения (например, 43440). Может кто-нибудь показать мне, как это сделать правильно?
1 ответ
Я понял. Очевидно, что когда вы пишете гиперсписки массива, вам нужно второе пространство данных, соответствующее массиву в памяти, который вы пишете. Вот исправленная функция:
static void WriteArrayByRows()
{
H5FileId h5 = H5F.create(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "test.h5"), H5F.CreateMode.ACC_TRUNC);
H5DataSpaceId dsi = H5S.create_simple(2, new long[] { ROWS, COLS });
H5DataSetId dataset = H5D.create(h5, "array", new H5DataTypeId(H5T.H5Type.NATIVE_USHORT), dsi);
ushort[,] array = new ushort[ROWS, COLS];
ushort value = 1;
for (int i = 0; i < array.GetLength(0); i++)
{
for (int j = 0; j < array.GetLength(1); j++)
{
array[i, j] = value++;
}
}
for (int i = 0; i < array.GetLength(0); i++)
{
H5S.selectHyperslab(dsi, H5S.SelectOperator.SET, new long[] { i, 0 }, new long[] { 1, array.GetLength(1) });
H5DataSpaceId dsi2 = H5S.create_simple(2, new long[] { 1, array.GetLength(1) }); // added
ushort[,] row = new ushort[1, array.GetLength(1)];
for (int j = 0; j < array.GetLength(1); j++)
{
row[0, j] = array[i, j];
}
H5PropertyListId pli = new H5PropertyListId(H5P.Template.DEFAULT); // added
H5D.write<ushort>(dataset, new H5DataTypeId(H5T.H5Type.NATIVE_USHORT), dsi2, dsi, pli, new H5Array<ushort>(row)); // modified
}
H5D.close(dataset);
H5F.close(h5);
}
Я также обнаружил, что чанкинг очень полезен для получения приличной производительности при написании моего большого массива, вот пример этого:
H5PropertyListId pli = H5P.create(H5P.PropertyListClass.DATASET_CREATE); // added
H5P.setChunk(pli, new long[] { 1, COLS }); // added
H5DataSetId dataset = H5D.create(h5, "array", new H5DataTypeId(H5T.H5Type.NATIVE_USHORT), dsi, H5P.create(H5P.PropertyListClass.LINK_CREATE), pli, H5P.create(H5P.PropertyListClass.DATASET_ACCESS)); // modified