Skip to content

Commit

Permalink
Add unique_ptr overloads for storeChunk calls (#1294)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
franzpoeschel authored Feb 22, 2023
1 parent f883f8e commit 9a215b2
Show file tree
Hide file tree
Showing 22 changed files with 905 additions and 375 deletions.
30 changes: 30 additions & 0 deletions examples/12_span_write.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,36 @@ void span_write(std::string const &filename)
}
++j;
}

using mesh_type = position_t;

RecordComponent chargeDensity =
iteration.meshes["e_chargeDensity"][RecordComponent::SCALAR];

/*
* A similar memory optimization is possible by using a unique_ptr type
* in the call to storeChunk().
* Unlike the Span API, the buffer here is user-created, but in both
* approaches, the backend will manage the memory after the call to
* storeChunk().
* Some backends (especially: ADIOS2 BP5) will benefit from being able
* to avoid memcopies since they know that they can just keep the memory
* and noone else is reading it.
*/
chargeDensity.resetDataset(dataset);
/*
* The class template UniquePtrWithLambda (subclass of std::unique_ptr)
* can be used to specify custom destructors, e.g. for deallocating
* GPU pointers.
* Normal std::unique_ptr types can also be used, even with custom
* destructors.
*/
UniquePtrWithLambda<mesh_type> data(
new mesh_type[length](), [](auto const *ptr) { delete[] ptr; });
/*
* Move the unique_ptr into openPMD. It must now no longer be accessed.
*/
chargeDensity.storeChunk(std::move(data), {0}, extent);
iteration.close();
}

Expand Down
47 changes: 21 additions & 26 deletions include/openPMD/Datatype.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#pragma once

#include "openPMD/auxiliary/TypeTraits.hpp"
#include "openPMD/auxiliary/UniquePtr.hpp"

#include <array>
#include <climits>
Expand Down Expand Up @@ -276,31 +277,26 @@ inline constexpr Datatype determineDatatype()
return Datatype::UNDEFINED;
}

template <typename T>
inline constexpr Datatype determineDatatype(std::shared_ptr<T>)
{
return determineDatatype<T>();
}

template <typename T>
inline constexpr Datatype determineDatatype(T *)
{
return determineDatatype<T>();
}

