Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix dirtyRecursive() performance for Series with many steps #1598

Merged
merged 15 commits into from
Mar 25, 2024
Merged
10 changes: 0 additions & 10 deletions include/openPMD/Iteration.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -380,16 +380,6 @@ class Iteration : public Attributable
*/
void setStepStatus(StepStatus);

/*
* @brief Check recursively whether this Iteration is dirty.
* It is dirty if any attribute or dataset is read from or written to
* the backend.
*
* @return true If dirty.
* @return false Otherwise.
*/
bool dirtyRecursive() const;

/**
* @brief Link with parent.
*
Expand Down
10 changes: 0 additions & 10 deletions include/openPMD/ParticleSpecies.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,6 @@ class ParticleSpecies : public Container<Record>

void read();
void flush(std::string const &, internal::FlushParams const &) override;

/**
* @brief Check recursively whether this ParticleSpecies is dirty.
* It is dirty if any attribute or dataset is read from or written to
* the backend.
*
* @return true If dirty.
* @return false Otherwise.
*/
bool dirtyRecursive() const;
};

namespace traits
Expand Down
12 changes: 2 additions & 10 deletions include/openPMD/RecordComponent.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ namespace internal
* Chunk reading/writing requests on the contained dataset.
*/
std::queue<IOTask> m_chunks;

void push_chunk(IOTask &&task);
/**
* Stores the value for constant record components.
* Ignored otherwise.
Expand Down Expand Up @@ -497,16 +499,6 @@ class RecordComponent : public BaseRecordComponent
void storeChunk(
auxiliary::WriteBuffer buffer, Datatype datatype, Offset o, Extent e);

/**
* @brief Check recursively whether this RecordComponent is dirty.
* It is dirty if any attribute or dataset is read from or written to
* the backend.
*
* @return true If dirty.
* @return false Otherwise.
*/
bool dirtyRecursive() const;

// clang-format off
OPENPMD_protected
// clang-format on
Expand Down
2 changes: 1 addition & 1 deletion include/openPMD/RecordComponent.tpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ RecordComponent::loadChunk(std::shared_ptr<T> data, Offset o, Extent e)
dRead.extent = extent;
dRead.dtype = getDatatype();
dRead.data = std::static_pointer_cast<void>(data);
rc.m_chunks.push(IOTask(this, dRead));
rc.push_chunk(IOTask(this, dRead));
}
}

Expand Down
5 changes: 5 additions & 0 deletions include/openPMD/Series.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -836,6 +836,11 @@ OPENPMD_private
AbstractIOHandler *IOHandler();
AbstractIOHandler const *IOHandler() const;
}; // Series

namespace debug
{
void printDirty(Series const &);
}
} // namespace openPMD

// Make sure that this one is always included if Series.hpp is included,
Expand Down
55 changes: 51 additions & 4 deletions include/openPMD/backend/Attributable.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,15 @@ namespace internal

template <typename, typename>
class BaseRecordData;

class RecordComponentData;
} // namespace internal

namespace debug
{
void printDirty(Series const &);
}

/** @brief Layer to manage storage of attributes associated with file objects.
*
* Mandatory and user-defined Attributes and their data for every object in the
Expand All @@ -109,6 +116,8 @@ class Attributable
friend class Series;
friend class Writable;
friend class WriteIterations;
friend class internal::RecordComponentData;
friend void debug::printDirty(Series const &);

protected:
// tag for internal constructor
Expand Down Expand Up @@ -378,11 +387,49 @@ OPENPMD_protected

bool dirty() const
{
return writable().dirty;
return writable().dirtySelf;
}
/** O(1).
*/
bool dirtyRecursive() const
{
return writable().dirtyRecursive;
}
void setDirty(bool dirty_in)
{
auto &w = writable();
w.dirtySelf = dirty_in;
setDirtyRecursive(dirty_in);
}
bool &dirty()
/* Amortized O(1) if dirty_in is true, else O(1).
*
* Must be used carefully with `dirty_in == false` since it is assumed that
* all children are not dirty.
*
* Invariant of dirtyRecursive:
* this->dirtyRecursive implies parent->dirtyRecursive.
*
* Hence:
*
* * If dirty_in is true: This needs only go up far enough until a parent is
* found that itself is dirtyRecursive.
* * If dirty_in is false: Only sets `this` to `dirtyRecursive == false`.
* The caller must ensure that the invariant holds (e.g. clearing
* everything during flushing or reading logic).
*/
void setDirtyRecursive(bool dirty_in)
{
return writable().dirty;
auto &w = writable();
w.dirtyRecursive = dirty_in;
if (dirty_in)
{
auto current = w.parent;
while (current && !current->dirtyRecursive)
{
current->dirtyRecursive = true;
current = current->parent;
}
}
}
bool written() const
{
Expand Down Expand Up @@ -417,7 +464,7 @@ inline bool Attributable::setAttribute(std::string const &key, T value)
error::throwNoSuchAttribute(out_of_range_msg(key));
}

dirty() = true;
setDirty(true);
auto it = attri.m_attributes.lower_bound(key);
if (it != attri.m_attributes.end() &&
!attri.m_attributes.key_comp()(key, it->first))
Expand Down
30 changes: 4 additions & 26 deletions include/openPMD/backend/BaseRecord.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -494,15 +494,6 @@ class BaseRecord
virtual void
flush_impl(std::string const &, internal::FlushParams const &) = 0;

