Skip to content

Commit e4ce81f

Browse files
franzpoeschelax3l
andauthored
Fix dirtyRecursive() performance for Series with many steps (#1598)
* Don't check closed iterations if they are dirty * WIP: Alternative way of checking for errors * Re-enable some tests * Clear dirty files after flushing in ADIOS2 * [WIP] Track dirtyRecursive from the beginning * Continue fixing things * Further fixes * Test cleanup * CI fixes * Replace drop() with a condition check * Move printDirty to debug::printDirty * Update include/openPMD/RecordComponent.hpp Co-authored-by: Axel Huebl <axel.huebl@plasma.ninja> * Revert to older verification logic It's more performant now with the new implementation for dirtyRecursive * Document * Remove stale changes --------- Co-authored-by: Axel Huebl <axel.huebl@plasma.ninja>
1 parent 5940f7c commit e4ce81f

19 files changed

Lines changed: 248 additions & 153 deletions

include/openPMD/Iteration.hpp

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -380,16 +380,6 @@ class Iteration : public Attributable
380380
*/
381381
void setStepStatus(StepStatus);
382382

383-
/*
384-
* @brief Check recursively whether this Iteration is dirty.
385-
* It is dirty if any attribute or dataset is read from or written to
386-
* the backend.
387-
*
388-
* @return true If dirty.
389-
* @return false Otherwise.
390-
*/
391-
bool dirtyRecursive() const;
392-
393383
/**
394384
* @brief Link with parent.
395385
*

include/openPMD/ParticleSpecies.hpp

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -44,16 +44,6 @@ class ParticleSpecies : public Container<Record>
4444

4545
void read();
4646
void flush(std::string const &, internal::FlushParams const &) override;
47-
48-
/**
49-
* @brief Check recursively whether this ParticleSpecies is dirty.
50-
* It is dirty if any attribute or dataset is read from or written to
51-
* the backend.
52-
*
53-
* @return true If dirty.
54-
* @return false Otherwise.
55-
*/
56-
bool dirtyRecursive() const;
5747
};
5848

5949
namespace traits

include/openPMD/RecordComponent.hpp

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ namespace internal
6969
* Chunk reading/writing requests on the contained dataset.
7070
*/
7171
std::queue<IOTask> m_chunks;
72+
73+
void push_chunk(IOTask &&task);
7274
/**
7375
* Stores the value for constant record components.
7476
* Ignored otherwise.
@@ -497,16 +499,6 @@ class RecordComponent : public BaseRecordComponent
497499
void storeChunk(
498500
auxiliary::WriteBuffer buffer, Datatype datatype, Offset o, Extent e);
499501

500-
/**
501-
* @brief Check recursively whether this RecordComponent is dirty.
502-
* It is dirty if any attribute or dataset is read from or written to
503-
* the backend.
504-
*
505-
* @return true If dirty.
506-
* @return false Otherwise.
507-
*/
508-
bool dirtyRecursive() const;
509-
510502
// clang-format off
511503
OPENPMD_protected
512504
// clang-format on

include/openPMD/RecordComponent.tpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ RecordComponent::loadChunk(std::shared_ptr<T> data, Offset o, Extent e)
172172
dRead.extent = extent;
173173
dRead.dtype = getDatatype();
174174
dRead.data = std::static_pointer_cast<void>(data);
175-
rc.m_chunks.push(IOTask(this, dRead));
175+
rc.push_chunk(IOTask(this, dRead));
176176
}
177177
}
178178

include/openPMD/Series.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -837,6 +837,11 @@ OPENPMD_private
837837
AbstractIOHandler *IOHandler();
838838
AbstractIOHandler const *IOHandler() const;
839839
}; // Series
840+
841+
namespace debug
842+
{
843+
void printDirty(Series const &);
844+
}
840845
} // namespace openPMD
841846

842847
// Make sure that this one is always included if Series.hpp is included,

include/openPMD/backend/Attributable.hpp

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,15 @@ namespace internal
8383

8484
template <typename, typename>
8585
class BaseRecordData;
86+
87+
class RecordComponentData;
8688
} // namespace internal
8789

