Skip to content

Commit 9a215b2

Browse files
Add unique_ptr overloads for storeChunk calls (#1294)
* Add OpenpmdUniquePtr class * Prepare IOTask.hpp for a non-copyable parameter Refactor ::clone() to ::to_heap(), also make copy/move constructors/operators in AbstractParameter protected instead of deleting them. * Backend fully prepared for accepting unique_ptr buffers No optimizations based on that yet * Add storeChunk(OpenpmdUniquePtr, ...) overload * Fix invasive tests * Add test for ADIOS2 backend optimization Not yet implemented, so test fails * Implement ADIOS2 backend optimization * Support also regular std::unique_ptr * CI fixes * Use OpenpmdUniquePtr with storeChunk in an example * Rename OpenpmdUniquePtr -> UniquePtrWithLambda
1 parent f883f8e commit 9a215b2

22 files changed

+905
-375
lines changed

examples/12_span_write.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,36 @@ void span_write(std::string const &filename)
8282
}
8383
++j;
8484
}
85+
86+
using mesh_type = position_t;
87+
88+
RecordComponent chargeDensity =
89+
iteration.meshes["e_chargeDensity"][RecordComponent::SCALAR];
90+
91+
/*
92+
* A similar memory optimization is possible by using a unique_ptr type
93+
* in the call to storeChunk().
94+
* Unlike the Span API, the buffer here is user-created, but in both
95+
* approaches, the backend will manage the memory after the call to
96+
* storeChunk().
97+
* Some backends (especially: ADIOS2 BP5) will benefit from being able
98+
* to avoid memcopies since they know that they can just keep the memory
99+
* and noone else is reading it.
100+
*/
101+
chargeDensity.resetDataset(dataset);
102+
/*
103+
* The class template UniquePtrWithLambda (subclass of std::unique_ptr)
104+
* can be used to specify custom destructors, e.g. for deallocating
105+
* GPU pointers.
106+
* Normal std::unique_ptr types can also be used, even with custom
107+
* destructors.
108+
*/
109+
UniquePtrWithLambda<mesh_type> data(
110+
new mesh_type[length](), [](auto const *ptr) { delete[] ptr; });
111+
/*
112+
* Move the unique_ptr into openPMD. It must now no longer be accessed.
113+
*/
114+
chargeDensity.storeChunk(std::move(data), {0}, extent);
85115
iteration.close();
86116
}
87117

include/openPMD/Datatype.hpp

Lines changed: 21 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#pragma once
2222

2323
#include "openPMD/auxiliary/TypeTraits.hpp"
24+
#include "openPMD/auxiliary/UniquePtr.hpp"
2425

2526
#include <array>
2627
#include <climits>
@@ -276,31 +277,26 @@ inline constexpr Datatype determineDatatype()
276277
return Datatype::UNDEFINED;
277278
}
278279