/**
* @brief Check recursively whether this BaseRecord is dirty.
* It is dirty if any attribute or dataset is read from or written to
* the backend.
*
* @return true If dirty.
* @return false Otherwise.
*/
bool dirtyRecursive() const;
void eraseScalar();
}; // BaseRecord

Expand Down Expand Up @@ -999,25 +990,12 @@ inline void BaseRecord<T_elem>::flush(
}

this->flush_impl(name, flushParams);
// flush_impl must take care to correctly set the dirty() flag so this
// method doesn't do it
}

template <typename T_elem>
inline bool BaseRecord<T_elem>::dirtyRecursive() const
{
if (this->dirty())
if (flushParams.flushLevel != FlushLevel::SkeletonOnly)
{
return true;
this->setDirty(false);
}
for (auto const &pair : *this)
{
if (pair.second.dirtyRecursive())
{
return true;
}
}
return false;
// flush_impl must take care to correctly set the dirty() flag so this
// method doesn't do it
}

template <typename T_elem>
Expand Down
6 changes: 3 additions & 3 deletions include/openPMD/backend/PatchRecordComponent.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ inline void PatchRecordComponent::load(std::shared_ptr<T> data)
dRead.dtype = getDatatype();
dRead.data = std::static_pointer_cast<void>(data);
auto &rc = get();
rc.m_chunks.push(IOTask(this, dRead));
rc.push_chunk(IOTask(this, dRead));
}

template <typename T>
Expand Down Expand Up @@ -182,7 +182,7 @@ inline void PatchRecordComponent::store(uint64_t idx, T data)
dWrite.dtype = dtype;
dWrite.data = std::make_shared<T>(data);
auto &rc = get();
rc.m_chunks.push(IOTask(this, std::move(dWrite)));
rc.push_chunk(IOTask(this, std::move(dWrite)));
}

template <typename T>
Expand Down Expand Up @@ -211,6 +211,6 @@ inline void PatchRecordComponent::store(T data)
dWrite.dtype = dtype;
dWrite.data = std::make_shared<T>(data);
auto &rc = get();
rc.m_chunks.push(IOTask(this, std::move(dWrite)));
rc.push_chunk(IOTask(this, std::move(dWrite)));
}
} // namespace openPMD
27 changes: 26 additions & 1 deletion include/openPMD/backend/Writable.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ template <typename FilePositionType>
class AbstractIOHandlerImplCommon;
template <typename>
class Span;
class Series;

namespace internal
{
Expand All @@ -55,6 +56,11 @@ namespace detail
class ADIOS2File;
}

namespace debug
{
void printDirty(Series const &);
}

/** @brief Layer to mirror structure of logical data and persistent data in
* file.
*
Expand Down Expand Up @@ -94,6 +100,7 @@ class Writable final
friend std::string concrete_bp1_file_position(Writable *);
template <typename>
friend class Span;
friend void debug::printDirty(Series const &);

private:
Writable(internal::AttributableData *);
Expand Down Expand Up @@ -135,7 +142,25 @@ OPENPMD_private
IOHandler = nullptr;
internal::AttributableData *attributable = nullptr;
Writable *parent = nullptr;
bool dirty = true;

/** Tracks if there are unwritten changes for this specific Writable.
*
* Manipulate via Attributable::dirty() and Attributable::setDirty().
*/
bool dirtySelf = true;
/**
* Tracks if there are unwritten changes anywhere in the
* tree whose ancestor this Writable is.
*
* Invariant: this->dirtyRecursive implies parent->dirtyRecursive.
*
* dirtySelf and dirtyRecursive are separated since that allows specifying
* that `this` is not dirty, but some child is.
*
* Manipulate via Attributable::dirtyRecursive() and
* Attributable::setDirtyRecursive().
*/
bool dirtyRecursive = true;
/**
* If parent is not null, then this is a key such that:
* &(*parent)[key] == this
Expand Down
8 changes: 7 additions & 1 deletion src/IO/ADIOS/ADIOS2File.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
*/

#include "openPMD/IO/ADIOS/ADIOS2File.hpp"
#include "openPMD/Error.hpp"
#include "openPMD/IO/ADIOS/ADIOS2IOHandler.hpp"
#include "openPMD/auxiliary/Environment.hpp"

Expand Down Expand Up @@ -1188,7 +1189,12 @@ AdvanceStatus ADIOS2File::advance(AdvanceMode mode)

void ADIOS2File::drop()
{
m_buffer.clear();
if (!m_buffer.empty())
{
throw error::Internal(
"ADIOS2 backend: File data for '" + m_file +
"' dropped, but there were enqueued operations.");
}
}

static std::vector<std::string> availableAttributesOrVariablesPrefixed(
Expand Down
1 change: 1 addition & 0 deletions src/IO/ADIOS/ADIOS2IOHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,7 @@ ADIOS2IOHandlerImpl::flush(internal::ParsedFlushParams &flushParams)
p.second->drop();
}
}
m_dirty.clear();
return res;
}

Expand Down
Loading
Loading