Skip to content

Commit 1501238

Browse files
committed
Avoid memory allocation with H5Sselect_hyperslab
Implement `HighFive::RegularHyperSlabNoMalloc<Rank>` that takes hyperslab variables in stack space. Do not construct `std::vector<hsize_t>`. Similarly, implement `::select(RegularHyperSlabNoMalloc<>)` such that it is free of `operator new[]` calls before invoking `H5Sselect_hyperslab`. Ensure that when hyperslab offset and range are compile-time constants, compiler (with arguments `-O3 -flto`) will inline everything without any `operator new[]` statements.
1 parent c1070f3 commit 1501238

3 files changed

Lines changed: 67 additions & 23 deletions

File tree

include/highfive/bits/H5Slice_traits.hpp

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010

1111
#include <cstdlib>
1212
#include <vector>
13+
#if __cplusplus >= 201703L
14+
#include <optional>
15+
#endif
1316

1417
#include "H5_definitions.hpp"
1518
#include "H5Utils.hpp"
@@ -108,6 +111,45 @@ struct RegularHyperSlab {
108111
std::vector<hsize_t> block;
109112
};
110113

114+
#if __cplusplus >= 201703L
115+
template<size_t Rank>
116+
struct RegularHyperSlabNoMalloc {
117+
constexpr size_t rank() const {
118+
return Rank;
119+
}
120+
121+
/// Dimensions when all gaps are removed.
122+
constexpr std::array<size_t, Rank> packedDims() const {
123+
auto dims = std::array<size_t, Rank>{};
124+
125+
for (size_t i = 0; i < Rank; ++i) {
126+
(*dims)[i] = (*count)[i] * (block ? (*block)[i] : 1);
127+
}
128+
129+
return dims;
130+
}
131+
132+
DataSpace apply(const DataSpace& space_) const {
133+
auto space = space_.clone();
134+
const auto error_code = H5Sselect_hyperslab(space.getId(),
135+
H5S_SELECT_SET,
136+
offset ? offset->data() : nullptr,
137+
stride ? stride->data() : nullptr,
138+
count ? count->data() : nullptr,
139+
block ? block->data() : nullptr);
140+
141+
if (error_code < 0) {
142+
HDF5ErrMapper::ToException<DataSpaceException>("Unable to select hyperslab");
143+
}
144+
return space;
145+
}
146+
std::optional<std::array<hsize_t, Rank>> offset{std::nullopt};
147+
std::optional<std::array<hsize_t, Rank>> count{std::nullopt};
148+
std::optional<std::array<hsize_t, Rank>> stride{std::nullopt};
149+
std::optional<std::array<hsize_t, Rank>> block{std::nullopt};
150+
};
151+
#endif
152+
111153
class HyperSlab {
112154
public:
113155
HyperSlab() {
@@ -416,6 +458,14 @@ class ProductSet {
416458
friend class SliceTraits;
417459
};
418460

461+
#if __cplusplus >= 202002L
462+
template <class T>
463+
concept HyperSlabInterface = requires(const T& t, const DataSpace& ds) {
464+
{ t.apply(ds) } -> std::same_as<DataSpace>;
465+
};
466+
#else
467+
#define HyperSlabInterface class
468+
#endif
419469

420470
template <typename Derivate>
421471
class SliceTraits {
@@ -428,7 +478,8 @@ class SliceTraits {
428478
/// nicely into a multi-dimensional array, but only a subset of such an array.
429479
///
430480
/// Therefore, the only memspaces supported for general hyperslabs are one-dimensional arrays.
431-
Selection select(const HyperSlab& hyper_slab) const;
481+
template <HyperSlabInterface H>
482+
Selection select(const H& hyper_slab) const;
432483

433484
///
434485
/// \brief Select an \p hyper_slab in the current Slice/Dataset.

include/highfive/bits/H5Slice_traits_misc.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,8 @@ inline Selection SliceTraits<Derivate>::select(const HyperSlab& hyper_slab,
229229
}
230230

231231
template <typename Derivate>
232-
inline Selection SliceTraits<Derivate>::select(const HyperSlab& hyper_slab) const {
232+
template <HyperSlabInterface H>
233+
inline Selection SliceTraits<Derivate>::select(const H& hyper_slab) const {
233234
const auto& slice = static_cast<const Derivate&>(*this);
234235
auto filespace = slice.getSpace();
235236
filespace = hyper_slab.apply(filespace);
@@ -240,7 +241,6 @@ inline Selection SliceTraits<Derivate>::select(const HyperSlab& hyper_slab) cons
240241
return detail::make_selection(memspace, filespace, details::get_dataset(slice));
241242
}
242243

243-
244244
template <typename Derivate>
245245
inline Selection SliceTraits<Derivate>::select(const std::vector<size_t>& offset,
246246
const std::vector<size_t>& count,

src/examples/select_partial_dataset_cpp11.cpp

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,43 +7,36 @@
77
*
88
*/
99
#include <functional>
10-
#include <iostream>
1110
#include <string>
1211
#include <vector>
1312

1413
#include <highfive/highfive.hpp>
1514

16-
const std::string file_name("select_partial_example.h5");
17-
const std::string dataset_name("dset");
18-
1915
// Create a dataset name "dset" of double 4x6
2016
//
2117
int main(void) {
2218
using namespace HighFive;
2319

20+
constexpr char file_name[]{"select_partial_example.h5"};
21+
constexpr char dataset_name[]{"dset"};
22+
2423
// Create a new file using the default property lists.
2524
File file(file_name, File::ReadWrite | File::Create | File::Truncate);
2625

27-
// we have some example values in a 2D vector 2x5
28-
std::vector<std::vector<double>> values = {{1.0, 2.0, 4.0, 8.0, 16.0},
29-
{32.0, 64.0, 128.0, 256.0, 512.0}};
30-
3126
// let's create a dataset of this size
32-
DataSet dataset = file.createDataSet<double>(dataset_name, DataSpace::From(values));
27+
constexpr size_t H = 2;
28+
constexpr size_t W = 5;
29+
constexpr std::array<double, W * H> values = {
30+
1.0, 2.0, 4.0, 8.0, 16.0, 32.0, 64.0, 128.0, 256.0, 512.0};
31+
DataSet dataset = file.createDataSet<double>(dataset_name, {H, W});
3332
// and write them
34-
dataset.write(values);
33+
dataset.write_raw(values.data());
3534

3635
// now we read back 2x2 values after an offset of 0x2
37-
std::vector<std::vector<double>> result;
38-
dataset.select({0, 2}, {2, 2}).read(result);
39-
40-
// we print out 4 values
41-
for (auto i: result) {
42-
for (auto j: i) {
43-
std::cout << " " << j;
44-
}
45-
std::cout << "\n";
46-
}
36+
using Slab = HighFive::RegularHyperSlabNoMalloc<2>;
37+
using Slice = std::array<hsize_t, 2>;
38+
std::vector<double> result(4);
39+
dataset.select(Slab{Slice{0, 2}, Slice{2, 2}}).read_raw(result.data());
4740

4841
return 0; // successfully terminated
4942
}

0 commit comments

Comments
 (0)