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

Tomasz Kaminski tkaminsk@redhat.com
Wed Jul 29 11:08:44 GMT 2026


On Wed, Jul 29, 2026 at 1:05 PM Jonathan Wakely <jwakely@redhat.com> wrote:

> "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.
>
I will just change it to "eliminates".

>
> >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
> >
> >
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://gcc.gnu.org/pipermail/libstdc++/attachments/20260729/a5fa9abf/attachment.htm>


More information about the Libstdc++ mailing list