diff --git a/include/openPMD/Iteration.hpp b/include/openPMD/Iteration.hpp index c66199c46f..e2a4e6b1a5 100644 --- a/include/openPMD/Iteration.hpp +++ b/include/openPMD/Iteration.hpp @@ -129,6 +129,15 @@ namespace internal */ StepStatus m_stepStatus = StepStatus::NoStep; + /** + * Cached copy of the key under which this Iteration lives in + * Series::iterations. Populated when the iteration + * object is created/inserted. This allows constant-time lookup + * of the owning map entry instead of a linear scan in + * Series::indexOf(). + */ + std::optional m_iterationIndex = 0; + /** * Information on a parsing request that has not yet been executed. * Otherwise empty. @@ -153,6 +162,8 @@ class Iteration : public Attributable friend class Writable; friend class StatefulIterator; friend class StatefulSnapshotsContainer; + template + friend struct traits::GenerationPolicy; public: Iteration(Iteration const &) = default; @@ -276,6 +287,16 @@ class Iteration : public Attributable private: Iteration(); + /** + * @brief Get the cached iteration index. + * This is the key under which this iteration is stored in the + * Series::iterations map. Used internally for testing the index + * caching optimization. + * + * @return The cached iteration index. + */ + uint64_t getCachedIterationIndex() const; + using Data_t = internal::IterationData; std::shared_ptr m_iterationData; @@ -431,6 +452,20 @@ class Iteration : public Attributable void runDeferredParseAccess(); }; // Iteration +namespace traits +{ + template <> + struct GenerationPolicy + { + constexpr static bool is_noop = false; + template + void operator()(Iterator &it) + { + it->second.get().m_iterationIndex = it->first; + } + }; +} // namespace traits + extern template float Iteration::time() const; extern template double Iteration::time() const; diff --git a/include/openPMD/ParticleSpecies.hpp b/include/openPMD/ParticleSpecies.hpp index 4f309c0f2a..7309afddef 100644 --- a/include/openPMD/ParticleSpecies.hpp +++ b/include/openPMD/ParticleSpecies.hpp @@ -62,9 +62,9 @@ namespace traits { constexpr static bool is_noop = false; template - void operator()(T &ret) + void operator()(T &it) { - ret.particlePatches.linkHierarchy(ret.writable()); + it->second.particlePatches.linkHierarchy(it->second.writable()); } }; } // namespace traits diff --git a/include/openPMD/backend/ContainerImpl.tpp b/include/openPMD/backend/ContainerImpl.tpp index 0384333e10..de11c91f14 100644 --- a/include/openPMD/backend/ContainerImpl.tpp +++ b/include/openPMD/backend/ContainerImpl.tpp @@ -140,7 +140,8 @@ auto Container::operator[](key_type const &key) T t = T(); t.linkHierarchy(writable()); - auto &ret = container().insert({key, std::move(t)}).first->second; + auto inserted_iterator = container().insert({key, std::move(t)}).first; + auto &ret = inserted_iterator->second; if constexpr (std::is_same_v) { ret.writable().ownKeyWithinParent = key; @@ -150,7 +151,7 @@ auto Container::operator[](key_type const &key) ret.writable().ownKeyWithinParent = std::to_string(key); } traits::GenerationPolicy gen; - gen(ret); + gen(inserted_iterator); return ret; } } @@ -172,7 +173,8 @@ auto Container::operator[](key_type &&key) T t = T(); t.linkHierarchy(writable()); - auto &ret = container().insert({key, std::move(t)}).first->second; + auto inserted_iterator = container().insert({key, std::move(t)}).first; + auto &ret = inserted_iterator->second; if constexpr (std::is_same_v) { ret.writable().ownKeyWithinParent = std::move(key); @@ -182,7 +184,7 @@ auto Container::operator[](key_type &&key) ret.writable().ownKeyWithinParent = std::to_string(std::move(key)); } traits::GenerationPolicy gen; - gen(ret); + gen(inserted_iterator); return ret; } } diff --git a/src/Iteration.cpp b/src/Iteration.cpp index d668ac3c08..83c4c3818b 100644 --- a/src/Iteration.cpp +++ b/src/Iteration.cpp @@ -59,6 +59,16 @@ Iteration::Iteration() : Attributable(NoInit()) particles.writable().ownKeyWithinParent = "particles"; } +uint64_t Iteration::getCachedIterationIndex() const +{ + auto idx = get().m_iterationIndex; + if (!idx.has_value()) + { + throw error::Internal("Iteration index not known."); + } + return *idx; +} + template Iteration &Iteration::setTime(T newTime) { diff --git a/src/Series.cpp b/src/Series.cpp index 70a17921a5..32595f42bc 100644 --- a/src/Series.cpp +++ b/src/Series.cpp @@ -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(