Recover the speed lost making posixtz.typeAt correct #6
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Fixing a correctness bug in
773f3a1made the POSIX rule path a lot slower,and the speed is worth getting back.
posixtz.typeAtused to search one year rather than three, which wasabout a quarter quicker, and it was wrong: a switch time may be up to 167
hours either side of its day and spill into a neighbouring year, and two
switches landing in the same week can swap order from year to year. Both
shapes came out of fuzzing, neither out of reading it, so
typeAtnowdefers to
spanAtrather than growing a guard for each case.Two things are behind it.
spanAtcomputes six switches wheretypeAtcomputed two, each one a weekday calculation and a day-number conversion.
And
Date.toDaysSinceStartOfEranow does its arithmetic in the wider type,which it has to: the old one overflowed an i32 from about year 5.9 million.
Carrying each switch with what it opens, rather than deriving that again
from the answer, already took some of it back. What is left, in the order I
would try it:
Compute the neighbouring years lazily. The instant's own year needs two
switches; the year before is only needed when the instant sits before
both of them, and the year after only when it sits after both. That is
two calculations in the middle of a span and four near its ends, rather
than six always.
Narrow the calendar arithmetic again where it is provably safe. Only
the year shift at the start and the era multiplication at the end need
the wider type;
year_of_era,day_of_yearandday_of_eraare allsmall and could stay 32-bit.
Reconsider whether
spanAtneeds a sort at all. Six switches arrive intwo interleaved ascending runs, so a merge would do.
Whatever comes of it, the fuzz targets in
src/fuzz.zigare what say it isstill right:
zig build test -Dfuzz-iterations=500000exercises the rulesthat broke the old shortcut.
Done in
85e1f61.resolveends up well ahead of where it started;atTimestampis theone still short, by about 8%.
Three things, roughly the plan in the issue:
Rule.dayNumbertakesthe year's first of January and returns the day the rule picks out, so
placing a switch no longer converts a date, and the three years share
one conversion -- the neighbours are that day stepped by a year's
length.
Date.yearAndFirstDayis that one conversion: civil-from-days stoppedshort of the month and day, which were only being added back up again
to recover the first of January. This was the largest single win.
proven rather than assumed. When the year's own two switches are more
than ten days inside it, no switch of a neighbouring year can fall
between them, since a switch time is at most 167 hours off its day and
an offset at most 25 hours. An instant between them needs no neighbour;
one outside them needs only the side it fell off of. So the switch set
is two, four or six rather than always six.
The sort is gone as well -- one pass for the nearest switch either side.
The reasoning in (3) is the part that could be wrong the way the original
typeAtwas wrong, sospanAtScanningkeeps the version that reasonsabout nothing and evaluates all three years, and a test holds
spanAtagainst it over every shape of rule this file, its tests and the fuzzer's
seeds have thought of, swept second by second through the switches and the
turns of the year. Each of the four claims the shortcut rests on was
reintroduced as a bug and the test caught all four. The ten day margin is
covered by a rule whose switch lands on the first of January in one year
and the December before it in the next, which a margin of zero gets wrong.
Verified with the three oracles, 1.8M mutated inputs across six seeds, and
the debug, ReleaseSafe, ReleaseFast,
-Dembed-tzdataand-Dno-system-tzdatabuilds.