/*
* Catch-all overload for unsupported types, with static_assert errors
* triggered at compile-time.
/**
* @brief Determine datatype of passed value
*
* @param val Value whose type to evaluate
* @tparam T Type of the passed value
* @return If T is of a pointer type, then the type of the contained value.
* Otherwise, a compile-time error detailing the use of this function.
*/
template <typename T_ContiguousContainer>
inline constexpr Datatype determineDatatype(T_ContiguousContainer &&)
template <typename T>
inline constexpr Datatype determineDatatype(T &&val)
{
using T_ContiguousContainer_stripped =
std::remove_reference_t<T_ContiguousContainer>;
if constexpr (auxiliary::IsContiguousContainer_v<
T_ContiguousContainer_stripped>)
(void)val; // don't need this, it only has a name for Doxygen
using T_stripped = std::remove_cv_t<std::remove_reference_t<T>>;
if constexpr (auxiliary::IsPointer_v<T_stripped>)
{
return determineDatatype<auxiliary::IsPointer_t<T_stripped>>();
}
else if constexpr (auxiliary::IsContiguousContainer_v<T_stripped>)
{
static_assert(auxiliary::dependent_false_v<T_ContiguousContainer>, R"(
static_assert(auxiliary::dependent_false_v<T_stripped>, R"(
Error: Passed a contiguous container type to determineDatatype<>().
These types are not directly supported due to colliding semantics.
Assuming a vector object `std::vector<float> vec;`,
Expand All @@ -322,15 +318,14 @@ use one of the following alternatives:
}
else
{
static_assert(auxiliary::dependent_false_v<T_ContiguousContainer>, R"(
static_assert(auxiliary::dependent_false_v<T_stripped>, R"(
Error: Unknown datatype passed to determineDatatype<>().
For a direct translation from C++ type to the openPMD::Datatype enum, use:
`auto determineDatatype<T>() -> Datatype`.
For detecting the contained datatpye of a pointer type (shared or raw pointer),
use either of the following overloads:
`auto determineDatatype<T>(std::shared_ptr<T>) -> Datatype` or
`auto determineDatatype<T>(T *) -> Datatype`.
use this following template (i.e. `auto determineDatatype<T>(T &&) -> Datatype`)
which accepts pointer-type parameters (raw, shared or unique).
)");
}
// Unreachable, but C++ does not know it
Expand Down
42 changes: 31 additions & 11 deletions include/openPMD/IO/ADIOS/ADIOS2IOHandler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ namespace detail
struct BufferedGet;
struct BufferedAttributeRead;
struct BufferedAttributeWrite;
struct RunUniquePtrPut;
} // namespace detail

namespace ADIOS2Schema
Expand Down Expand Up @@ -124,6 +125,7 @@ class ADIOS2IOHandlerImpl
friend struct detail::WriteDataset;
friend struct detail::BufferedActions;
friend struct detail::BufferedAttributeRead;
friend struct detail::RunUniquePtrPut;

public:
#if openPMD_HAVE_MPI
Expand Down Expand Up @@ -188,8 +190,8 @@ class ADIOS2IOHandlerImpl
void deleteAttribute(
Writable *, Parameter<Operation::DELETE_ATT> const &) override;

void writeDataset(
Writable *, Parameter<Operation::WRITE_DATASET> const &) override;
void
writeDataset(Writable *, Parameter<Operation::WRITE_DATASET> &) override;

void writeAttribute(
Writable *, Parameter<Operation::WRITE_ATT> const &) override;
Expand Down Expand Up @@ -544,11 +546,7 @@ namespace detail
struct WriteDataset
{
template <typename T>
static void call(
ADIOS2IOHandlerImpl *impl,
BufferedPut &bp,
adios2::IO &IO,
adios2::Engine &engine);
static void call(BufferedActions &ba, BufferedPut &bp);

template <int n, typename... Params>
static void call(Params &&...);
Expand Down Expand Up @@ -916,6 +914,17 @@ namespace detail
void run(BufferedActions &) override;
};

struct BufferedUniquePtrPut
{
std::string name;
Offset offset;
Extent extent;
UniquePtrWithLambda<void> data;
Datatype dtype;

void run(BufferedActions &);
};

struct OldBufferedAttributeRead : BufferedAction
{
Parameter<Operation::READ_ATT> param;
Expand Down Expand Up @@ -967,6 +976,8 @@ namespace detail
{
friend struct BufferedGet;
friend struct BufferedPut;
friend struct RunUniquePtrPut;
friend struct WriteDataset;

using FlushTarget = ADIOS2IOHandlerImpl::FlushTarget;

Expand Down Expand Up @@ -1023,6 +1034,13 @@ namespace detail
* penalty, once preloadAttributes has been filled.
*/
std::vector<BufferedAttributeRead> m_attributeReads;
/**
* When receiving a unique_ptr, we know that the buffer is ours and
* ours alone. So, for performance reasons, show the buffer to ADIOS2 as
* late as possible and avoid unnecessary data copies in BP5 triggered
* by PerformDataWrites().
*/
std::vector<BufferedUniquePtrPut> m_uniquePtrPuts;
/**
* This contains deferred actions that have already been enqueued into
* ADIOS2, but not yet performed in ADIOS2.
Expand Down Expand Up @@ -1116,24 +1134,26 @@ namespace detail
* * adios2::Engine::EndStep
* * adios2::Engine::Perform(Puts|Gets)
* * adios2::Engine::Close
* @param writeAttributes If using the new attribute layout, perform
* deferred attribute writes now.
* @param writeLatePuts Some things are deferred until right before
* Engine::EndStep() or Engine::Close():
* 1) Writing attributes in new ADIOS2 schema.
* 2) Running unique_ptr Put()s.
* @param flushUnconditionally Whether to run the functor even if no
* deferred IO tasks had been queued.
*/
template <typename F>
void flush_impl(
ADIOS2FlushParams flushParams,
F &&performPutsGets,
bool writeAttributes,
bool writeLatePuts,
bool flushUnconditionally);

/**
* Overload of flush() that uses adios2::Engine::Perform(Puts|Gets)
* and does not flush unconditionally.
*
*/
void flush_impl(ADIOS2FlushParams, bool writeAttributes = false);
void flush_impl(ADIOS2FlushParams, bool writeLatePuts = false);

/**
* @brief Begin or end an ADIOS step.
Expand Down
4 changes: 2 additions & 2 deletions include/openPMD/IO/ADIOS/CommonADIOS1IOHandler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,8 @@ class CommonADIOS1IOHandlerImpl : public AbstractIOHandlerImpl
Writable *, Parameter<Operation::DELETE_DATASET> const &) override;
void deleteAttribute(
Writable *, Parameter<Operation::DELETE_ATT> const &) override;
void writeDataset(
Writable *, Parameter<Operation::WRITE_DATASET> const &) override;
void
writeDataset(Writable *, Parameter<Operation::WRITE_DATASET> &) override;
void writeAttribute(
Writable *, Parameter<Operation::WRITE_ATT> const &) override;
void readDataset(Writable *, Parameter<Operation::READ_DATASET> &) override;
Expand Down
2 changes: 1 addition & 1 deletion include/openPMD/IO/AbstractIOHandlerImpl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,7 @@ class AbstractIOHandlerImpl
* storage after the operation completes successfully.
*/
virtual void
writeDataset(Writable *, Parameter<Operation::WRITE_DATASET> const &) = 0;
writeDataset(Writable *, Parameter<Operation::WRITE_DATASET> &) = 0;

/** Get a view into a dataset buffer that can be filled by a user.
*
Expand Down
4 changes: 2 additions & 2 deletions include/openPMD/IO/HDF5/HDF5IOHandlerImpl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@ class HDF5IOHandlerImpl : public AbstractIOHandlerImpl
Writable *, Parameter<Operation::DELETE_DATASET> const &) override;
void deleteAttribute(
Writable *, Parameter<Operation::DELETE_ATT> const &) override;
void writeDataset(
Writable *, Parameter<Operation::WRITE_DATASET> const &) override;
void
writeDataset(Writable *, Parameter<Operation::WRITE_DATASET> &) override;
void writeAttribute(
Writable *, Parameter<Operation::WRITE_ATT> const &) override;
void readDataset(Writable *, Parameter<Operation::READ_DATASET> &) override;
Expand Down
Loading

0 comments on commit 9a215b2

Please sign in to comment.