我正在处理文件数量,文件的每个处理将输出数千个浮点数组,我将将所有文件的数据存储在单个hdf5 中的一个巨大数据集中,以便进一步处理。
目前我很困惑如何将我的数据附加到hdf5文件中。 (在上面的代码中注释)在上面的2个for循环中,正如你所看到的,我想一次将1维float数组追加到hdf5中,而不是整个东西。我的数据是以兆兆字节为单位,我们只能将数据附加到文件中。
有几个问题:
或者这不可能吗?
编辑:
我一直在关注Simon的建议,目前这里是我的更新代码:
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
标记。)我已经得到了Simon指定的块数据,那么这里的问题是什么?
另一方面,没有H5Dset_extent,我可以编写一个[0,1,2]的测试数组就好了,但是我们怎样才能将输出上面的代码作为测试数组放到文件中:
[0, 1, 2]
[0, 1, 2]
[0, 1, 2]
[0, 1, 2]
...
...
回想一下:这只是一个测试数组,实际数据更大,我无法将整个数据保存在RAM中,所以我必须一次一个地放入数据。
编辑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
,Simon必须是*bufi
而不是H5Dwrite
,因为bufi
会给我完全随机的数字。
更新3:
对于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};
这些将发出以下错误:
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}
,它将始终大喊上述错误。
你确定这是正确的吗?
答案 0 :(得分:35)
如何在这种情况下附加数据?我必须使用什么样的功能?
您必须使用 hyperslabs 。这就是您只需编写数据集的一部分所需的内容。
执行此操作的功能是H5Sselect_hyperslab
。在fd1
上使用它,并在fd1
来电中使用H5Dwrite
作为文件数据空间。
我已尝试将HDF5的infinity标志放入,但运行时执行会抱怨。
您需要创建分块数据集,以便能够将其最大大小设置为无穷大。创建数据集创建属性列表并使用H5Pset_layout
将其分块。使用H5Pset_chunk
设置块大小。然后使用此属性列表创建数据集。
我不想每次计算出的数据;有没有办法简单地继续添加数据,而不关心
fdim
的价值?
你可以做两件事:
预先计算最终大小,以便您可以创建足够大的数据集。看起来这就是你在做什么。
使用H5Dset_extent
扩展数据集。为此,您需要将最大尺寸设置为无穷大,这样您就需要一个分块数据集(见上文)。
在这两种情况下,您需要在H5Dwrite
调用中的文件数据空间中选择hyperslab(参见上文)。
#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;
关闭资源。现在创建了数据集,因此我们不再需要属性列表。 我们不再需要文件数据空间,因为当数据集将被扩展时,它将变为无效,因为它仍将保持先前的范围。 因此,无论如何我们都必须获取更新的文件数据空间。
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,因此我们需要首先扩展它。 请注意,我们扩展数据集本身,而不是数据空间。 请记住,第一个缓冲区只有两行。
dims[0] = 2;
dims[1] = ncols;
H5Dset_extent(dset, dims);
std::cout << "- Dataset extended" << std::endl;
在文件数据集上选择hyperslab。
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;
我们现在可以关闭文件数据空间。 我们现在可以关闭内存数据空间并为第二个缓冲区创建一个新数据空间, 但我们只会更新它的大小。
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。 通常,您可以从文件数据空间读取当前范围 并为其添加所需的行数。
dims[0] = 5;
dims[1] = ncols;
H5Dset_extent(dset, dims);
std::cout << "- Dataset extended" << std::endl;
在文件数据集上选择hyperslab。 再次在这个简单的例子中,我们知道0 + 2 = 2。 通常,您可以从文件数据空间读取当前范围。 第二个缓冲区长三行。
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: 我删除了之前的更新,因为答案太长了。如果您有兴趣,请浏览历史记录。