279-
template <typename T>
280-
inline constexpr Datatype determineDatatype(std::shared_ptr<T>)
281-
{
282-
return determineDatatype<T>();
283-
}
284-
285-
template <typename T>
286-
inline constexpr Datatype determineDatatype(T *)
287-
{
288-
return determineDatatype<T>();
289-
}
290-
291-
/*
292-
* Catch-all overload for unsupported types, with static_assert errors
293-
* triggered at compile-time.
280+
/**
281+
* @brief Determine datatype of passed value
282+
*
283+
* @param val Value whose type to evaluate
284+
* @tparam T Type of the passed value
285+
* @return If T is of a pointer type, then the type of the contained value.
286+
* Otherwise, a compile-time error detailing the use of this function.
294287
*/
295-
template <typename T_ContiguousContainer>
296-
inline constexpr Datatype determineDatatype(T_ContiguousContainer &&)
288+
template <typename T>
289+
inline constexpr Datatype determineDatatype(T &&val)
297290
{
298-
using T_ContiguousContainer_stripped =
299-
std::remove_reference_t<T_ContiguousContainer>;
300-
if constexpr (auxiliary::IsContiguousContainer_v<
301-
T_ContiguousContainer_stripped>)
291+
(void)val; // don't need this, it only has a name for Doxygen
292+
using T_stripped = std::remove_cv_t<std::remove_reference_t<T>>;
293+
if constexpr (auxiliary::IsPointer_v<T_stripped>)
294+
{
295+
return determineDatatype<auxiliary::IsPointer_t<T_stripped>>();
296+
}
297+
else if constexpr (auxiliary::IsContiguousContainer_v<T_stripped>)
302298
{
303-
static_assert(auxiliary::dependent_false_v<T_ContiguousContainer>, R"(
299+
static_assert(auxiliary::dependent_false_v<T_stripped>, R"(
304300
Error: Passed a contiguous container type to determineDatatype<>().
305301
These types are not directly supported due to colliding semantics.
306302
Assuming a vector object `std::vector<float> vec;`,
@@ -322,15 +318,14 @@ use one of the following alternatives:
322318
}
323319
else
324320
{
325-
static_assert(auxiliary::dependent_false_v<T_ContiguousContainer>, R"(
321+
static_assert(auxiliary::dependent_false_v<T_stripped>, R"(
326322
Error: Unknown datatype passed to determineDatatype<>().
327323
For a direct translation from C++ type to the openPMD::Datatype enum, use:
328324
`auto determineDatatype<T>() -> Datatype`.
329325
330326
For detecting the contained datatpye of a pointer type (shared or raw pointer),
331-
use either of the following overloads:
332-
`auto determineDatatype<T>(std::shared_ptr<T>) -> Datatype` or
333-
`auto determineDatatype<T>(T *) -> Datatype`.
327+
use this following template (i.e. `auto determineDatatype<T>(T &&) -> Datatype`)
328+
which accepts pointer-type parameters (raw, shared or unique).
334329
)");
335330
}
336331
// Unreachable, but C++ does not know it

include/openPMD/IO/ADIOS/ADIOS2IOHandler.hpp

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ namespace detail
8282
struct BufferedGet;
8383
struct BufferedAttributeRead;
8484
struct BufferedAttributeWrite;
85+
struct RunUniquePtrPut;
8586
} // namespace detail
8687

8788
namespace ADIOS2Schema
@@ -124,6 +125,7 @@ class ADIOS2IOHandlerImpl
124125
friend struct detail::WriteDataset;
125126
friend struct detail::BufferedActions;
126127
friend struct detail::BufferedAttributeRead;
128+
friend struct detail::RunUniquePtrPut;
127129

128130
public:
129131
#if openPMD_HAVE_MPI
@@ -188,8 +190,8 @@ class ADIOS2IOHandlerImpl
188190
void deleteAttribute(
189191
Writable *, Parameter<Operation::DELETE_ATT> const &) override;
190192

191-
void writeDataset(
192-
Writable *, Parameter<Operation::WRITE_DATASET> const &) override;
193+
void
194+
writeDataset(Writable *, Parameter<Operation::WRITE_DATASET> &) override;
193195

194196
void writeAttribute(
195197
Writable *, Parameter<Operation::WRITE_ATT> const &) override;
@@ -544,11 +546,7 @@ namespace detail
544546
struct WriteDataset
545547
{
546548
template <typename T>
547-
static void call(
548-
ADIOS2IOHandlerImpl *impl,
549-
BufferedPut &bp,
550-
adios2::IO &IO,
551-
adios2::Engine &engine);
549+
static void call(BufferedActions &ba, BufferedPut &bp);
552550

553551
template <int n, typename... Params>
554552
static void call(Params &&...);
@@ -916,6 +914,17 @@ namespace detail
916914
void run(BufferedActions &) override;
917915
};
918916

917+
struct BufferedUniquePtrPut
918+
{
919+
std::string name;
920+
Offset offset;
921+
Extent extent;
922+
UniquePtrWithLambda<void> data;
923+
Datatype dtype;
924+
925+
void run(BufferedActions &);
926+
};
927+
919928
struct OldBufferedAttributeRead : BufferedAction
920929
{
921930
Parameter<Operation::READ_ATT> param;
@@ -967,6 +976,8 @@ namespace detail
967976
{
968977
friend struct BufferedGet;
969978
friend struct BufferedPut;
979+
friend struct RunUniquePtrPut;
980+
friend struct WriteDataset;
970981

971982
using FlushTarget = ADIOS2IOHandlerImpl::FlushTarget;
972983

@@ -1023,6 +1034,13 @@ namespace detail
10231034
* penalty, once preloadAttributes has been filled.
10241035
*/
10251036
std::vector<BufferedAttributeRead> m_attributeReads;
1037+
/**
1038+
* When receiving a unique_ptr, we know that the buffer is ours and
1039+
* ours alone. So, for performance reasons, show the buffer to ADIOS2 as
1040+
* late as possible and avoid unnecessary data copies in BP5 triggered
1041+
* by PerformDataWrites().
1042+
*/
1043+
std::vector<BufferedUniquePtrPut> m_uniquePtrPuts;
10261044
/**
10271045
* This contains deferred actions that have already been enqueued into
10281046
* ADIOS2, but not yet performed in ADIOS2.
@@ -1116,24 +1134,26 @@ namespace detail
11161134
* * adios2::Engine::EndStep
11171135
* * adios2::Engine::Perform(Puts|Gets)
11181136
* * adios2::Engine::Close
1119-
* @param writeAttributes If using the new attribute layout, perform
1120-
* deferred attribute writes now.
1137+
* @param writeLatePuts Some things are deferred until right before
1138+
* Engine::EndStep() or Engine::Close():
1139+
* 1) Writing attributes in new ADIOS2 schema.
1140+
* 2) Running unique_ptr Put()s.
11211141
* @param flushUnconditionally Whether to run the functor even if no
11221142
* deferred IO tasks had been queued.
11231143
*/
11241144
template <typename F>
11251145
void flush_impl(
11261146
ADIOS2FlushParams flushParams,
11271147
F &&performPutsGets,
1128-
bool writeAttributes,
1148+
bool writeLatePuts,
11291149
bool flushUnconditionally);
11301150

11311151
/**
11321152
* Overload of flush() that uses adios2::Engine::Perform(Puts|Gets)
11331153
* and does not flush unconditionally.
11341154
*
11351155
*/
1136-
void flush_impl(ADIOS2FlushParams, bool writeAttributes = false);
1156+
void flush_impl(ADIOS2FlushParams, bool writeLatePuts = false);
11371157

11381158
/**
11391159
* @brief Begin or end an ADIOS step.

include/openPMD/IO/ADIOS/CommonADIOS1IOHandler.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,8 @@ class CommonADIOS1IOHandlerImpl : public AbstractIOHandlerImpl
7474
Writable *, Parameter<Operation::DELETE_DATASET> const &) override;
7575
void deleteAttribute(
7676
Writable *, Parameter<Operation::DELETE_ATT> const &) override;
77-
void writeDataset(
78-
Writable *, Parameter<Operation::WRITE_DATASET> const &) override;
77+
void
78+
writeDataset(Writable *, Parameter<Operation::WRITE_DATASET> &) override;
7979
void writeAttribute(
8080
Writable *, Parameter<Operation::WRITE_ATT> const &) override;
8181
void readDataset(Writable *, Parameter<Operation::READ_DATASET> &) override;

include/openPMD/IO/AbstractIOHandlerImpl.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -456,7 +456,7 @@ class AbstractIOHandlerImpl
456456
* storage after the operation completes successfully.
457457
*/
458458
virtual void
459-
writeDataset(Writable *, Parameter<Operation::WRITE_DATASET> const &) = 0;
459+
writeDataset(Writable *, Parameter<Operation::WRITE_DATASET> &) = 0;
460460

461461
/** Get a view into a dataset buffer that can be filled by a user.
462462
*

include/openPMD/IO/HDF5/HDF5IOHandlerImpl.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ class HDF5IOHandlerImpl : public AbstractIOHandlerImpl
6565
Writable *, Parameter<Operation::DELETE_DATASET> const &) override;
6666
void deleteAttribute(
6767
Writable *, Parameter<Operation::DELETE_ATT> const &) override;
68-
void writeDataset(
69-
Writable *, Parameter<Operation::WRITE_DATASET> const &) override;
68+
void
69+
writeDataset(Writable *, Parameter<Operation::WRITE_DATASET> &) override;
7070
void writeAttribute(
7171
Writable *, Parameter<Operation::WRITE_ATT> const &) override;
7272
void readDataset(Writable *, Parameter<Operation::READ_DATASET> &) override;

0 commit comments

Comments
 (0)