[PATCH] libstdc++: Rsolve UNTIL save adjustment at tzdb loading time [PR116110]

Jonathan Wakely jwakely@redhat.com
Wed Jul 29 11:05:41 GMT 2026


"Resolve" in the first line of the commit.

On Tue, 28 Jul 2026 at 19:10 +0200, Tomasz Kamiński wrote:
>This patch moves the save calculation (ZoneInfo::calc_save) to database
>loading code (reload_tzdb) instead of applying it on demand when zone
>is queried (time_zone::_M_get_sys_info). This nivelates the performance

I'm not sure what nivelates was meant to be.

>inpact on non-first calls (that return the chached) result, caused by

"impact", "cached"

>iterator adjustment checks.
>
>Local perfomance test indicate an 10% (30ns to 33ns on average) for cached

"an 10%" -> "a 10% cost"

>queries with on-demand implementation (after r17-2466-g020e02fcf28), combined
>with huge swings on time on first calls. This patch leads 200ms increase
>(1.95s to 2.15s) on time of reload_tzdb, that happens only during initial
>load (and later explicit reload).
>
>As we need two bits of state (expanded or unitl_pending), I have decied

"until_pending", "decided"

>to keep the four value m_state enum.
>
>	PR libstdc++/116110
>
>libstdc++-v3/ChangeLog:
>
>	* src/c++20/tzdb.cc (time_zone::_M_get_sys_info): Remove
>	ZoneInfo::calc_save invocaiton and related iterator adjustment.

"invocation"

>	(chrono::reload_tzdb): Calculate save (invoke calc_save) for
>	all infos on all zones.
>---
>This goes back to the initial patch from Álvaro Begué, as it turned out
>the cost of calculating save is much smaller than actually parsing the
>data. And this way we avoid performance impact on second or later calls.
>
>For the reload_tzdb I have measure this by changing the implementation
>to ignore remote_version check, and load tzdb regardless. I have
>preserved the check in _S_repleace_head, so the zone was dropped after
>loading (so we do not increase memory consumption). the results I got
>where:
>w/calc_save    2151407 ns      2141084 ns          333
>wo/calc_save   1959904 ns      1951554 ns          364
>
>To measure I have measuared call get_info for all know zones for
>on three randomly picked dates: 1970y/December/31, 2020y/January/14,
>2026y/July/28.
>With the non-modified implementation this produced a cached result
>(only first call was exporting). To measure the first call I have again
>modified the implementation to not store new_info and not to mark
>SaveKnow (I have tried creating list of same tzdbs, but variance betwee
>runs was two high).
>
>This gave me following results, averaginhg from all zones
>                Cached   First     %AVG(-Base/First)
>Base		30.90ns  1,630.86
>Lazy (before)	33.92ns  1,924.59  176.35%
>At_parse (this)	31.38ns  1,685.57  1.98%
>
>In general I think this is much better approach, and something
>we could backport to GCC-16.
>
>Testing on x86-64_linux. OK for trunk when all test passes?

OK with the typos above fixed.

>I will create an separate GCC-16 patch with original and follow-up
>merged.
>
> libstdc++-v3/src/c++20/tzdb.cc | 25 +++++++------------------
> 1 file changed, 7 insertions(+), 18 deletions(-)
>
>diff --git a/libstdc++-v3/src/c++20/tzdb.cc b/libstdc++-v3/src/c++20/tzdb.cc
>index 6fe3c6cfee6..e358b778e2d 100644
>--- a/libstdc++-v3/src/c++20/tzdb.cc
>+++ b/libstdc++-v3/src/c++20/tzdb.cc
>@@ -1069,35 +1069,19 @@ namespace std::chrono
>     // Find the transition info for the time point.
>     auto i = ranges::upper_bound(infos, tp, ranges::less{}, &ZoneInfo::until);
>
>-    // Perform the comparison on save adjusted until values (if needed)
>-    // Assume that applying the save will not change relative order of
>-    // ZoneInfo objects.
>-    if (i != infos.begin() && i[-1].calc_save(node->rules) && (i[-1].until() > tp))
>-      --i;
>-    else if (i != infos.end() && i->calc_save(node->rules) && (i->until() <= tp))
>-      ++i;
>-
>     if (i == infos.end())
>       {
> 	if (infos.empty())
> 	  __throw_runtime_error("std::chrono::time_zone::get_info: invalid data");
>-	(--i)->calc_save(node->rules);
>-	tp = i->until();
>+	tp = (--i)->until();
>       }
>-    else // Guarantee that i->until() is correct
>-      i->calc_save(node->rules);
>-
>
>     sys_info info;
>
>     if (i == infos.begin())
>       info.begin = sys_days(year::min()/January/1);
>     else
>-      {
>-	ZoneInfo& prev = i[-1];
>-	prev.calc_save(node->rules);
>-	info.begin = prev.until();
>-      }
>+      info.begin = i[-1].until();
>
>     if (i->to(info)) // We already know a sys_info for this time.
>       return info;
>@@ -2050,6 +2034,11 @@ constinit tzdb_list::_Node::NumLeapSeconds tzdb_list::_Node::num_leap_seconds;
>       return lhs.save < rhs.save;
>     });
>
>+    // Calculate the SAVE value at UNTIL, and adjust it if necessary.
>+    for (time_zone& tz : node->db.zones)
>+      for (ZoneInfo& info : tz._M_impl->infos)
>+	info.calc_save(node->rules);
>+
>     return Node::_S_replace_head(std::move(head), std::move(node));
> #else
>     __throw_disabled();
>-- 
>2.55.0
>
>



More information about the Libstdc++ mailing list