Skip to content
21 changes: 21 additions & 0 deletions include/openPMD/Iteration.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,15 @@ namespace internal
*/
StepStatus m_stepStatus = StepStatus::NoStep;

/**
* Cached copy of the key under which this Iteration lives in
* <code>Series::iterations</code>. Populated when the iteration
* object is created/inserted. This allows constant-time lookup
* of the owning map entry instead of a linear scan in
* <code>Series::indexOf()</code>.
*/
std::optional<uint64_t> m_iterationIndex = 0;

/**
* Information on a parsing request that has not yet been executed.
* Otherwise empty.
Expand Down Expand Up @@ -246,6 +255,18 @@ class Iteration : public Attributable
*/
bool closed() const;

/**
* @brief Get the cached iteration index.
* This is the key under which this iteration is stored in the
* Series::iterations map. Used for testing the index caching
* optimization.
*
* @return The cached iteration index.
*/
uint64_t getCachedIterationIndex() const
Comment thread
guj marked this conversation as resolved.
Outdated
{
return *get().m_iterationIndex;
}
/**
* @brief Has the iteration been parsed yet?
If not, it will contain no structure yet.
Expand Down
26 changes: 23 additions & 3 deletions include/openPMD/backend/ContainerImpl.tpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
* If not, see <http://www.gnu.org/licenses/>.
*/

#include "openPMD/Iteration.hpp" // needed for index caching in operator[]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pre-declaring should be fine, but note my comment below, this entire logic is better placed in linkHierarchy()

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oh, didn't know, let me see

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I don't know linkHierarchy() enough to decide. It looks like a big change to me. Maybe merge this pull request and you make a new pull request and work on it?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I put the logic into GenerationPolicy now, linkHierarchy was not adequate upon closer looking. With this, I think we can merge.

#include "openPMD/backend/Container.hpp"

/*
* Instantiations in src/backend/Container.cpp
* This file exists so that our tests can include the Container class with
Expand Down Expand Up @@ -149,6 +149,12 @@ auto Container<T, T_key, T_container>::operator[](key_type const &key)
{
ret.writable().ownKeyWithinParent = std::to_string(key);
}
// remember our key inside the iteration object itself so that
// Series::indexOf becomes constant/ log-time instead of linear.
if constexpr (std::is_same_v<T, Iteration>)
{
ret.get().m_iterationIndex = key;
}
traits::GenerationPolicy<T> gen;
gen(ret);
return ret;
Expand Down Expand Up @@ -181,6 +187,10 @@ auto Container<T, T_key, T_container>::operator[](key_type &&key)
{
ret.writable().ownKeyWithinParent = std::to_string(std::move(key));
}
if constexpr (std::is_same_v<T, Iteration>)
{
ret.get().m_iterationIndex = key;
}
traits::GenerationPolicy<T> gen;
gen(ret);
return ret;
Expand All @@ -201,13 +211,23 @@ template <typename T, typename T_key, typename T_container>
auto Container<T, T_key, T_container>::insert(value_type const &value)
-> std::pair<iterator, bool>
{
return container().insert(value);
auto res = container().insert(value);
if constexpr (std::is_same_v<T, Iteration>)
{
res.first->second.get().m_iterationIndex = res.first->first;
}
return res;
}
template <typename T, typename T_key, typename T_container>
auto Container<T, T_key, T_container>::insert(value_type &&value)
-> std::pair<iterator, bool>
{
return container().insert(value);
auto res = container().insert(value);
if constexpr (std::is_same_v<T, Iteration>)
{
res.first->second.get().m_iterationIndex = res.first->first;
}
return res;
Comment thread
franzpoeschel marked this conversation as resolved.
Outdated
}
template <typename T, typename T_key, typename T_container>
auto Container<T, T_key, T_container>::insert(
Expand Down
25 changes: 17 additions & 8 deletions src/Series.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2604,16 +2604,25 @@ std::string Series::iterationFilename(IterationIndex_t i)
Series::iterations_iterator Series::indexOf(Iteration const &iteration)
{
auto &series = get();
for (auto it = series.iterations.begin(); it != series.iterations.end();
++it)
// first try the cached index; if it points to the correct entry return it
auto idx = iteration.get().m_iterationIndex;
if (!idx.has_value())
{
if (&it->second.Attributable::get() == &iteration.Attributable::get())
{
return it;
}
throw error::Internal("Iteration index not known.");
}

auto it = series.iterations.find(*idx);
if (it != series.iterations.end() &&
&it->second.Attributable::get() == &iteration.Attributable::get())
{
return it;
}
else
{
throw error::Internal(
"Iteration " + std::to_string(*idx) +
" no longer known by the Series?");
}
throw std::runtime_error(
"[Iteration::close] Iteration not found in Series.");
}

AdvanceStatus Series::advance(
Expand Down
36 changes: 36 additions & 0 deletions test/SerialIOTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5404,6 +5404,42 @@ TEST_CASE("variableBasedSingleIteration", "[serial][adios2]")
}
}

#if openPMD_HAVE_ADIOS2_BP5
// verify that the iteration index is cached inside the Iteration object and
// that Series::indexOf can make use of it to find the map entry without
// scanning all iterations.
TEST_CASE("iterationIndexCaching", "[serial][adios2]")
{
using auxiliary::remove_file;
std::string file = "tmp-index-cache.bp5";
// ensure a clean slate
remove_file(file);

// write a few iterations and close them
{
Series s(file, Access::CREATE_LINEAR);
for (uint64_t i = 0; i < 3; ++i)
{
auto &it = s.iterations[i];
it.setTime<double>(static_cast<double>(i));
it.close();
}
s.flush();
}

// reopen read-only and inspect internal index
{
Series s(file, Access::READ_ONLY);
for (auto &pair : s.iterations)
{
auto &iter = pair.second;
REQUIRE(iter.getCachedIterationIndex() == pair.first);
}
}

remove_file(file);
}
#endif
namespace epsilon
{
template <typename T>
Expand Down
Loading