90+
namespace debug
91+
{
92+
void printDirty(Series const &);
93+
}
94+
8895
/** @brief Layer to manage storage of attributes associated with file objects.
8996
*
9097
* Mandatory and user-defined Attributes and their data for every object in the
@@ -109,6 +116,8 @@ class Attributable
109116
friend class Series;
110117
friend class Writable;
111118
friend class WriteIterations;
119+
friend class internal::RecordComponentData;
120+
friend void debug::printDirty(Series const &);
112121

113122
protected:
114123
// tag for internal constructor
@@ -378,11 +387,49 @@ OPENPMD_protected
378387

379388
bool dirty() const
380389
{
381-
return writable().dirty;
390+
return writable().dirtySelf;
391+
}
392+
/** O(1).
393+
*/
394+
bool dirtyRecursive() const
395+
{
396+
return writable().dirtyRecursive;
397+
}
398+
void setDirty(bool dirty_in)
399+
{
400+
auto &w = writable();
401+
w.dirtySelf = dirty_in;
402+
setDirtyRecursive(dirty_in);
382403
}
383-
bool &dirty()
404+
/* Amortized O(1) if dirty_in is true, else O(1).
405+
*
406+
* Must be used carefully with `dirty_in == false` since it is assumed that
407+
* all children are not dirty.
408+
*
409+
* Invariant of dirtyRecursive:
410+
* this->dirtyRecursive implies parent->dirtyRecursive.
411+
*
412+
* Hence:
413+
*
414+
* * If dirty_in is true: This needs only go up far enough until a parent is
415+
* found that itself is dirtyRecursive.
416+
* * If dirty_in is false: Only sets `this` to `dirtyRecursive == false`.
417+
* The caller must ensure that the invariant holds (e.g. clearing
418+
* everything during flushing or reading logic).
419+
*/
420+
void setDirtyRecursive(bool dirty_in)
384421
{
385-
return writable().dirty;
422+
auto &w = writable();
423+
w.dirtyRecursive = dirty_in;
424+
if (dirty_in)
425+
{
426+
auto current = w.parent;
427+
while (current && !current->dirtyRecursive)
428+
{
429+
current->dirtyRecursive = true;
430+
current = current->parent;
431+
}
432+
}
386433
}
387434
bool written() const
388435
{
@@ -417,7 +464,7 @@ inline bool Attributable::setAttribute(std::string const &key, T value)
417464
error::throwNoSuchAttribute(out_of_range_msg(key));
418465
}
419466

420-
dirty() = true;
467+
setDirty(true);
421468
auto it = attri.m_attributes.lower_bound(key);
422469
if (it != attri.m_attributes.end() &&
423470
!attri.m_attributes.key_comp()(key, it->first))

include/openPMD/backend/BaseRecord.hpp

Lines changed: 4 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -494,15 +494,6 @@ class BaseRecord
494494
virtual void
495495
flush_impl(std::string const &, internal::FlushParams const &) = 0;
496496

497-
/**
498-
* @brief Check recursively whether this BaseRecord is dirty.
499-
* It is dirty if any attribute or dataset is read from or written to
500-
* the backend.
501-
*
502-
* @return true If dirty.
503-
* @return false Otherwise.
504-
*/
505-
bool dirtyRecursive() const;
506497
void eraseScalar();
507498
}; // BaseRecord
508499

@@ -999,25 +990,12 @@ inline void BaseRecord<T_elem>::flush(
999990
}
1000991

1001992
this->flush_impl(name, flushParams);
1002-
// flush_impl must take care to correctly set the dirty() flag so this
1003-
// method doesn't do it
1004-
}
1005-
1006-
template <typename T_elem>
1007-
inline bool BaseRecord<T_elem>::dirtyRecursive() const
1008-
{
1009-
if (this->dirty())
993+
if (flushParams.flushLevel != FlushLevel::SkeletonOnly)
1010994
{
1011-
return true;
995+
this->setDirty(false);
1012996
}
1013-
for (auto const &pair : *this)
1014-
{
1015-
if (pair.second.dirtyRecursive())
1016-
{
1017-
return true;
1018-
}
1019-
}
1020-
return false;
997+
// flush_impl must take care to correctly set the dirty() flag so this
998+
// method doesn't do it
1021999
}
10221000

