Skip to content

Improve the performance of ArithmeticDate.until()#7739

Merged
Manishearth merged 3 commits intounicode-org:mainfrom
poulsbo:fix/calendar-until-arithmetic
Mar 6, 2026
Merged

Improve the performance of ArithmeticDate.until()#7739
Manishearth merged 3 commits intounicode-org:mainfrom
poulsbo:fix/calendar-until-arithmetic

Conversation

@poulsbo
Copy link
Contributor

@poulsbo poulsbo commented Mar 5, 2026

Progress on #7077

Improve the performnace of the calendar until() function.

  • Tighten the estimation of the number of months between two dates in order to reduce the number of incremental surpasses() tests. This is done by introducing a new DateFieldsResolver function, min_months_from(), which the Coptic, Ethiopic, and Hebrew calendar implementations customize.

  • Eliminate the surpasses() loop for the final days delta calculation. Instead compute an intermediate date, which is the start date plus the years, months, and weeks computed so far. Subtract the Rata Die of the intermediate date from that of the target date to get the days field of the DateDuration.

  • Fix minor typos in function names and comments.

Performance, as measured using criterion, is improved for Years and Months intervals. Improvements on the order of 20% to 80%, or 100s of ns to >1 ms, are shown, for base times of 500 ns to 5 ms.

For Weeks and Days, which are already computed efficiently using RD subtraction, criterion measures a slight slowdown (on the order of 1 ns, for a 15-20 ns operation). This is most likely due to a change in compiler inlining, optimization, register usage, etc., since the code for Days/Weeks has not changed.

Changelog: N/A

(covered by other PRs)

Improve the performnace of the calendar `until()` function.

- Tighten the estimation of the number of months between two dates
  in order to reduce the number of incremental `surpasses()` tests.
  This is done by introducing a new DateFieldsResolver function,
  `min_months_from()`, which the Coptic, Ethiopic, and Hebrew
  calendar implementations customize.

- Eliminate the `surpasses()` loop for the final days delta calculation.
  Instead compute an intermediate date, which is the start date plus
  the years, months, and weeks computed so far. Subtract the Rata Die
  of the intermediate date from that of the target date to get the
  days field of the DateDuration.

- Fix minor typos in function names and comments.

Performance, as measured using criterion, is improved for Years and
Months intervals. Improvements on the order of 20% to 80%, or 100s
of ns to >1 ms, are shown, for base times of 500 ns to 5 ms.

For Weeks and Days, which are already computed
efficiently using RD subtraction, criterion measures a slight slowdown
(on the order of 1 ns, for a 15-20 ns operation). This is most likely
due to a change in compiler inlining, optimization, register usage,
etc., since the code for Days/Weeks has not changed.
@poulsbo poulsbo requested review from a team, Manishearth and sffc as code owners March 5, 2026 02:21
@poulsbo
Copy link
Contributor Author

poulsbo commented Mar 5, 2026

Output of cargo bench --all-features --bench until -- --baseline pre-alan2 (after first running cargo bench --all-features --bench until -- --save-baseline pre-alan2).

Many regressions are in the 1-2 ns range. For example, this seems to be 11.7 ns -> 13.2 ns, delta = +1.5 ns:

                        time:   [13.164 ns 13.203 ns 13.245 ns]
                        change: [+11.784% +12.212% +12.631%] (p = 0.00 < 0.05)

Code for until/weeks and until/days has not changed, so regressions are due to something else, like change in the function size. I thought it might be noise from running benchmarks on a virtual workstation, but the results held up over two separate runs (each taking almost an hour total).

Here's the full output.

   Compiling icu_calendar v2.1.1 (/usr/local/google/home/liualan/icu4x/components/calendar)
   Compiling icu_time v2.1.1 (/usr/local/google/home/liualan/icu4x/components/time)
   Compiling icu_datetime v2.1.1 (/usr/local/google/home/liualan/icu4x/components/datetime)
   Compiling icu v2.1.1 (/usr/local/google/home/liualan/icu4x/components/icu)
    Finished `bench` profile [optimized] target(s) in 1m 04s
     Running benches/until.rs (target/release/deps/until-11fac7c771c9fefd)
