Skip to content

Commit 14b628a

Browse files
committed
attempt to fix issue #1859 by caching the index and avoid indexOf() call
for iterations
1 parent cf3e1c5 commit 14b628a

4 files changed

Lines changed: 95 additions & 3 deletions

File tree

include/openPMD/Iteration.hpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,15 @@ namespace internal
129129
*/
130130
StepStatus m_stepStatus = StepStatus::NoStep;
131131

132+
/**
133+
* Cached copy of the key under which this Iteration lives in
134+
* <code>Series::iterations</code>. Populated when the iteration
135+
* object is created/inserted. This allows constant-time lookup
136+
* of the owning map entry instead of a linear scan in
137+
* <code>Series::indexOf()</code>.
138+
*/
139+
uint64_t m_iterationIndex = 0;
140+
132141
/**
133142
* Information on a parsing request that has not yet been executed.
134143
* Otherwise empty.
@@ -246,6 +255,18 @@ class Iteration : public Attributable
246255
*/
247256
bool closed() const;
248257

258+
/**
259+
* @brief Get the cached iteration index.
260+
* This is the key under which this iteration is stored in the
261+
* Series::iterations map. Used for testing the index caching
262+
* optimization.
263+
*
264+
* @return The cached iteration index.
265+
*/
266+
uint64_t getCachedIterationIndex() const
267+
{
268+
return get().m_iterationIndex;
269+
}
249270
/**
250271
* @brief Has the iteration been parsed yet?
251272
If not, it will contain no structure yet.

include/openPMD/backend/ContainerImpl.tpp

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
*/
2121

2222
#include "openPMD/backend/Container.hpp"
23-
23+
#include "openPMD/Iteration.hpp" // needed for index caching in operator[]
2424
/*
2525
* Instantiations in src/backend/Container.cpp
2626
* This file exists so that our tests can include the Container class with
@@ -149,6 +149,12 @@ auto Container<T, T_key, T_container>::operator[](key_type const &key)
149149
{
150150
ret.writable().ownKeyWithinParent = std::to_string(key);
151151
}
152+
// remember our key inside the iteration object itself so that
153+
// Series::indexOf becomes constant/ log-time instead of linear.
154+
if constexpr (std::is_same_v<T, Iteration>)
155+
{
156+
ret.get().m_iterationIndex = key;
157+
}
152158
traits::GenerationPolicy<T> gen;
153159
gen(ret);
154160
return ret;
@@ -181,6 +187,10 @@ auto Container<T, T_key, T_container>::operator[](key_type &&key)
181187
{
182188
ret.writable().ownKeyWithinParent = std::to_string(std::move(key));
183189
}
190+
if constexpr (std::is_same_v<T, Iteration>)
191+
{
192+
ret.get().m_iterationIndex = key;
193+
}
184194
traits::GenerationPolicy<T> gen;
185195
gen(ret);
186196
return ret;
@@ -201,13 +211,23 @@ template <typename T, typename T_key, typename T_container>
201211
auto Container<T, T_key, T_container>::insert(value_type const &value)
202212
-> std::pair<iterator, bool>
203213
{
204-
return container().insert(value);
214+
auto res = container().insert(value);
215+
if constexpr (std::is_same_v<T, Iteration>)
216+
{
217+
res.first->second.get().m_iterationIndex = res.first->first;
218+
}
219+
return res;
205220
}
206221
template <typename T, typename T_key, typename T_container>
207222
auto Container<T, T_key, T_container>::insert(value_type &&value)
208223
-> std::pair<iterator, bool>
209224
{
210-
return container().insert(value);
225+
auto res = container().insert(value);
226+
if constexpr (std::is_same_v<T, Iteration>)
227+
{
228+
res.first->second.get().m_iterationIndex = res.first->first;
229+
}
230+
return res;
211231
}
212232
template <typename T, typename T_key, typename T_container>
213233
auto Container<T, T_key, T_container>::insert(

src/Series.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2604,11 +2604,26 @@ std::string Series::iterationFilename(IterationIndex_t i)
26042604
Series::iterations_iterator Series::indexOf(Iteration const &iteration)
26052605
{
26062606
auto &series = get();
2607+
// first try the cached index; if it points to the correct entry return it
2608+
auto idx = iteration.get().m_iterationIndex;
2609+
if (idx != 0) // zero is default/unset but index 0 is valid, so we still check
2610+
{
2611+
auto it = series.iterations.find(idx);
2612+
if (it != series.iterations.end() &&
2613+
&it->second.Attributable::get() == &iteration.Attributable::get())
2614+
{
2615+
return it;
2616+
}
2617+
// if the cached index is stale (shouldn't happen), fall back
2618+
}
2619+
// fallback to linear scan for safety
26072620
for (auto it = series.iterations.begin(); it != series.iterations.end();
26082621
++it)
26092622
{
26102623
if (&it->second.Attributable::get() == &iteration.Attributable::get())
26112624
{
2625+
// update cache so future calls are fast
2626+
it->second.get().m_iterationIndex = it->first;
26122627
return it;
26132628
}
26142629
}

test/SerialIOTest.cpp

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5404,6 +5404,42 @@ TEST_CASE("variableBasedSingleIteration", "[serial][adios2]")
54045404
}
54055405
}
54065406

5407+
5408+
// verify that the iteration index is cached inside the Iteration object and
5409+
// that Series::indexOf can make use of it to find the map entry without
5410+
// scanning all iterations.
5411+
TEST_CASE("iterationIndexCaching", "[serial][adios2]")
5412+
{
5413+
using auxiliary::remove_file;
5414+
std::string file = "tmp-index-cache.bp5";
5415+
// ensure a clean slate
5416+
remove_file(file);
5417+
5418+
// write a few iterations and close them
5419+
{
5420+
Series s(file, Access::CREATE_LINEAR);
5421+
for (uint64_t i = 0; i < 3; ++i)
5422+
{
5423+
auto &it = s.iterations[i];
5424+
it.setTime<double>(static_cast<double>(i));
5425+
it.close();
5426+
}
5427+
s.flush();
5428+
}
5429+
5430+
// reopen read-only and inspect internal index
5431+
{
5432+
Series s(file, Access::READ_ONLY);
5433+
for (auto &pair : s.iterations)
5434+
{
5435+
auto &iter = pair.second;
5436+
REQUIRE(iter.getCachedIterationIndex() == pair.first);
5437+
}
5438+
}
5439+
5440+
remove_file(file);
5441+
}
5442+
54075443
namespace epsilon
54085444
{
54095445
template <typename T>

0 commit comments

Comments
 (0)