10231001
template <typename T_elem>

include/openPMD/backend/PatchRecordComponent.hpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ inline void PatchRecordComponent::load(std::shared_ptr<T> data)
143143
dRead.dtype = getDatatype();
144144
dRead.data = std::static_pointer_cast<void>(data);
145145
auto &rc = get();
146-
rc.m_chunks.push(IOTask(this, dRead));
146+
rc.push_chunk(IOTask(this, dRead));
147147
}
148148

149149
template <typename T>
@@ -182,7 +182,7 @@ inline void PatchRecordComponent::store(uint64_t idx, T data)
182182
dWrite.dtype = dtype;
183183
dWrite.data = std::make_shared<T>(data);
184184
auto &rc = get();
185-
rc.m_chunks.push(IOTask(this, std::move(dWrite)));
185+
rc.push_chunk(IOTask(this, std::move(dWrite)));
186186
}
187187

188188
template <typename T>
@@ -211,6 +211,6 @@ inline void PatchRecordComponent::store(T data)
211211
dWrite.dtype = dtype;
212212
dWrite.data = std::make_shared<T>(data);
213213
auto &rc = get();
214-
rc.m_chunks.push(IOTask(this, std::move(dWrite)));
214+
rc.push_chunk(IOTask(this, std::move(dWrite)));
215215
}
216216
} // namespace openPMD

include/openPMD/backend/Writable.hpp

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ template <typename FilePositionType>
4444
class AbstractIOHandlerImplCommon;
4545
template <typename>
4646
class Span;
47+
class Series;
4748

4849
namespace internal
4950
{
@@ -55,6 +56,11 @@ namespace detail
5556
class ADIOS2File;
5657
}
5758

59+
namespace debug
60+
{
61+
void printDirty(Series const &);
62+
}
63+
5864
/** @brief Layer to mirror structure of logical data and persistent data in
5965
* file.
6066
*
@@ -94,6 +100,7 @@ class Writable final
94100
friend std::string concrete_bp1_file_position(Writable *);
95101
template <typename>
96102
friend class Span;
103+
friend void debug::printDirty(Series const &);
97104

98105
private:
99106
Writable(internal::AttributableData *);
@@ -135,7 +142,25 @@ OPENPMD_private
135142
IOHandler = nullptr;
136143
internal::AttributableData *attributable = nullptr;
137144
Writable *parent = nullptr;
138-
bool dirty = true;
145+
146+
/** Tracks if there are unwritten changes for this specific Writable.
147+
*
148+
* Manipulate via Attributable::dirty() and Attributable::setDirty().
149+
*/
150+
bool dirtySelf = true;
151+
/**
152+
* Tracks if there are unwritten changes anywhere in the
153+
* tree whose ancestor this Writable is.
154+
*
155+
* Invariant: this->dirtyRecursive implies parent->dirtyRecursive.
156+
*
157+
* dirtySelf and dirtyRecursive are separated since that allows specifying
158+
* that `this` is not dirty, but some child is.
159+
*
160+
* Manipulate via Attributable::dirtyRecursive() and
161+
* Attributable::setDirtyRecursive().
162+
*/
163+
bool dirtyRecursive = true;
139164
/**
140165
* If parent is not null, then this is a key such that:
141166
* &(*parent)[key] == this

src/IO/ADIOS/ADIOS2File.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
*/
2121

2222
#include "openPMD/IO/ADIOS/ADIOS2File.hpp"
23+
#include "openPMD/Error.hpp"
2324
#include "openPMD/IO/ADIOS/ADIOS2IOHandler.hpp"
2425
#include "openPMD/auxiliary/Environment.hpp"
2526

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

11891190
void ADIOS2File::drop()
11901191
{
1191-
m_buffer.clear();
1192+
if (!m_buffer.empty())
1193+
{
1194+
throw error::Internal(
1195+
"ADIOS2 backend: File data for '" + m_file +
1196+
"' dropped, but there were enqueued operations.");
1197+
}
11921198
}
11931199

11941200
static std::vector<std::string> availableAttributesOrVariablesPrefixed(

0 commit comments

Comments
 (0)