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(