Skip to content

Commit 62cd478

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 62cd478

File tree

3 files changed

+68
-1
lines changed

3 files changed

+68
-1
lines changed

include/highfive/bits/H5Slice_traits.hpp

Lines changed: 45 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.data(),
137+
stride ? stride->data() : nullptr,
138+
count.data(),
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::array<hsize_t, Rank> offset{};
147+
std::array<hsize_t, Rank> count{};
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,7 +458,6 @@ class ProductSet {
416458
friend class SliceTraits;
417459
};
418460

419-
420461
template <typename Derivate>
421462
class SliceTraits {
422463
public:
@@ -430,6 +471,9 @@ class SliceTraits {
430471
/// Therefore, the only memspaces supported for general hyperslabs are one-dimensional arrays.
431472
Selection select(const HyperSlab& hyper_slab) const;
432473

474+
template <size_t Rank>
475+
Selection select(const RegularHyperSlabNoMalloc<Rank>& hyper_slab) const;
476+
433477
///
434478
/// \brief Select an \p hyper_slab in the current Slice/Dataset.
435479
///

include/highfive/bits/H5Slice_traits_misc.hpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,24 @@ inline Selection SliceTraits<Derivate>::select(const HyperSlab& hyper_slab) cons
240240
return detail::make_selection(memspace, filespace, details::get_dataset(slice));
241241
}
242242

243+
template <typename Derivate>
244+
template <size_t Rank>
245+
inline Selection SliceTraits<Derivate>::select(
246+
const RegularHyperSlabNoMalloc<Rank>& hyper_slab) const {
247+
const auto& slice = static_cast<const Derivate&>(*this);
248+
auto filespace = slice.getSpace();
249+
filespace = hyper_slab.apply(filespace);
250+
251+
const auto packed_dims = hyper_slab.packedDims();
252+
const auto n_elements =
253+
std::accumulate(packed_dims.begin(),
254+
packed_dims.end(),
255+
size_t{1},
256+
[](const size_t& a, const hsize_t& b) { return a * b; });
257+
auto memspace = DataSpace(std::array<size_t, 1>{n_elements});
258+
259+
return detail::make_selection(memspace, filespace, details::get_dataset(slice));
260+
}
243261

244262
template <typename Derivate>
245263
inline Selection SliceTraits<Derivate>::select(const std::vector<size_t>& offset,

src/examples/select_partial_dataset_cpp11.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,11 @@ int main(void) {
3434
dataset.write(values);
3535

3636
// now we read back 2x2 values after an offset of 0x2
37+
{
38+
std::vector<double> result;
39+
dataset.select(RegularHyperSlabNoMalloc<2>{{0, 2}, {2, 2}}).read(result);
40+
}
41+
3742
std::vector<std::vector<double>> result;
3843
dataset.select({0, 2}, {2, 2}).read(result);
3944

0 commit comments

Comments
 (0)