Benchmarking until/years/calendar/iso/far_past
Benchmarking until/years/calendar/iso/far_past: Warming up for 3.0000 s
Benchmarking until/years/calendar/iso/far_past: Collecting 100 samples in estimated 5.0001 s (31M iterations)
Benchmarking until/years/calendar/iso/far_past: Analyzing
until/years/calendar/iso/far_past
                        time:   [159.07 ns 159.32 ns 159.61 ns]
                        change: [-66.862% -66.537% -66.302%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 7 outliers among 100 measurements (7.00%)
  5 (5.00%) high mild
  2 (2.00%) high severe
Benchmarking until/years/calendar/iso/very_far_past
Benchmarking until/years/calendar/iso/very_far_past: Warming up for 3.0000 s
Benchmarking until/years/calendar/iso/very_far_past: Collecting 100 samples in estimated 5.0003 s (43M iterations)
Benchmarking until/years/calendar/iso/very_far_past: Analyzing
until/years/calendar/iso/very_far_past
                        time:   [114.45 ns 114.87 ns 115.40 ns]
                        change: [-60.200% -59.976% -59.710%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 8 outliers among 100 measurements (8.00%)
  3 (3.00%) high mild
  5 (5.00%) high severe
Benchmarking until/years/calendar/buddhist/far_past
Benchmarking until/years/calendar/buddhist/far_past: Warming up for 3.0000 s
Benchmarking until/years/calendar/buddhist/far_past: Collecting 100 samples in estimated 5.0002 s (31M iterations)
Benchmarking until/years/calendar/buddhist/far_past: Analyzing
until/years/calendar/buddhist/far_past
                        time:   [159.07 ns 159.52 ns 160.14 ns]
                        change: [-66.694% -66.569% -66.444%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 5 outliers among 100 measurements (5.00%)
  5 (5.00%) high severe
Benchmarking until/years/calendar/buddhist/very_far_past
Benchmarking until/years/calendar/buddhist/very_far_past: Warming up for 3.0000 s
Benchmarking until/years/calendar/buddhist/very_far_past: Collecting 100 samples in estimated 5.0004 s (44M iterations)
Benchmarking until/years/calendar/buddhist/very_far_past: Analyzing
until/years/calendar/buddhist/very_far_past
                        time:   [114.38 ns 114.63 ns 114.89 ns]
                        change: [-60.232% -60.097% -59.950%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 6 outliers among 100 measurements (6.00%)
  1 (1.00%) low mild
  4 (4.00%) high mild
  1 (1.00%) high severe
Benchmarking until/years/calendar/coptic/far_past
Benchmarking until/years/calendar/coptic/far_past: Warming up for 3.0000 s
Benchmarking until/years/calendar/coptic/far_past: Collecting 100 samples in estimated 5.0004 s (43M iterations)
Benchmarking until/years/calendar/coptic/far_past: Analyzing
until/years/calendar/coptic/far_past
                        time:   [118.23 ns 119.15 ns 120.40 ns]
                        change: [-28.726% -26.374% -23.880%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 5 outliers among 100 measurements (5.00%)
  1 (1.00%) high mild
  4 (4.00%) high severe
Benchmarking until/years/calendar/coptic/very_far_past
Benchmarking until/years/calendar/coptic/very_far_past: Warming up for 3.0000 s
Benchmarking until/years/calendar/coptic/very_far_past: Collecting 100 samples in estimated 5.0002 s (58M iterations)
Benchmarking until/years/calendar/coptic/very_far_past: Analyzing
until/years/calendar/coptic/very_far_past
                        time:   [85.279 ns 85.478 ns 85.704 ns]
                        change: [-84.660% -84.488% -84.207%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 8 outliers among 100 measurements (8.00%)
  5 (5.00%) high mild
  3 (3.00%) high severe
Benchmarking until/years/calendar/ethiopic/far_past
Benchmarking until/years/calendar/ethiopic/far_past: Warming up for 3.0000 s
Benchmarking until/years/calendar/ethiopic/far_past: Collecting 100 samples in estimated 5.0006 s (43M iterations)
Benchmarking until/years/calendar/ethiopic/far_past: Analyzing
until/years/calendar/ethiopic/far_past
                        time:   [116.76 ns 116.98 ns 117.23 ns]
                        change: [-32.348% -32.131% -31.928%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 8 outliers among 100 measurements (8.00%)
  1 (1.00%) low mild
  6 (6.00%) high mild
  1 (1.00%) high severe
Benchmarking until/years/calendar/ethiopic/very_far_past
Benchmarking until/years/calendar/ethiopic/very_far_past: Warming up for 3.0000 s
Benchmarking until/years/calendar/ethiopic/very_far_past: Collecting 100 samples in estimated 5.0001 s (59M iterations)
Benchmarking until/years/calendar/ethiopic/very_far_past: Analyzing
until/years/calendar/ethiopic/very_far_past
                        time:   [84.880 ns 85.078 ns 85.283 ns]
                        change: [-84.973% -84.902% -84.839%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 2 outliers among 100 measurements (2.00%)
  2 (2.00%) high mild
Benchmarking until/years/calendar/indian/far_past
Benchmarking until/years/calendar/indian/far_past: Warming up for 3.0000 s
Benchmarking until/years/calendar/indian/far_past: Collecting 100 samples in estimated 5.0000 s (34M iterations)
Benchmarking until/years/calendar/indian/far_past: Analyzing
until/years/calendar/indian/far_past
                        time:   [145.09 ns 145.40 ns 145.74 ns]
                        change: [-71.605% -71.502% -71.385%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 7 outliers among 100 measurements (7.00%)
  4 (4.00%) high mild
  3 (3.00%) high severe
Benchmarking until/years/calendar/indian/very_far_past
Benchmarking until/years/calendar/indian/very_far_past: Warming up for 3.0000 s
Benchmarking until/years/calendar/indian/very_far_past: Collecting 100 samples in estimated 5.0005 s (46M iterations)
Benchmarking until/years/calendar/indian/very_far_past: Analyzing
until/years/calendar/indian/very_far_past
                        time:   [108.65 ns 109.21 ns 110.05 ns]
                        change: [-62.688% -62.468% -62.182%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 9 outliers among 100 measurements (9.00%)
  2 (2.00%) high mild
  7 (7.00%) high severe
Benchmarking until/years/calendar/julian/far_past
Benchmarking until/years/calendar/julian/far_past: Warming up for 3.0000 s
Benchmarking until/years/calendar/julian/far_past: Collecting 100 samples in estimated 5.0005 s (46M iterations)
Benchmarking until/years/calendar/julian/far_past: Analyzing
until/years/calendar/julian/far_past
                        time:   [107.29 ns 107.62 ns 107.96 ns]
                        change: [-60.765% -60.610% -60.446%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 4 outliers among 100 measurements (4.00%)
  1 (1.00%) low mild
  1 (1.00%) high mild
  2 (2.00%) high severe
Benchmarking until/years/calendar/julian/very_far_past
Benchmarking until/years/calendar/julian/very_far_past: Warming up for 3.0000 s
Benchmarking until/years/calendar/julian/very_far_past: Collecting 100 samples in estimated 5.0001 s (57M iterations)
Benchmarking until/years/calendar/julian/very_far_past: Analyzing
until/years/calendar/julian/very_far_past
                        time:   [87.014 ns 87.269 ns 87.549 ns]
                        change: [-85.931% -85.846% -85.758%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 7 outliers among 100 measurements (7.00%)
  5 (5.00%) high mild
  2 (2.00%) high severe
Benchmarking until/years/calendar/chinese_cached/far_past
Benchmarking until/years/calendar/chinese_cached/far_past: Warming up for 3.0000 s
Benchmarking until/years/calendar/chinese_cached/far_past: Collecting 100 samples in estimated 5.0008 s (4.8M iterations)
Benchmarking until/years/calendar/chinese_cached/far_past: Analyzing
until/years/calendar/chinese_cached/far_past
                        time:   [1.0361 µs 1.0408 µs 1.0474 µs]
                        change: [-79.851% -79.740% -79.595%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 4 outliers among 100 measurements (4.00%)
  1 (1.00%) high mild
  3 (3.00%) high severe
Benchmarking until/years/calendar/chinese_cached/very_far_past
Benchmarking until/years/calendar/chinese_cached/very_far_past: Warming up for 3.0000 s
Benchmarking until/years/calendar/chinese_cached/very_far_past: Collecting 100 samples in estimated 5.0029 s (7.8M iterations)
Benchmarking until/years/calendar/chinese_cached/very_far_past: Analyzing
until/years/calendar/chinese_cached/very_far_past
                        time:   [639.42 ns 640.47 ns 641.56 ns]
                        change: [-79.883% -79.795% -79.717%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 5 outliers among 100 measurements (5.00%)
  2 (2.00%) high mild
  3 (3.00%) high severe
Benchmarking until/years/calendar/gregorian/far_past
Benchmarking until/years/calendar/gregorian/far_past: Warming up for 3.0000 s
Benchmarking until/years/calendar/gregorian/far_past: Collecting 100 samples in estimated 5.0005 s (31M iterations)
Benchmarking until/years/calendar/gregorian/far_past: Analyzing
until/years/calendar/gregorian/far_past
                        time:   [158.69 ns 159.02 ns 159.41 ns]
                        change: [-66.379% -66.258% -66.139%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 5 outliers among 100 measurements (5.00%)
  3 (3.00%) high mild
  2 (2.00%) high severe
Benchmarking until/years/calendar/gregorian/very_far_past
Benchmarking until/years/calendar/gregorian/very_far_past: Warming up for 3.0000 s
Benchmarking until/years/calendar/gregorian/very_far_past: Collecting 100 samples in estimated 5.0004 s (44M iterations)
Benchmarking until/years/calendar/gregorian/very_far_past: Analyzing
until/years/calendar/gregorian/very_far_past
                        time:   [114.17 ns 116.78 ns 120.30 ns]
                        change: [-60.289% -59.955% -59.395%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 7 outliers among 100 measurements (7.00%)
  2 (2.00%) high mild
  5 (5.00%) high severe
Benchmarking until/years/calendar/hebrew/far_past
Benchmarking until/years/calendar/hebrew/far_past: Warming up for 3.0000 s
Benchmarking until/years/calendar/hebrew/far_past: Collecting 100 samples in estimated 5.0006 s (15M iterations)
Benchmarking until/years/calendar/hebrew/far_past: Analyzing
until/years/calendar/hebrew/far_past
                        time:   [321.42 ns 324.18 ns 327.02 ns]
                        change: [-83.636% -83.549% -83.465%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 10 outliers among 100 measurements (10.00%)
  6 (6.00%) high mild
  4 (4.00%) high severe
Benchmarking until/years/calendar/hebrew/very_far_past
Benchmarking until/years/calendar/hebrew/very_far_past: Warming up for 3.0000 s
Benchmarking until/years/calendar/hebrew/very_far_past: Collecting 100 samples in estimated 5.0003 s (14M iterations)
Benchmarking until/years/calendar/hebrew/very_far_past: Analyzing
until/years/calendar/hebrew/very_far_past
                        time:   [346.80 ns 347.45 ns 348.13 ns]
                        change: [-79.637% -79.526% -79.433%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 6 outliers among 100 measurements (6.00%)
  5 (5.00%) high mild
  1 (1.00%) high severe
Benchmarking until/years/calendar/islamic/observational/far_past
Benchmarking until/years/calendar/islamic/observational/far_past: Warming up for 3.0000 s
Benchmarking until/years/calendar/islamic/observational/far_past: Collecting 100 samples in estimated 5.0010 s (16M iterations)
Benchmarking until/years/calendar/islamic/observational/far_past: Analyzing
until/years/calendar/islamic/observational/far_past
                        time:   [311.31 ns 312.13 ns 313.05 ns]
                        change: [-80.259% -80.182% -80.111%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 8 outliers among 100 measurements (8.00%)
  1 (1.00%) low mild
  6 (6.00%) high mild
  1 (1.00%) high severe
Benchmarking until/years/calendar/islamic/observational/very_far_past
Benchmarking until/years/calendar/islamic/observational/very_far_past: Warming up for 3.0000 s
Benchmarking until/years/calendar/islamic/observational/very_far_past: Collecting 100 samples in estimated 5.0008 s (13M iterations)
Benchmarking until/years/calendar/islamic/observational/very_far_past: Analyzing
until/years/calendar/islamic/observational/very_far_past
                        time:   [384.17 ns 385.68 ns 387.81 ns]
                        change: [-16.221% -15.914% -15.577%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 7 outliers among 100 measurements (7.00%)
  5 (5.00%) high mild
  2 (2.00%) high severe
Benchmarking until/years/calendar/islamic/civil/far_past
Benchmarking until/years/calendar/islamic/civil/far_past: Warming up for 3.0000 s
Benchmarking until/years/calendar/islamic/civil/far_past: Collecting 100 samples in estimated 5.0009 s (16M iterations)
Benchmarking until/years/calendar/islamic/civil/far_past: Analyzing
until/years/calendar/islamic/civil/far_past
                        time:   [313.20 ns 314.82 ns 317.41 ns]
                        change: [-80.577% -80.503% -80.421%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 2 outliers among 100 measurements (2.00%)
  1 (1.00%) high mild
  1 (1.00%) high severe
Benchmarking until/years/calendar/islamic/civil/very_far_past
Benchmarking until/years/calendar/islamic/civil/very_far_past: Warming up for 3.0000 s
Benchmarking until/years/calendar/islamic/civil/very_far_past: Collecting 100 samples in estimated 5.0004 s (13M iterations)
Benchmarking until/years/calendar/islamic/civil/very_far_past: Analyzing
until/years/calendar/islamic/civil/very_far_past
                        time:   [385.74 ns 386.63 ns 387.69 ns]
                        change: [-18.270% -17.965% -17.677%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 3 outliers among 100 measurements (3.00%)
  2 (2.00%) high mild
  1 (1.00%) high severe
Benchmarking until/years/calendar/islamic/ummalqura/far_past
Benchmarking until/years/calendar/islamic/ummalqura/far_past: Warming up for 3.0000 s
Benchmarking until/years/calendar/islamic/ummalqura/far_past: Collecting 100 samples in estimated 5.0008 s (16M iterations)
Benchmarking until/years/calendar/islamic/ummalqura/far_past: Analyzing
until/years/calendar/islamic/ummalqura/far_past
                        time:   [312.14 ns 312.69 ns 313.31 ns]
                        change: [-80.171% -80.105% -80.042%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 9 outliers among 100 measurements (9.00%)
  7 (7.00%) high mild
  2 (2.00%) high severe
Benchmarking until/years/calendar/islamic/ummalqura/very_far_past
Benchmarking until/years/calendar/islamic/ummalqura/very_far_past: Warming up for 3.0000 s
Benchmarking until/years/calendar/islamic/ummalqura/very_far_past: Collecting 100 samples in estimated 5.0013 s (13M iterations)
Benchmarking until/years/calendar/islamic/ummalqura/very_far_past: Analyzing
until/years/calendar/islamic/ummalqura/very_far_past
                        time:   [384.29 ns 385.22 ns 386.18 ns]
                        change: [-16.055% -15.821% -15.569%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 1 outliers among 100 measurements (1.00%)
  1 (1.00%) high mild
Benchmarking until/years/calendar/islamic/tabular/far_past
Benchmarking until/years/calendar/islamic/tabular/far_past: Warming up for 3.0000 s
Benchmarking until/years/calendar/islamic/tabular/far_past: Collecting 100 samples in estimated 5.0010 s (16M iterations)
Benchmarking until/years/calendar/islamic/tabular/far_past: Analyzing
until/years/calendar/islamic/tabular/far_past
                        time:   [311.25 ns 311.82 ns 312.48 ns]
                        change: [-80.868% -80.796% -80.733%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 6 outliers among 100 measurements (6.00%)
  3 (3.00%) high mild
  3 (3.00%) high severe
Benchmarking until/years/calendar/islamic/tabular/very_far_past
Benchmarking until/years/calendar/islamic/tabular/very_far_past: Warming up for 3.0000 s
Benchmarking until/years/calendar/islamic/tabular/very_far_past: Collecting 100 samples in estimated 5.0017 s (13M iterations)
Benchmarking until/years/calendar/islamic/tabular/very_far_past: Analyzing
until/years/calendar/islamic/tabular/very_far_past
                        time:   [384.98 ns 386.20 ns 387.71 ns]
                        change: [-18.885% -18.437% -18.063%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 10 outliers among 100 measurements (10.00%)
  4 (4.00%) high mild
  6 (6.00%) high severe

Benchmarking until/months/calendar/iso/far_past
Benchmarking until/months/calendar/iso/far_past: Warming up for 3.0000 s
Benchmarking until/months/calendar/iso/far_past: Collecting 100 samples in estimated 5.0011 s (14M iterations)
Benchmarking until/months/calendar/iso/far_past: Analyzing
until/months/calendar/iso/far_past
                        time:   [346.01 ns 346.72 ns 347.49 ns]
                        change: [-49.795% -49.638% -49.478%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 4 outliers among 100 measurements (4.00%)
  3 (3.00%) high mild
  1 (1.00%) high severe
Benchmarking until/months/calendar/buddhist/far_past
Benchmarking until/months/calendar/buddhist/far_past: Warming up for 3.0000 s
Benchmarking until/months/calendar/buddhist/far_past: Collecting 100 samples in estimated 5.0001 s (14M iterations)
Benchmarking until/months/calendar/buddhist/far_past: Analyzing
until/months/calendar/buddhist/far_past
                        time:   [344.21 ns 344.94 ns 345.85 ns]
                        change: [-49.966% -49.750% -49.462%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 8 outliers among 100 measurements (8.00%)
  3 (3.00%) high mild
  5 (5.00%) high severe
Benchmarking until/months/calendar/coptic/far_past
Benchmarking until/months/calendar/coptic/far_past: Warming up for 3.0000 s
Benchmarking until/months/calendar/coptic/far_past: Collecting 100 samples in estimated 5.0003 s (44M iterations)
Benchmarking until/months/calendar/coptic/far_past: Analyzing
until/months/calendar/coptic/far_past
                        time:   [113.16 ns 113.36 ns 113.58 ns]
                        change: [-98.589% -98.585% -98.581%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 7 outliers among 100 measurements (7.00%)
  1 (1.00%) low mild
  4 (4.00%) high mild
  2 (2.00%) high severe
Benchmarking until/months/calendar/ethiopic/far_past
Benchmarking until/months/calendar/ethiopic/far_past: Warming up for 3.0000 s
Benchmarking until/months/calendar/ethiopic/far_past: Collecting 100 samples in estimated 5.0002 s (44M iterations)
Benchmarking until/months/calendar/ethiopic/far_past: Analyzing
until/months/calendar/ethiopic/far_past
                        time:   [113.84 ns 114.06 ns 114.31 ns]
                        change: [-98.577% -98.574% -98.570%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 8 outliers among 100 measurements (8.00%)
  1 (1.00%) low mild
  4 (4.00%) high mild
  3 (3.00%) high severe
Benchmarking until/months/calendar/indian/far_past
Benchmarking until/months/calendar/indian/far_past: Warming up for 3.0000 s
Benchmarking until/months/calendar/indian/far_past: Collecting 100 samples in estimated 5.0007 s (14M iterations)
Benchmarking until/months/calendar/indian/far_past: Analyzing
until/months/calendar/indian/far_past
                        time:   [350.78 ns 351.68 ns 352.67 ns]
                        change: [-60.752% -59.370% -57.914%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 6 outliers among 100 measurements (6.00%)
  3 (3.00%) high mild
  3 (3.00%) high severe
Benchmarking until/months/calendar/julian/far_past
Benchmarking until/months/calendar/julian/far_past: Warming up for 3.0000 s
Benchmarking until/months/calendar/julian/far_past: Collecting 100 samples in estimated 5.0004 s (49M iterations)
Benchmarking until/months/calendar/julian/far_past: Analyzing
until/months/calendar/julian/far_past
                        time:   [100.89 ns 101.33 ns 102.02 ns]
                        change: [-63.410% -63.235% -63.040%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 7 outliers among 100 measurements (7.00%)
  4 (4.00%) high mild
  3 (3.00%) high severe
Benchmarking until/months/calendar/chinese_cached/far_past
Benchmarking until/months/calendar/chinese_cached/far_past: Warming up for 3.0000 s
Benchmarking until/months/calendar/chinese_cached/far_past: Collecting 100 samples in estimated 5.4407 s (200 iterations)
Benchmarking until/months/calendar/chinese_cached/far_past: Analyzing
until/months/calendar/chinese_cached/far_past
                        time:   [27.191 ms 27.299 ms 27.437 ms]
                        change: [-6.8553% -6.4722% -5.9893%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 11 outliers among 100 measurements (11.00%)
  7 (7.00%) high mild
  4 (4.00%) high severe
Benchmarking until/months/calendar/gregorian/far_past
Benchmarking until/months/calendar/gregorian/far_past: Warming up for 3.0000 s
Benchmarking until/months/calendar/gregorian/far_past: Collecting 100 samples in estimated 5.0015 s (14M iterations)
Benchmarking until/months/calendar/gregorian/far_past: Analyzing
until/months/calendar/gregorian/far_past
                        time:   [343.24 ns 343.86 ns 344.55 ns]
                        change: [-50.128% -49.982% -49.849%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 6 outliers among 100 measurements (6.00%)
  5 (5.00%) high mild
  1 (1.00%) high severe
Benchmarking until/months/calendar/hebrew/far_past
Benchmarking until/months/calendar/hebrew/far_past: Warming up for 3.0000 s
Benchmarking until/months/calendar/hebrew/far_past: Collecting 100 samples in estimated 5.1283 s (116k iterations)
Benchmarking until/months/calendar/hebrew/far_past: Analyzing
until/months/calendar/hebrew/far_past
                        time:   [44.009 µs 44.127 µs 44.261 µs]
                        change: [-98.194% -98.186% -98.177%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 5 outliers among 100 measurements (5.00%)
  2 (2.00%) high mild
  3 (3.00%) high severe
Benchmarking until/months/calendar/islamic/observational/far_past
Benchmarking until/months/calendar/islamic/observational/far_past: Warming up for 3.0000 s
Benchmarking until/months/calendar/islamic/observational/far_past: Collecting 100 samples in estimated 5.0664 s (66k iterations)
Benchmarking until/months/calendar/islamic/observational/far_past: Analyzing
until/months/calendar/islamic/observational/far_past
                        time:   [77.164 µs 77.349 µs 77.553 µs]
                        change: [-75.824% -75.731% -75.631%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 11 outliers among 100 measurements (11.00%)
  8 (8.00%) high mild
  3 (3.00%) high severe
Benchmarking until/months/calendar/islamic/civil/far_past
Benchmarking until/months/calendar/islamic/civil/far_past: Warming up for 3.0000 s
Benchmarking until/months/calendar/islamic/civil/far_past: Collecting 100 samples in estimated 5.0003 s (15M iterations)
Benchmarking until/months/calendar/islamic/civil/far_past: Analyzing
until/months/calendar/islamic/civil/far_past
                        time:   [343.25 ns 344.73 ns 346.38 ns]
                        change: [-78.751% -78.663% -78.570%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 5 outliers among 100 measurements (5.00%)
  3 (3.00%) high mild
  2 (2.00%) high severe
Benchmarking until/months/calendar/islamic/ummalqura/far_past
Benchmarking until/months/calendar/islamic/ummalqura/far_past: Warming up for 3.0000 s
Benchmarking until/months/calendar/islamic/ummalqura/far_past: Collecting 100 samples in estimated 5.0866 s (66k iterations)
Benchmarking until/months/calendar/islamic/ummalqura/far_past: Analyzing
until/months/calendar/islamic/ummalqura/far_past
                        time:   [77.190 µs 77.378 µs 77.577 µs]
                        change: [-75.853% -75.774% -75.700%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 6 outliers among 100 measurements (6.00%)
  5 (5.00%) high mild
  1 (1.00%) high severe
Benchmarking until/months/calendar/islamic/tabular/far_past
Benchmarking until/months/calendar/islamic/tabular/far_past: Warming up for 3.0000 s
Benchmarking until/months/calendar/islamic/tabular/far_past: Collecting 100 samples in estimated 5.0012 s (15M iterations)
Benchmarking until/months/calendar/islamic/tabular/far_past: Analyzing
until/months/calendar/islamic/tabular/far_past
                        time:   [419.59 ns 450.94 ns 484.90 ns]
                        change: [-76.201% -74.908% -73.415%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 7 outliers among 100 measurements (7.00%)
  6 (6.00%) high mild
  1 (1.00%) high severe

Benchmarking until/weeks/calendar/iso/far_past
Benchmarking until/weeks/calendar/iso/far_past: Warming up for 3.0000 s
Benchmarking until/weeks/calendar/iso/far_past: Collecting 100 samples in estimated 5.0001 s (297M iterations)
Benchmarking until/weeks/calendar/iso/far_past: Analyzing
until/weeks/calendar/iso/far_past
                        time:   [16.748 ns 16.778 ns 16.812 ns]
                        change: [+0.5026% +1.1800% +1.8756%] (p = 0.00 < 0.05)
                        Change within noise threshold.
Found 8 outliers among 100 measurements (8.00%)
  4 (4.00%) high mild
  4 (4.00%) high severe
Benchmarking until/weeks/calendar/iso/very_far_past
Benchmarking until/weeks/calendar/iso/very_far_past: Warming up for 3.0000 s
Benchmarking until/weeks/calendar/iso/very_far_past: Collecting 100 samples in estimated 5.0001 s (295M iterations)
Benchmarking until/weeks/calendar/iso/very_far_past: Analyzing
until/weeks/calendar/iso/very_far_past
                        time:   [16.942 ns 17.000 ns 17.064 ns]
                        change: [+2.7581% +3.2382% +3.7133%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 8 outliers among 100 measurements (8.00%)
  7 (7.00%) high mild
  1 (1.00%) high severe
Benchmarking until/weeks/calendar/buddhist/far_past
Benchmarking until/weeks/calendar/buddhist/far_past: Warming up for 3.0000 s
Benchmarking until/weeks/calendar/buddhist/far_past: Collecting 100 samples in estimated 5.0001 s (294M iterations)
Benchmarking until/weeks/calendar/buddhist/far_past: Analyzing
until/weeks/calendar/buddhist/far_past
                        time:   [16.770 ns 16.810 ns 16.854 ns]
                        change: [-3.8865% -1.3828% +0.8844%] (p = 0.28 > 0.05)
                        No change in performance detected.
Found 7 outliers among 100 measurements (7.00%)
  4 (4.00%) high mild
  3 (3.00%) high severe
Benchmarking until/weeks/calendar/buddhist/very_far_past
Benchmarking until/weeks/calendar/buddhist/very_far_past: Warming up for 3.0000 s
Benchmarking until/weeks/calendar/buddhist/very_far_past: Collecting 100 samples in estimated 5.0001 s (296M iterations)
Benchmarking until/weeks/calendar/buddhist/very_far_past: Analyzing
until/weeks/calendar/buddhist/very_far_past
                        time:   [16.795 ns 16.833 ns 16.876 ns]
                        change: [+2.0481% +2.4631% +2.8769%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 9 outliers among 100 measurements (9.00%)
  6 (6.00%) high mild
  3 (3.00%) high severe
Benchmarking until/weeks/calendar/coptic/far_past
Benchmarking until/weeks/calendar/coptic/far_past: Warming up for 3.0000 s
Benchmarking until/weeks/calendar/coptic/far_past: Collecting 100 samples in estimated 5.0000 s (376M iterations)
Benchmarking until/weeks/calendar/coptic/far_past: Analyzing
until/weeks/calendar/coptic/far_past
                        time:   [13.218 ns 13.308 ns 13.420 ns]
                        change: [+12.322% +12.981% +13.729%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 10 outliers among 100 measurements (10.00%)
  2 (2.00%) high mild
  8 (8.00%) high severe
Benchmarking until/weeks/calendar/coptic/very_far_past
Benchmarking until/weeks/calendar/coptic/very_far_past: Warming up for 3.0000 s
Benchmarking until/weeks/calendar/coptic/very_far_past: Collecting 100 samples in estimated 5.0001 s (378M iterations)
Benchmarking until/weeks/calendar/coptic/very_far_past: Analyzing
until/weeks/calendar/coptic/very_far_past
                        time:   [13.179 ns 13.213 ns 13.251 ns]
                        change: [+11.599% +12.093% +12.610%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 8 outliers among 100 measurements (8.00%)
  5 (5.00%) high mild
  3 (3.00%) high severe
Benchmarking until/weeks/calendar/ethiopic/far_past
Benchmarking until/weeks/calendar/ethiopic/far_past: Warming up for 3.0000 s
Benchmarking until/weeks/calendar/ethiopic/far_past: Collecting 100 samples in estimated 5.0000 s (378M iterations)
Benchmarking until/weeks/calendar/ethiopic/far_past: Analyzing
until/weeks/calendar/ethiopic/far_past
                        time:   [13.164 ns 13.203 ns 13.245 ns]
                        change: [+11.784% +12.212% +12.631%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 7 outliers among 100 measurements (7.00%)
  4 (4.00%) high mild
  3 (3.00%) high severe
Benchmarking until/weeks/calendar/ethiopic/very_far_past
Benchmarking until/weeks/calendar/ethiopic/very_far_past: Warming up for 3.0000 s
Benchmarking until/weeks/calendar/ethiopic/very_far_past: Collecting 100 samples in estimated 5.0000 s (377M iterations)
Benchmarking until/weeks/calendar/ethiopic/very_far_past: Analyzing
until/weeks/calendar/ethiopic/very_far_past
                        time:   [13.162 ns 13.190 ns 13.222 ns]
                        change: [+10.764% +11.472% +12.095%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 7 outliers among 100 measurements (7.00%)
  4 (4.00%) high mild
  3 (3.00%) high severe
Benchmarking until/weeks/calendar/indian/far_past
Benchmarking until/weeks/calendar/indian/far_past: Warming up for 3.0000 s
Benchmarking until/weeks/calendar/indian/far_past: Collecting 100 samples in estimated 5.0001 s (205M iterations)
Benchmarking until/weeks/calendar/indian/far_past: Analyzing
until/weeks/calendar/indian/far_past
                        time:   [24.035 ns 24.114 ns 24.206 ns]
                        change: [+2.2442% +2.8989% +3.5792%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 7 outliers among 100 measurements (7.00%)
  3 (3.00%) high mild
  4 (4.00%) high severe
Benchmarking until/weeks/calendar/indian/very_far_past
Benchmarking until/weeks/calendar/indian/very_far_past: Warming up for 3.0000 s
Benchmarking until/weeks/calendar/indian/very_far_past: Collecting 100 samples in estimated 5.0001 s (207M iterations)
Benchmarking until/weeks/calendar/indian/very_far_past: Analyzing
until/weeks/calendar/indian/very_far_past
                        time:   [24.012 ns 24.106 ns 24.228 ns]
                        change: [+2.4138% +3.0092% +3.5986%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 8 outliers among 100 measurements (8.00%)
  4 (4.00%) high mild
  4 (4.00%) high severe
Benchmarking until/weeks/calendar/julian/far_past
Benchmarking until/weeks/calendar/julian/far_past: Warming up for 3.0000 s
Benchmarking until/weeks/calendar/julian/far_past: Collecting 100 samples in estimated 5.0000 s (511M iterations)
Benchmarking until/weeks/calendar/julian/far_past: Analyzing
until/weeks/calendar/julian/far_past
                        time:   [9.7385 ns 9.7611 ns 9.7859 ns]
                        change: [+1.2149% +1.5046% +1.7984%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 5 outliers among 100 measurements (5.00%)
  4 (4.00%) high mild
  1 (1.00%) high severe
Benchmarking until/weeks/calendar/julian/very_far_past
Benchmarking until/weeks/calendar/julian/very_far_past: Warming up for 3.0000 s
Benchmarking until/weeks/calendar/julian/very_far_past: Collecting 100 samples in estimated 5.0000 s (509M iterations)
Benchmarking until/weeks/calendar/julian/very_far_past: Analyzing
until/weeks/calendar/julian/very_far_past
                        time:   [9.7609 ns 9.7810 ns 9.8029 ns]
                        change: [+1.1303% +1.8250% +2.3655%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 1 outliers among 100 measurements (1.00%)
  1 (1.00%) high severe
Benchmarking until/weeks/calendar/chinese_cached/far_past
Benchmarking until/weeks/calendar/chinese_cached/far_past: Warming up for 3.0000 s
Benchmarking until/weeks/calendar/chinese_cached/far_past: Collecting 100 samples in estimated 5.0001 s (224M iterations)
Benchmarking until/weeks/calendar/chinese_cached/far_past: Analyzing
until/weeks/calendar/chinese_cached/far_past
                        time:   [22.222 ns 22.320 ns 22.468 ns]
                        change: [-0.5867% +0.1606% +1.3944%] (p = 0.81 > 0.05)
                        No change in performance detected.
Found 8 outliers among 100 measurements (8.00%)
  4 (4.00%) high mild
  4 (4.00%) high severe
Benchmarking until/weeks/calendar/chinese_cached/very_far_past
Benchmarking until/weeks/calendar/chinese_cached/very_far_past: Warming up for 3.0000 s
Benchmarking until/weeks/calendar/chinese_cached/very_far_past: Collecting 100 samples in estimated 5.0001 s (223M iterations)
Benchmarking until/weeks/calendar/chinese_cached/very_far_past: Analyzing
until/weeks/calendar/chinese_cached/very_far_past
                        time:   [22.328 ns 22.380 ns 22.437 ns]
                        change: [-0.0573% +0.1914% +0.4631%] (p = 0.17 > 0.05)
                        No change in performance detected.
Found 7 outliers among 100 measurements (7.00%)
  1 (1.00%) low mild
  5 (5.00%) high mild
  1 (1.00%) high severe
Benchmarking until/weeks/calendar/gregorian/far_past
Benchmarking until/weeks/calendar/gregorian/far_past: Warming up for 3.0000 s
Benchmarking until/weeks/calendar/gregorian/far_past: Collecting 100 samples in estimated 5.0000 s (296M iterations)
Benchmarking until/weeks/calendar/gregorian/far_past: Analyzing
until/weeks/calendar/gregorian/far_past
                        time:   [16.788 ns 16.817 ns 16.850 ns]
                        change: [+2.4715% +2.7390% +3.0150%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 7 outliers among 100 measurements (7.00%)
  6 (6.00%) high mild
  1 (1.00%) high severe
Benchmarking until/weeks/calendar/gregorian/very_far_past
Benchmarking until/weeks/calendar/gregorian/very_far_past: Warming up for 3.0000 s
Benchmarking until/weeks/calendar/gregorian/very_far_past: Collecting 100 samples in estimated 5.0001 s (296M iterations)
Benchmarking until/weeks/calendar/gregorian/very_far_past: Analyzing
until/weeks/calendar/gregorian/very_far_past
                        time:   [16.849 ns 16.900 ns 16.955 ns]
                        change: [-12.434% -8.6276% -4.9502%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 9 outliers among 100 measurements (9.00%)
  6 (6.00%) high mild
  3 (3.00%) high severe
Benchmarking until/weeks/calendar/hebrew/far_past
Benchmarking until/weeks/calendar/hebrew/far_past: Warming up for 3.0000 s
Benchmarking until/weeks/calendar/hebrew/far_past: Collecting 100 samples in estimated 5.0001 s (164M iterations)
Benchmarking until/weeks/calendar/hebrew/far_past: Analyzing
until/weeks/calendar/hebrew/far_past
                        time:   [30.515 ns 30.582 ns 30.650 ns]
                        change: [-0.5723% -0.2296% +0.1119%] (p = 0.19 > 0.05)
                        No change in performance detected.
Found 3 outliers among 100 measurements (3.00%)
  2 (2.00%) high mild
  1 (1.00%) high severe
Benchmarking until/weeks/calendar/hebrew/very_far_past
Benchmarking until/weeks/calendar/hebrew/very_far_past: Warming up for 3.0000 s
Benchmarking until/weeks/calendar/hebrew/very_far_past: Collecting 100 samples in estimated 5.0001 s (162M iterations)
Benchmarking until/weeks/calendar/hebrew/very_far_past: Analyzing
until/weeks/calendar/hebrew/very_far_past
                        time:   [30.501 ns 30.557 ns 30.618 ns]
                        change: [+0.0159% +0.4634% +0.8781%] (p = 0.03 < 0.05)
                        Change within noise threshold.
Found 8 outliers among 100 measurements (8.00%)
  4 (4.00%) high mild
  4 (4.00%) high severe
Benchmarking until/weeks/calendar/islamic/observational/far_past
Benchmarking until/weeks/calendar/islamic/observational/far_past: Warming up for 3.0000 s
Benchmarking until/weeks/calendar/islamic/observational/far_past: Collecting 100 samples in estimated 5.0000 s (238M iterations)
Benchmarking until/weeks/calendar/islamic/observational/far_past: Analyzing
until/weeks/calendar/islamic/observational/far_past
                        time:   [20.872 ns 20.919 ns 20.967 ns]
                        change: [+7.0563% +7.3710% +7.7140%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 4 outliers among 100 measurements (4.00%)
  3 (3.00%) high mild
  1 (1.00%) high severe
Benchmarking until/weeks/calendar/islamic/observational/very_far_past
Benchmarking until/weeks/calendar/islamic/observational/very_far_past: Warming up for 3.0000 s
Benchmarking until/weeks/calendar/islamic/observational/very_far_past: Collecting 100 samples in estimated 5.0001 s (238M iterations)
Benchmarking until/weeks/calendar/islamic/observational/very_far_past: Analyzing
until/weeks/calendar/islamic/observational/very_far_past
                        time:   [21.067 ns 21.160 ns 21.284 ns]
                        change: [+7.3583% +8.5957% +10.212%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 6 outliers among 100 measurements (6.00%)
  1 (1.00%) high mild
  5 (5.00%) high severe
Benchmarking until/weeks/calendar/islamic/civil/far_past
Benchmarking until/weeks/calendar/islamic/civil/far_past: Warming up for 3.0000 s
Benchmarking until/weeks/calendar/islamic/civil/far_past: Collecting 100 samples in estimated 5.0000 s (247M iterations)
Benchmarking until/weeks/calendar/islamic/civil/far_past: Analyzing
until/weeks/calendar/islamic/civil/far_past
                        time:   [20.031 ns 20.194 ns 20.461 ns]
                        change: [-15.219% -14.418% -13.682%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 4 outliers among 100 measurements (4.00%)
  3 (3.00%) high mild
  1 (1.00%) high severe
Benchmarking until/weeks/calendar/islamic/civil/very_far_past
Benchmarking until/weeks/calendar/islamic/civil/very_far_past: Warming up for 3.0000 s
Benchmarking until/weeks/calendar/islamic/civil/very_far_past: Collecting 100 samples in estimated 5.0001 s (249M iterations)
Benchmarking until/weeks/calendar/islamic/civil/very_far_past: Analyzing
until/weeks/calendar/islamic/civil/very_far_past
                        time:   [20.052 ns 20.097 ns 20.147 ns]
                        change: [-13.352% -12.991% -12.623%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 7 outliers among 100 measurements (7.00%)
  3 (3.00%) high mild
  4 (4.00%) high severe
Benchmarking until/weeks/calendar/islamic/ummalqura/far_past
Benchmarking until/weeks/calendar/islamic/ummalqura/far_past: Warming up for 3.0000 s
Benchmarking until/weeks/calendar/islamic/ummalqura/far_past: Collecting 100 samples in estimated 5.0001 s (239M iterations)
Benchmarking until/weeks/calendar/islamic/ummalqura/far_past: Analyzing
until/weeks/calendar/islamic/ummalqura/far_past
                        time:   [20.853 ns 20.900 ns 20.952 ns]
                        change: [+6.6172% +6.9794% +7.3270%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 9 outliers among 100 measurements (9.00%)
  5 (5.00%) high mild
  4 (4.00%) high severe
Benchmarking until/weeks/calendar/islamic/ummalqura/very_far_past
Benchmarking until/weeks/calendar/islamic/ummalqura/very_far_past: Warming up for 3.0000 s
Benchmarking until/weeks/calendar/islamic/ummalqura/very_far_past: Collecting 100 samples in estimated 5.0000 s (197M iterations)
Benchmarking until/weeks/calendar/islamic/ummalqura/very_far_past: Analyzing
until/weeks/calendar/islamic/ummalqura/very_far_past
                        time:   [20.870 ns 20.924 ns 20.981 ns]
                        change: [+6.2710% +7.0141% +7.6009%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 4 outliers among 100 measurements (4.00%)
  2 (2.00%) high mild
  2 (2.00%) high severe
Benchmarking until/weeks/calendar/islamic/tabular/far_past
Benchmarking until/weeks/calendar/islamic/tabular/far_past: Warming up for 3.0000 s
Benchmarking until/weeks/calendar/islamic/tabular/far_past: Collecting 100 samples in estimated 5.0000 s (248M iterations)
Benchmarking until/weeks/calendar/islamic/tabular/far_past: Analyzing
until/weeks/calendar/islamic/tabular/far_past
                        time:   [20.093 ns 20.144 ns 20.203 ns]
                        change: [-13.309% -13.016% -12.715%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 6 outliers among 100 measurements (6.00%)
  3 (3.00%) high mild
  3 (3.00%) high severe
Benchmarking until/weeks/calendar/islamic/tabular/very_far_past
Benchmarking until/weeks/calendar/islamic/tabular/very_far_past: Warming up for 3.0000 s
Benchmarking until/weeks/calendar/islamic/tabular/very_far_past: Collecting 100 samples in estimated 5.0000 s (247M iterations)
Benchmarking until/weeks/calendar/islamic/tabular/very_far_past: Analyzing
until/weeks/calendar/islamic/tabular/very_far_past
                        time:   [20.079 ns 20.125 ns 20.176 ns]
                        change: [-13.143% -12.882% -12.617%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 5 outliers among 100 measurements (5.00%)
  4 (4.00%) high mild
  1 (1.00%) high severe

Benchmarking until/days/calendar/iso/far_past
Benchmarking until/days/calendar/iso/far_past: Warming up for 3.0000 s
Benchmarking until/days/calendar/iso/far_past: Collecting 100 samples in estimated 5.0000 s (314M iterations)
Benchmarking until/days/calendar/iso/far_past: Analyzing
until/days/calendar/iso/far_past
                        time:   [15.909 ns 15.949 ns 15.994 ns]
                        change: [+5.6229% +6.1468% +6.6708%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 7 outliers among 100 measurements (7.00%)
  3 (3.00%) high mild
  4 (4.00%) high severe
Benchmarking until/days/calendar/iso/very_far_past
Benchmarking until/days/calendar/iso/very_far_past: Warming up for 3.0000 s
Benchmarking until/days/calendar/iso/very_far_past: Collecting 100 samples in estimated 5.0001 s (314M iterations)
Benchmarking until/days/calendar/iso/very_far_past: Analyzing
until/days/calendar/iso/very_far_past
                        time:   [15.903 ns 15.941 ns 15.985 ns]
                        change: [+6.4034% +6.8137% +7.2103%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 7 outliers among 100 measurements (7.00%)
  5 (5.00%) high mild
  2 (2.00%) high severe
Benchmarking until/days/calendar/buddhist/far_past
Benchmarking until/days/calendar/buddhist/far_past: Warming up for 3.0000 s
Benchmarking until/days/calendar/buddhist/far_past: Collecting 100 samples in estimated 5.0001 s (311M iterations)
Benchmarking until/days/calendar/buddhist/far_past: Analyzing
until/days/calendar/buddhist/far_past
                        time:   [16.017 ns 16.083 ns 16.157 ns]
                        change: [+6.6924% +7.1704% +7.6867%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 7 outliers among 100 measurements (7.00%)
  5 (5.00%) high mild
  2 (2.00%) high severe
Benchmarking until/days/calendar/buddhist/very_far_past
Benchmarking until/days/calendar/buddhist/very_far_past: Warming up for 3.0000 s
Benchmarking until/days/calendar/buddhist/very_far_past: Collecting 100 samples in estimated 5.0001 s (314M iterations)
Benchmarking until/days/calendar/buddhist/very_far_past: Analyzing
until/days/calendar/buddhist/very_far_past
                        time:   [15.882 ns 15.911 ns 15.943 ns]
                        change: [+5.9586% +6.6360% +7.1465%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 5 outliers among 100 measurements (5.00%)
  2 (2.00%) high mild
  3 (3.00%) high severe
Benchmarking until/days/calendar/coptic/far_past
Benchmarking until/days/calendar/coptic/far_past: Warming up for 3.0000 s
Benchmarking until/days/calendar/coptic/far_past: Collecting 100 samples in estimated 5.0000 s (408M iterations)
Benchmarking until/days/calendar/coptic/far_past: Analyzing
until/days/calendar/coptic/far_past
                        time:   [12.170 ns 12.194 ns 12.220 ns]
                        change: [+18.432% +18.960% +19.736%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 8 outliers among 100 measurements (8.00%)
  2 (2.00%) low mild
  4 (4.00%) high mild
  2 (2.00%) high severe
Benchmarking until/days/calendar/coptic/very_far_past
Benchmarking until/days/calendar/coptic/very_far_past: Warming up for 3.0000 s
Benchmarking until/days/calendar/coptic/very_far_past: Collecting 100 samples in estimated 5.0000 s (401M iterations)
Benchmarking until/days/calendar/coptic/very_far_past: Analyzing
until/days/calendar/coptic/very_far_past
                        time:   [12.177 ns 12.206 ns 12.238 ns]
                        change: [+18.391% +18.805% +19.237%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 8 outliers among 100 measurements (8.00%)
  5 (5.00%) high mild
  3 (3.00%) high severe
Benchmarking until/days/calendar/ethiopic/far_past
Benchmarking until/days/calendar/ethiopic/far_past: Warming up for 3.0000 s
Benchmarking until/days/calendar/ethiopic/far_past: Collecting 100 samples in estimated 5.0000 s (409M iterations)
Benchmarking until/days/calendar/ethiopic/far_past: Analyzing
until/days/calendar/ethiopic/far_past
                        time:   [12.148 ns 12.179 ns 12.213 ns]
                        change: [+17.072% +17.798% +18.436%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 11 outliers among 100 measurements (11.00%)
  9 (9.00%) high mild
  2 (2.00%) high severe
Benchmarking until/days/calendar/ethiopic/very_far_past
Benchmarking until/days/calendar/ethiopic/very_far_past: Warming up for 3.0000 s
Benchmarking until/days/calendar/ethiopic/very_far_past: Collecting 100 samples in estimated 5.0001 s (409M iterations)
Benchmarking until/days/calendar/ethiopic/very_far_past: Analyzing
until/days/calendar/ethiopic/very_far_past
                        time:   [12.143 ns 12.174 ns 12.210 ns]
                        change: [+18.723% +19.034% +19.370%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 6 outliers among 100 measurements (6.00%)
  2 (2.00%) high mild
  4 (4.00%) high severe
Benchmarking until/days/calendar/indian/far_past
Benchmarking until/days/calendar/indian/far_past: Warming up for 3.0000 s
Benchmarking until/days/calendar/indian/far_past: Collecting 100 samples in estimated 5.0001 s (226M iterations)
Benchmarking until/days/calendar/indian/far_past: Analyzing
until/days/calendar/indian/far_past
                        time:   [21.951 ns 21.994 ns 22.043 ns]
                        change: [+1.4869% +1.9584% +2.4783%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 8 outliers among 100 measurements (8.00%)
  5 (5.00%) high mild
  3 (3.00%) high severe
Benchmarking until/days/calendar/indian/very_far_past
Benchmarking until/days/calendar/indian/very_far_past: Warming up for 3.0000 s
Benchmarking until/days/calendar/indian/very_far_past: Collecting 100 samples in estimated 5.0000 s (211M iterations)
Benchmarking until/days/calendar/indian/very_far_past: Analyzing
until/days/calendar/indian/very_far_past
                        time:   [22.056 ns 22.132 ns 22.222 ns]
                        change: [+2.0514% +2.3821% +2.7081%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 8 outliers among 100 measurements (8.00%)
  5 (5.00%) high mild
  3 (3.00%) high severe
Benchmarking until/days/calendar/julian/far_past
Benchmarking until/days/calendar/julian/far_past: Warming up for 3.0000 s
Benchmarking until/days/calendar/julian/far_past: Collecting 100 samples in estimated 5.0000 s (581M iterations)
Benchmarking until/days/calendar/julian/far_past: Analyzing
until/days/calendar/julian/far_past
                        time:   [8.5409 ns 8.5587 ns 8.5785 ns]
                        change: [+2.8465% +3.3127% +3.8455%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 7 outliers among 100 measurements (7.00%)
  3 (3.00%) high mild
  4 (4.00%) high severe
Benchmarking until/days/calendar/julian/very_far_past
Benchmarking until/days/calendar/julian/very_far_past: Warming up for 3.0000 s
Benchmarking until/days/calendar/julian/very_far_past: Collecting 100 samples in estimated 5.0000 s (581M iterations)
Benchmarking until/days/calendar/julian/very_far_past: Analyzing
until/days/calendar/julian/very_far_past
                        time:   [8.5449 ns 8.5639 ns 8.5864 ns]
                        change: [+1.8008% +2.3842% +2.8705%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 6 outliers among 100 measurements (6.00%)
  4 (4.00%) high mild
  2 (2.00%) high severe
Benchmarking until/days/calendar/chinese_cached/far_past
Benchmarking until/days/calendar/chinese_cached/far_past: Warming up for 3.0000 s
Benchmarking until/days/calendar/chinese_cached/far_past: Collecting 100 samples in estimated 5.0001 s (250M iterations)
Benchmarking until/days/calendar/chinese_cached/far_past: Analyzing
until/days/calendar/chinese_cached/far_past
                        time:   [19.862 ns 19.899 ns 19.939 ns]
                        change: [-2.7672% -2.4632% -2.1844%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 5 outliers among 100 measurements (5.00%)
  2 (2.00%) high mild
  3 (3.00%) high severe
Benchmarking until/days/calendar/chinese_cached/very_far_past
Benchmarking until/days/calendar/chinese_cached/very_far_past: Warming up for 3.0000 s
Benchmarking until/days/calendar/chinese_cached/very_far_past: Collecting 100 samples in estimated 5.0001 s (250M iterations)
Benchmarking until/days/calendar/chinese_cached/very_far_past: Analyzing
until/days/calendar/chinese_cached/very_far_past
                        time:   [19.828 ns 19.862 ns 19.901 ns]
                        change: [-3.2242% -2.7606% -2.1619%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 8 outliers among 100 measurements (8.00%)
  6 (6.00%) high mild
  2 (2.00%) high severe
Benchmarking until/days/calendar/gregorian/far_past
Benchmarking until/days/calendar/gregorian/far_past: Warming up for 3.0000 s
Benchmarking until/days/calendar/gregorian/far_past: Collecting 100 samples in estimated 5.0000 s (314M iterations)
Benchmarking until/days/calendar/gregorian/far_past: Analyzing
until/days/calendar/gregorian/far_past
                        time:   [15.868 ns 15.900 ns 15.933 ns]
                        change: [+5.5897% +5.9980% +6.3849%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 5 outliers among 100 measurements (5.00%)
  4 (4.00%) high mild
  1 (1.00%) high severe
Benchmarking until/days/calendar/gregorian/very_far_past
Benchmarking until/days/calendar/gregorian/very_far_past: Warming up for 3.0000 s
Benchmarking until/days/calendar/gregorian/very_far_past: Collecting 100 samples in estimated 5.0000 s (314M iterations)
Benchmarking until/days/calendar/gregorian/very_far_past: Analyzing
until/days/calendar/gregorian/very_far_past
                        time:   [15.837 ns 15.865 ns 15.895 ns]
                        change: [+5.5115% +5.8237% +6.1228%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 7 outliers among 100 measurements (7.00%)
  6 (6.00%) high mild
  1 (1.00%) high severe
Benchmarking until/days/calendar/hebrew/far_past
Benchmarking until/days/calendar/hebrew/far_past: Warming up for 3.0000 s
Benchmarking until/days/calendar/hebrew/far_past: Collecting 100 samples in estimated 5.0001 s (170M iterations)
Benchmarking until/days/calendar/hebrew/far_past: Analyzing
until/days/calendar/hebrew/far_past
                        time:   [29.236 ns 29.462 ns 29.734 ns]
                        change: [+1.4054% +2.1546% +2.8725%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 14 outliers among 100 measurements (14.00%)
  2 (2.00%) high mild
  12 (12.00%) high severe
Benchmarking until/days/calendar/hebrew/very_far_past
Benchmarking until/days/calendar/hebrew/very_far_past: Warming up for 3.0000 s
Benchmarking until/days/calendar/hebrew/very_far_past: Collecting 100 samples in estimated 5.0001 s (171M iterations)
Benchmarking until/days/calendar/hebrew/very_far_past: Analyzing
until/days/calendar/hebrew/very_far_past
                        time:   [29.414 ns 29.523 ns 29.642 ns]
                        change: [+1.9759% +2.3193% +2.6701%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 4 outliers among 100 measurements (4.00%)
  3 (3.00%) high mild
  1 (1.00%) high severe
Benchmarking until/days/calendar/islamic/observational/far_past
Benchmarking until/days/calendar/islamic/observational/far_past: Warming up for 3.0000 s
Benchmarking until/days/calendar/islamic/observational/far_past: Collecting 100 samples in estimated 5.0000 s (256M iterations)
Benchmarking until/days/calendar/islamic/observational/far_past: Analyzing
until/days/calendar/islamic/observational/far_past
                        time:   [19.406 ns 19.458 ns 19.512 ns]
                        change: [+7.0261% +7.3230% +7.6554%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 5 outliers among 100 measurements (5.00%)
  3 (3.00%) high mild
  2 (2.00%) high severe
Benchmarking until/days/calendar/islamic/observational/very_far_past
Benchmarking until/days/calendar/islamic/observational/very_far_past: Warming up for 3.0000 s
Benchmarking until/days/calendar/islamic/observational/very_far_past: Collecting 100 samples in estimated 5.0001 s (257M iterations)
Benchmarking until/days/calendar/islamic/observational/very_far_past: Analyzing
until/days/calendar/islamic/observational/very_far_past
                        time:   [19.491 ns 19.560 ns 19.633 ns]
                        change: [+5.9034% +6.9218% +7.6300%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 5 outliers among 100 measurements (5.00%)
  4 (4.00%) high mild
  1 (1.00%) high severe
Benchmarking until/days/calendar/islamic/civil/far_past
Benchmarking until/days/calendar/islamic/civil/far_past: Warming up for 3.0000 s
Benchmarking until/days/calendar/islamic/civil/far_past: Collecting 100 samples in estimated 5.0001 s (271M iterations)
Benchmarking until/days/calendar/islamic/civil/far_past: Analyzing
until/days/calendar/islamic/civil/far_past
                        time:   [19.640 ns 20.179 ns 20.673 ns]
                        change: [-10.994% -9.6542% -8.3114%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 20 outliers among 100 measurements (20.00%)
  7 (7.00%) high mild
  13 (13.00%) high severe
Benchmarking until/days/calendar/islamic/civil/very_far_past
Benchmarking until/days/calendar/islamic/civil/very_far_past: Warming up for 3.0000 s
Benchmarking until/days/calendar/islamic/civil/very_far_past: Collecting 100 samples in estimated 5.0001 s (263M iterations)
Benchmarking until/days/calendar/islamic/civil/very_far_past: Analyzing
until/days/calendar/islamic/civil/very_far_past
                        time:   [18.583 ns 18.679 ns 18.785 ns]
                        change: [-12.174% -11.756% -11.326%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 7 outliers among 100 measurements (7.00%)
  6 (6.00%) high mild
  1 (1.00%) high severe
Benchmarking until/days/calendar/islamic/ummalqura/far_past
Benchmarking until/days/calendar/islamic/ummalqura/far_past: Warming up for 3.0000 s
Benchmarking until/days/calendar/islamic/ummalqura/far_past: Collecting 100 samples in estimated 5.0001 s (252M iterations)
Benchmarking until/days/calendar/islamic/ummalqura/far_past: Analyzing
until/days/calendar/islamic/ummalqura/far_past
                        time:   [19.320 ns 19.369 ns 19.425 ns]
                        change: [+6.6525% +7.0691% +7.4595%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 13 outliers among 100 measurements (13.00%)
  8 (8.00%) high mild
  5 (5.00%) high severe
Benchmarking until/days/calendar/islamic/ummalqura/very_far_past
Benchmarking until/days/calendar/islamic/ummalqura/very_far_past: Warming up for 3.0000 s
Benchmarking until/days/calendar/islamic/ummalqura/very_far_past: Collecting 100 samples in estimated 5.0000 s (254M iterations)
Benchmarking until/days/calendar/islamic/ummalqura/very_far_past: Analyzing
until/days/calendar/islamic/ummalqura/very_far_past
                        time:   [19.450 ns 19.518 ns 19.592 ns]
                        change: [+7.5282% +7.9267% +8.3939%] (p = 0.00 < 0.05)
                        Performance has regressed.
Found 10 outliers among 100 measurements (10.00%)
  4 (4.00%) high mild
  6 (6.00%) high severe
Benchmarking until/days/calendar/islamic/tabular/far_past
Benchmarking until/days/calendar/islamic/tabular/far_past: Warming up for 3.0000 s
Benchmarking until/days/calendar/islamic/tabular/far_past: Collecting 100 samples in estimated 5.0000 s (270M iterations)
Benchmarking until/days/calendar/islamic/tabular/far_past: Analyzing
until/days/calendar/islamic/tabular/far_past
                        time:   [18.445 ns 18.491 ns 18.544 ns]
                        change: [-44.930% -41.254% -37.394%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 4 outliers among 100 measurements (4.00%)
  2 (2.00%) high mild
  2 (2.00%) high severe
Benchmarking until/days/calendar/islamic/tabular/very_far_past
Benchmarking until/days/calendar/islamic/tabular/very_far_past: Warming up for 3.0000 s
Benchmarking until/days/calendar/islamic/tabular/very_far_past: Collecting 100 samples in estimated 5.0000 s (269M iterations)
Benchmarking until/days/calendar/islamic/tabular/very_far_past: Analyzing
until/days/calendar/islamic/tabular/very_far_past
                        time:   [18.414 ns 18.489 ns 18.574 ns]
                        change: [-13.204% -12.819% -12.456%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 10 outliers among 100 measurements (10.00%)
  8 (8.00%) high mild
  2 (2.00%) high severe

Copy link
Member

@robertbastian robertbastian left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious, how is the Hebrew, Coptic, and Ethiopian performance without custom min_months_from implementations? You get 80% performance improvements for calendars where the min_months_from computation didn't, so is that actually pulling its weight?

// (7y+1)/19 is the number of leap years before year y. Compute the
// number of leap years in the start year, up to but not including the
// end year.
let count_leaps = |y: i64| (7 * y + 1).div_euclid(19);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this probably doesn't need to be micro-optimised, but:

this does two euclidean integer divisions, which is going to be branchy and expensive. try to rewrite this in terms of normal divisions. you know that the divisor is positive, so all the div_euclid does is subtract 1 if y is negative. but since you're computing a lower bound, you can just subtract 1 unconditionally and call it a day.

having done that, you might be able to go from two divisions to one by distributing over the subtraction correctly.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but since you're computing a lower bound, you can just subtract 1 unconditionally and call it a day.

That makes this O(n) again: having min_months be a lower bound that is always close to the actual months value is part of the goal. As long as min_months is guaranteed to be at most a constant number of months off from months, the month search routine is constant time.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

However, I think this math can be far simpler: we just need to compute y * 235 / 19 (235 months in a 19 year metonic cycle)

This math produces a good lower bound: it's 12.38 months per year. At the "edge" of the calculation this will always be lower than the actual number of leap years when rounded down: the hebrew calendar has at least one leap year in every 3 year span anyway.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes this O(n) again

1 is a constant though? this is only called once, so subtracting one too much is one extra loop execution

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually started with y * 235 / 19 but then I confused myself by reading Reingold (7y + 1 mod 19 < 7 == is_leap_year). Let me try going back to that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in commit f3800d1.

// (`self`` + YMW) and the target date (`other`).
#[allow(clippy::expect_used)] // added() cannot fail: years/months/weeks validated above
let from = self
.added(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it might be worth inlining added and removing unnecessary code, such as all the error paths that you say can't happen

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Attempted in commit 659278f. Created a copy of added() without the bounds checking, and assuming Constrains behavior.

Pro: No expect() required, because written to be infallible.

Con: Duplication of the added() logic.

I couldn't think of a clean way to not have the code duplicated. I guess as long as the NonISODateAdd algorithm is stable it might not be an issue, but it is a code smell.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The algorithm isn't stable yet.

// Now that we have `years`, `months`, and `weeks`, we can compute
// `days` directly by subtracting RD values of the intermediate date
// (`self`` + YMW) and the target date (`other`).
#[allow(clippy::expect_used)] // added() cannot fail: years/months/weeks validated above
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added() has quite a few error cases, I'm not immediately convinced that this unwrap is safe

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed in commit 659278f.

Copy link
Member

@Manishearth Manishearth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't be doing the RataDie calculation: that was an approach that was considered and rejected.

The month stuff is fine.

I'm surprised your benches take an hour to run. Not ethat you can filter to specific benches by writing a filter after the --, but I was able to run the whole suite in ~10min IIRC.

candidate_days += sign;
}

// Now that we have `years`, `months`, and `weeks`, we can compute
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue: I'm not 100% convinced this works

added() is not intended to be the inverse of until; and verifying which cases this is the case for is quite tricky. In particular, Constrain/Reject behavior makes this hard to reason about.

We considered doing this early on and decided against it. As documented in #7682 the primary plan for optimizing this part of the code is to optimize surpasses(): note that at the moment this code won't be called more than 31 times.

We can also reduce that with a binary search, which I was originally going t oimplement, and @robertbastian has some code for.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree -- for fields larger than Day, or if Overflow::Reject is used.

But for the Day field, everything "left" winds up in Day. Running a loop to find !surpasses(d) && surpasses(d+1), is functionally the same as doing a RD subtraction.

Stated another way, once we are at the bottom of until(), Year/Month/Week values are frozen. All we're doing is nailing down the Day field. This is now a linear space problem, and the RD subtraction is mathematically the same as doing the loop. added() is appropriate here not because it is the inverse of until(), but because it uses the same new_balanced and regulated day logic as surpasses().

Incidentally, I first tried to compute both Week and Day using RD subtraction. This failed spectacularly, breaking a bunch of tests. When I investigated why, I saw that RD subtraction would never work for Week/Day because of the Constrain logic for fields > Day. No such problem when dealing only with Day.

Note that all the date_arithmetic_snapshot.rs tests are passing, across all calendars. Invoked via cargo insta test -p icu_calendar --all-features --review.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incidentally, I first tried to compute both Week and Day using RD subtraction. This failed spectacularly, breaking a bunch of tests. When I investigated why, I saw that RD subtraction would never work for Week/Day because of the Constrain logic for fields > Day. No such problem when dealing only with Day.

FWIW I got this to work.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Commit f3800d1: Reverted to follow the CalendarDateUntil spec in computing the Day field.

// (7y+1)/19 is the number of leap years before year y. Compute the
// number of leap years in the start year, up to but not including the
// end year.
let count_leaps = |y: i64| (7 * y + 1).div_euclid(19);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but since you're computing a lower bound, you can just subtract 1 unconditionally and call it a day.

That makes this O(n) again: having min_months be a lower bound that is always close to the actual months value is part of the goal. As long as min_months is guaranteed to be at most a constant number of months off from months, the month search routine is constant time.

// (7y+1)/19 is the number of leap years before year y. Compute the
// number of leap years in the start year, up to but not including the
// end year.
let count_leaps = |y: i64| (7 * y + 1).div_euclid(19);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

However, I think this math can be far simpler: we just need to compute y * 235 / 19 (235 months in a 19 year metonic cycle)

This math produces a good lower bound: it's 12.38 months per year. At the "edge" of the calculation this will always be lower than the actual number of leap years when rounded down: the hebrew calendar has at least one leap year in every 3 year span anyway.

///
/// For internal use by `until()` where the operation is known to succeed.
/// Follows `Overflow::Constrain` logic.
fn added_without_checks(&self, duration: DateDuration, cal: &C) -> UncheckedArithmeticDate<C> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still don't think we should do this.

I'm highly worried about diverging too far from the spec in terms of algorithm here. The arithmetic algorithms are tricky and matching the spec has helped find many issues both here and in the spec.

I left a comment here with next steps: #7077 (comment)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Commit f3800d1 reverts until() to follow the CalendarDateUntil spec in computing the Day field.

… min-month calculations for Hebrew and Chinese calendars
@poulsbo poulsbo force-pushed the fix/calendar-until-arithmetic branch 2 times, most recently from c8e240e to 431b775 Compare March 6, 2026 00:41
@poulsbo
Copy link
Contributor Author

poulsbo commented Mar 6, 2026

Commit 431b775 adds stateful surpasses. This seems like the winning approach, based on the performance improvement.

The surpasses code is harder to follow now. However, I clearly document how it still follows the NonISODateSurpasses spec by quoting spec lines at each relevant point in the rust code.

Disclaimer: I generated the following summary and markdown table with gemini CLI. I spot checked them; they seem accurate. Raw benchmark logs attached below.

Year

All benchmarks showed a statistically significant improvement.

Calendar Case % Change Before (ns) After (ns) Delta (ns)
ISO far_past -67.92% 492.00 157.82 -334.18
very_far_past -63.78% 296.61 107.43 -189.18
Buddhist far_past -68.32% 495.98 157.14 -338.84
very_far_past -63.62% 295.81 107.63 -188.18
Coptic far_past -57.35% 182.11 77.67 -104.44
very_far_past -63.76% 501.20 181.63 -319.57
Ethiopic far_past -58.05% 182.26 76.47 -105.79
very_far_past -64.37% 505.30 180.05 -325.25
Indian far_past -70.91% 466.17 135.60 -330.57
very_far_past -69.81% 274.87 82.98 -191.89
Julian far_past -71.80% 310.32 87.50 -222.82
very_far_past -83.17% 930.72 156.63 -774.09
Chinese (Cached) far_past -80.01% 5071.53 1013.90 -4057.63
very_far_past -73.47% 3135.80 832.05 -2303.75
Gregorian far_past -68.07% 494.93 158.04 -336.89
very_far_past -63.56% 295.64 107.74 -187.90
Hebrew far_past -73.46% 1991.26 528.45 -1462.81
very_far_past -71.26% 1720.53 494.55 -1225.98
Islamic (Obs.) far_past -72.89% 1608.15 435.92 -1172.23
very_far_past -52.43% 473.26 225.15 -248.11
Islamic (Civil) far_past -74.27% 1824.52 469.41 -1355.11
very_far_past -50.33% 473.36 235.11 -238.25
Islamic (Umm.) far_past -73.11% 1623.40 436.51 -1186.89
very_far_past -51.17% 465.60 227.34 -238.26
Islamic (Tab.) far_past -70.64% 1613.86 473.78 -1140.08
very_far_past -49.88% 470.22 235.66 -234.56

Summary of Results

  • Universal Improvement: Every single test case across all calendar types showed a significant performance improvement.
  • Relative Gains: Improvements ranged from a minimum of ~50% (Islamic/Tabular very_far_past) to a maximum of ~83% (Julian very_far_past).
  • Absolute Gains: The most significant absolute time saving occurred in the Chinese (Cached) calendar, which saw a reduction of over 4 microseconds in the far_past case.
  • Overall Impact: The changes appear to have drastically optimized the until/years calculation, often reducing execution time by more than 2/3 of the original duration.

Month

All calendar types show significant performance improvements for the far_past month-based benchmarks.

Benchmark Results: until/months/calendar (far_past)

Calendar Status % Change Before After Change (Abs)
ISO Improved -67.367% 685.35 ns 223.65 ns -461.70 ns
Buddhist Improved -67.171% 684.12 ns 224.59 ns -459.53 ns
Coptic Improved -98.996% 7,336.06 ns 73.65 ns -7,262.41 ns
Ethiopic Improved -99.136% 8,083.10 ns 69.84 ns -8,013.26 ns
Indian Improved -69.741% 679.47 ns 205.60 ns -473.87 ns
Julian Improved -71.955% 314.84 ns 88.30 ns -226.54 ns
Chinese (Cached) Improved -85.881% 29.38 ms 4.15 ms -25.24 ms
Gregorian Improved -66.971% 681.46 ns 225.08 ns -456.38 ns
Hebrew Improved -97.877% 2.43 ms 51.66 µs -2.38 ms
Islamic (Obs) Improved -77.036% 320.83 µs 73.68 µs -247.15 µs
Islamic (Civil) Improved -71.036% 1,641.05 ns 475.31 ns -1,165.74 ns
Islamic (Umm) Improved -76.862% 319.76 µs 73.99 µs -245.78 µs
Islamic (Tabular) Improved -70.676% 1,639.24 ns 480.69 ns -1,158.55 ns

Summary of Results

  • Universal Improvement: Every calendar tested showed a significant reduction in execution time, with no regressions detected.
  • Massive Speedups: Ethiopic (-99.1%), Coptic (-99.0%), and Hebrew (-97.9%) show the most dramatic improvements, effectively achieving a ~100x speedup.
  • Consistent Core Calendars: ISO, Gregorian, and Buddhist calendars show very similar improvements of approximately 67%, reducing execution time from ~685 ns to ~224 ns.
  • Complex Calendars: The Chinese Cached and Hebrew calendars, which previously took multiple milliseconds, have been reduced to the low millisecond and microsecond range respectively, representing a major optimization for these more computationally intensive systems.

bench.alan3-months.txt
bench.alan3-years.txt

@poulsbo poulsbo requested a review from Manishearth March 6, 2026 00:55
@poulsbo poulsbo force-pushed the fix/calendar-until-arithmetic branch from e84be6e to 431b775 Compare March 6, 2026 01:12
Copy link
Member

@Manishearth Manishearth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sffc, is this what you were envisioning for surpasses()? This is rather complicated, I was hoping there was a simpler way to cache the intermediates.

@Manishearth
Copy link
Member

Actually, @poulsbo , sorry to make you run benchmarks again, but can you split out the month stuff and the surpasses stuff into two PRs? We can land the month stuff easily, but it will be useful to have separate numbers on the surpasses stuff after the month stuff lands.

@poulsbo
Copy link
Contributor Author

poulsbo commented Mar 6, 2026

I have separate numbers on the surpasses stuff vs just the month stuff. As I recall, surpasses is where the big wins are, but let me post in the actual data.

@Manishearth
Copy link
Member

(My main goal is to separate out the PRs so we can land the month stuff)

@poulsbo
Copy link
Contributor Author

poulsbo commented Mar 6, 2026

Makes sense, I'll separate the surpasses() changes, let me untangle the commits...

Copy link
Member

@sffc sffc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good start. This is roughly the shape I had envisioned. I think we can make it cleaner and more closely follow the spec.

fn min_months_from_inner(_start: Self::YearInfo, years: i64) -> i64 {
// A lunisolar leap month is inserted at least every 3 years. Reingold denies
// that the Chinese calendar determines leap years using the 19-year Metonic cycle.
12 * years + (years / 3)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thought: This is much better, but we could in principle be even tighter. The 19-year bound is too small, but there should be something between that and what you have here. It would be nice to leave open a low-priority issue to investigate and implement the theoretical minimal bound.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I was working on that math earlier but did not finish it.

Comment on lines +139 to +140
// There are 7 leap years in every 19-year Metonic cycle.
235 * years / 19
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Explain why 235 * years / 19 is a lower bound. I understand that it is the average. and you are flooring the value. We end up with:

# of years min months
1 12
2 24
3 37 (+1 leap month)
4 49
5 61
6 74 (+1 leap month)
7 86
8 98
9 111 (+1 leap month)
10 123
11 136 (+1 leap month)
12 148
13 160
14 173 (+1 leap month)
15 185
16 197
17 210 (+1 leap month)
18 222
19 235 (+1 leap month)

Note: the Hebrew metonic cycle has leap years in 3, 6, 8, 11, 14, 17, and 19, i.e., leap year gaps of +3, +3, +2, +3, +3, +3, +2.

I think your formula is the theoretical minimum bound. I think, no matter where I start in the metonic cycle, the number of months I get from your formula is always less than or equal to the actual number of months.

For example, if I start in year 3 month M01 and add 5 years, I need to cross over 2 leap months, one in year 3 and one in year 6. Your formula agrees.

The "worst case" is I think month M06 in year 8 to year 18, a span that has only 3 leap months. That is a 10-year difference, and your formula correctly predicts that there are a minimum of 3 leap months during that span.

Please try to summarize why your bound is the theoretical minimum bound in a concise and compelling way.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do have the start of the hustification written in #7739 (comment)

Copy link
Contributor Author

@poulsbo poulsbo Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(I know this PR is closed/merged, but this seems like the right place for this comment.)

My original plan was to use the start year parameter to tell exactly where we were in the cycle, using the $7y + 1 \mod{19} &lt; 7$ rule (Reingold 8.14). Then we could give an exact answer. But during implementation I kept breaking tests. I think I wasn't handling years < 0 correctly.

// 5. Let m0 be MonthCodeToOrdinal(calendar, y0, ! ConstrainMonthCode(calendar, y0, parts.[[MonthCode]], constrain)).
let m0 = cal
.ordinal_from_month(y0, base_month, constrain)
.unwrap_or(1);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Keep the debug assertion we had before if possible.

// 3. Let y0 be parts.[[Year]] + years.
let y0 = cal.year_info_from_extended(parts.year().to_extended_year() + years as i32);
let base_month = cal.month_from_ordinal(parts.year(), parts.month());
if Self::compare_surpasses_lexicographic(sign, y0, base_month, parts.day(), cal_date_2, cal)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: Why did you move compare_surpasses_lexicographic below the calculation of m0? You don't need m0 as input to that function. Please try to match the spec as closely as possible.

let years = i64::from(duration.years) * sign_mul;
let months = i64::from(duration.months) * sign_mul;

let month_checker = self.surpasses_month_checker(other, years, sign, cal);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: you can also create a years checker. It can run the "lexicographic" surpasses only.

}

/// Prepares a stateful checker for week and day iteration in `surpasses()`.
fn surpasses_week_day_checker<'a>(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issue: you shouldn't need this fn, because there should always be a call to surpasses_month_checker before you need the week/day checker.

/// By saving intermediary computations based on a fixed year,
/// only the computations relating to the month are done. The week
/// and day are expected to be zero.
struct SurpassesMonthChecker<'a, C: DateFieldsResolver> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: I think you can fairly easily write this as a single SurpassesChecker, and it will be easier to reason about. For example, your call site should look more like:

let mut checker = self.surpasses_checker();
while !checker.surpasses_years(y) { y += 1 };
while !checker.surpasses_months(m) { m += 1 };
while !checker.surpasses_weeks(w) { w += 1 };
while !checker.surpasses_days(w, d) { d += 1 };
return (y, m, w, d)

When surpasses_years returns false, nothing happens, but as soon as it returns true, the checker saves the intermediate results and prepares for a call to surpasses_months. Rinse and repeat for surpasses_months. You can add a debug assertion that surpasses_years and surpasses_months may never return true multiple times.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, that would be much cleaner.

For some reason when I was reading through surpasses(), I anchored early on the idea that I would need two separate checker objects, one to handle months, one to handle weeks/days, because the algorithm seemed to break down that way.

But it would be less noisy to have a single checker. Let me try to structure it that way. I'll address your other comments at the same time.

@poulsbo poulsbo force-pushed the fix/calendar-until-arithmetic branch from 885f70e to f3800d1 Compare March 6, 2026 18:53
@poulsbo
Copy link
Contributor Author

poulsbo commented Mar 6, 2026

@Manishearth

I pulled the stateful surpasses() commits out of this PR and into PR #7745.

Copy link
Member

@Manishearth Manishearth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm going to approve and land this so that your PR doesn't need to be stacked. I will fix up the comments myself since I have already thought about the Hebrew and Chinese issue.

@Manishearth Manishearth enabled auto-merge (squash) March 6, 2026 19:10
@Manishearth Manishearth merged commit 0187a79 into unicode-org:main Mar 6, 2026
64 checks passed
@Manishearth
Copy link
Member

Followup #7746

Copy link
Member

@robertbastian robertbastian left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So what are the performance numbers of the the month optimsiation that was actually merged here?

Comment on lines +1004 to +1007
// months corresponding to min_years. For solar calendars, this is 12 * min_years.
// For the Hebrew calendar, a leap month is added for 7 out of 19 years. East Asian
// Calendars do not provide a specialized implementation of `min_months_from()`
// because it would be too expensive to calculate; they default to 12 * min_years.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: don't really have to list out all the different implementations of min_months_from_inner, this will just become stale

fn min_months_from_inner(_start: Self::YearInfo, years: i64) -> i64 {
// A lunisolar leap month is inserted at least every 3 years. Reingold denies
// that the Chinese calendar determines leap years using the 19-year Metonic cycle.
12 * years + (years / 3)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there are a two issues with this:

  • EastAsianTraditional is generic in R: Rules, and the rules can do whatever they want. We either need to add a requirement that the rules insert a leap month at least every three years, or delegate this logic to the Rules implementation
  • Even for our concrete calendars, I'm not sure that this is correct. Both China and Korea use three different rules (1900-1912, 1912-2050/2100, simple approximation otherwise), so this needs to be shown to hold for all three (and for the transitions). For the hardcoded data we can write a test that this holds, but for the simple approximation I would like to see some kind of reasoning.

Manishearth added a commit that referenced this pull request Mar 9, 2026
Improving docs from #7739

---------

Co-authored-by: Robert Bastian <4706271+robertbastian@users.noreply.github.com>
poulsbo pushed a commit to poulsbo/icu4x that referenced this pull request Mar 10, 2026
Improving docs from unicode-org#7739

---------

Co-authored-by: Robert Bastian <4706271+robertbastian@users.noreply.github.com>
Manishearth added a commit that referenced this pull request Mar 11, 2026
…an (#7753)

This is one way of resolving (some of?) @robertbastian's concerns from
#7739

---------

Co-authored-by: Robert Bastian <4706271+robertbastian@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants