Skip to content

Commit 269aee4

Browse files
committed
shared: attempt a faster date <-> rate die algorithm
This comes from: https://www.benjoffe.com/fast-date-64 More specifically, the code is here: https://github.com/benjoffe/fast-date-benchmarks/blob/7fcf82b07d340ddbec866e15cfe333485439ab7f/algorithms/benjoffe_fast64.hpp But the benchmarks don't show an improvement: ``` $ critcmp base x01 group base x01 ----- ---- --- civil_datetime/to_timestamp_static/bundled/jiff 1.00 10.5±0.09ns ? ?/sec 1.03 10.9±0.07ns ? ?/sec civil_datetime/to_timestamp_static/zoneinfo/jiff 1.00 10.4±0.09ns ? ?/sec 1.03 10.8±0.07ns ? ?/sec timestamp/to_civil_datetime_offset_conversion/jiff 1.00 4.4±0.05ns ? ?/sec 1.03 4.6±0.03ns ? ?/sec ``` I ran the benchmarks like this: ``` cd bench ``` Before the change: ``` cargo bench --'(civil_datetime/to_timestamp_static|timestamp/to_civil_datetime_offset_conversion).*jiff' --save-baseline base ``` And then after the change: ``` cargo bench --'(civil_datetime/to_timestamp_static|timestamp/to_civil_datetime_offset_conversion).*jiff' --save-baseline x01 ``` Then I used [`critcmp`] to compare them: ``` critcmp base x01 ``` It's very possible I didn't port it correctly. I haven't scrutinized the codegen. It's also possible that there is an improvement, but that it's hard to write a benchmark using Jiff APIs to observe it. (Note that I left out the ARM-specific bits. I'm testing this on x86-64. I wanted to test there first before digging into the platform specific optimizations.) [`critcmp`]: https://github.com/BurntSushi/critcmp
1 parent 9d7e099 commit 269aee4

File tree

2 files changed

+86
-74
lines changed

2 files changed

+86
-74
lines changed

crates/jiff-static/src/shared/util/itime.rs

Lines changed: 43 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -173,31 +173,40 @@ impl IEpochDay {
173173
#[cfg_attr(feature = "perf-inline", inline(always))]
174174
#[allow(non_upper_case_globals, non_snake_case)] // to mimic source
175175
pub(crate) const fn to_date(&self) -> IDate {
176-
const s: u32 = 82;
177-
const K: u32 = 719468 + 146097 * s;
178-
const L: u32 = 400 * s;
179-
180-
let N_U = self.epoch_day as u32;
181-
let N = N_U.wrapping_add(K);
182-
183-
let N_1 = 4 * N + 3;
184-
let C = N_1 / 146097;
185-
let N_C = (N_1 % 146097) / 4;
186-
187-
let N_2 = 4 * N_C + 3;
188-
let P_2 = 2939745 * (N_2 as u64);
189-
let Z = (P_2 / 4294967296) as u32;
190-
let N_Y = (P_2 % 4294967296) as u32 / 2939745 / 4;
191-
let Y = 100 * C + Z;
192-
193-
let N_3 = 2141 * N_Y + 197913;
194-
let M = N_3 / 65536;
195-
let D = (N_3 % 65536) / 2141;
196-
197-
let J = N_Y >= 306;
198-
let year = Y.wrapping_sub(L).wrapping_add(J as u32) as i16;
199-
let month = (if J { M - 12 } else { M }) as i8;
176+
// Ported from:
177+
// https://github.com/benjoffe/fast-date-benchmarks/blob/7fcf82b07d340ddbec866e15cfe333485439ab7f/algorithms/benjoffe_fast64.hpp#L73
178+
179+
const ERAS: u32 = 14704;
180+
const D_SHIFT: u32 = 146097 * ERAS - 719469;
181+
const Y_SHIFT: u32 = 400 * ERAS - 1;
182+
const SCALE: u32 = 32;
183+
const SHIFT_0: u32 = 30556 * SCALE;
184+
const SHIFT_1: u32 = 5980 * SCALE;
185+
const C1: u64 = 505054698555331; // floor(2^64*4/146097):
186+
const C2: u64 = 50504432782230121; // ceil(2^64*4/1461):
187+
const C3: u64 = 8619973866219416 * 32 / (SCALE as u64); // floor(2^64/2140):
188+
189+
let day_number = self.epoch_day as u32;
190+
let rev = D_SHIFT.wrapping_sub(day_number) as u64;
191+
let cen = ((C1 as u128) * (rev as u128) >> 64) as u64;
192+
let jul = rev - cen / 4 + cen;
193+
194+
let num = (C2 as u128) * (jul as u128);
195+
let yrs = Y_SHIFT.wrapping_sub((num >> 64) as u32);
196+
let low = num as u64;
197+
let ypt = ((24451 * SCALE as u128) * (low as u128) >> 64) as u32;
198+
199+
let bump = ypt < 3952 * SCALE;
200+
let shift = if bump { SHIFT_1 } else { SHIFT_0 };
201+
202+
let N = (yrs % 4) * (16 * SCALE) + shift - ypt;
203+
let M = N / (2048 * SCALE);
204+
let D = ((C3 as u128) * ((N % (2048 * SCALE)) as u128) >> 64) as u32;
205+
206+
let month = M as i8;
200207
let day = (D + 1) as i8;
208+
let year = yrs.wrapping_add(bump as u32) as i16;
209+
201210
IDate { year, month, day }
202211
}
203212

@@ -351,26 +360,23 @@ impl IDate {
351360
#[cfg_attr(feature = "perf-inline", inline(always))]
352361
#[allow(non_upper_case_globals, non_snake_case)] // to mimic source
353362
pub(crate) const fn to_epoch_day(&self) -> IEpochDay {
354-
const s: u32 = 82;
355-
const K: u32 = 719468 + 146097 * s;
356-
const L: u32 = 400 * s;
363+
// Ported from:
364+
// https://github.com/benjoffe/fast-date-benchmarks/blob/7fcf82b07d340ddbec866e15cfe333485439ab7f/algorithms/benjoffe_fast64.hpp#L130
357365

358366
let year = self.year as u32;
359367
let month = self.month as u32;
360368
let day = self.day as u32;
361369

362-
let J = month <= 2;
363-
let Y = year.wrapping_add(L).wrapping_sub(J as u32);
364-
let M = if J { month + 12 } else { month };
365-
let D = day - 1;
366-
let C = Y / 100;
370+
let bump = month <= 2;
371+
let yrs = year.wrapping_add(5880000).wrapping_sub(bump as u32);
372+
let cen = yrs / 100;
373+
let shift = if bump { 8829 } else { -2919 };
367374

368-
let y_star = 1461 * Y / 4 - C + C / 4;
369-
let m_star = (979 * M - 2919) / 32;
370-
let N = y_star + m_star + D;
375+
let year_days = yrs * 365 + yrs / 4 - cen + cen / 4;
376+
let month_days = ((979 * (month as i32) + shift) / 32) as u32;
377+
let epoch_day =
378+
(year_days + month_days + day).wrapping_sub(2148345369) as i32;
371379

372-
let N_U = N.wrapping_sub(K);
373-
let epoch_day = N_U as i32;
374380
IEpochDay { epoch_day }
375381
}
376382

src/shared/util/itime.rs

Lines changed: 43 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -171,31 +171,40 @@ impl IEpochDay {
171171
#[cfg_attr(feature = "perf-inline", inline(always))]
172172
#[allow(non_upper_case_globals, non_snake_case)] // to mimic source
173173
pub(crate) const fn to_date(&self) -> IDate {
174-
const s: u32 = 82;
175-
const K: u32 = 719468 + 146097 * s;
176-
const L: u32 = 400 * s;
177-
178-
let N_U = self.epoch_day as u32;
179-
let N = N_U.wrapping_add(K);
180-
181-
let N_1 = 4 * N + 3;
182-
let C = N_1 / 146097;
183-
let N_C = (N_1 % 146097) / 4;
184-
185-
let N_2 = 4 * N_C + 3;
186-
let P_2 = 2939745 * (N_2 as u64);
187-
let Z = (P_2 / 4294967296) as u32;
188-
let N_Y = (P_2 % 4294967296) as u32 / 2939745 / 4;
189-
let Y = 100 * C + Z;
190-
191-
let N_3 = 2141 * N_Y + 197913;
192-
let M = N_3 / 65536;
193-
let D = (N_3 % 65536) / 2141;
194-
195-
let J = N_Y >= 306;
196-
let year = Y.wrapping_sub(L).wrapping_add(J as u32) as i16;
197-
let month = (if J { M - 12 } else { M }) as i8;
174+
// Ported from:
175+
// https://github.com/benjoffe/fast-date-benchmarks/blob/7fcf82b07d340ddbec866e15cfe333485439ab7f/algorithms/benjoffe_fast64.hpp#L73
176+
177+
const ERAS: u32 = 14704;
178+
const D_SHIFT: u32 = 146097 * ERAS - 719469;
179+
const Y_SHIFT: u32 = 400 * ERAS - 1;
180+
const SCALE: u32 = 32;
181+
const SHIFT_0: u32 = 30556 * SCALE;
182+
const SHIFT_1: u32 = 5980 * SCALE;
183+
const C1: u64 = 505054698555331; // floor(2^64*4/146097):
184+
const C2: u64 = 50504432782230121; // ceil(2^64*4/1461):
185+
const C3: u64 = 8619973866219416 * 32 / (SCALE as u64); // floor(2^64/2140):
186+
187+
let day_number = self.epoch_day as u32;
188+
let rev = D_SHIFT.wrapping_sub(day_number) as u64;
189+
let cen = ((C1 as u128) * (rev as u128) >> 64) as u64;
190+
let jul = rev - cen / 4 + cen;
191+
192+
let num = (C2 as u128) * (jul as u128);
193+
let yrs = Y_SHIFT.wrapping_sub((num >> 64) as u32);
194+
let low = num as u64;
195+
let ypt = ((24451 * SCALE as u128) * (low as u128) >> 64) as u32;
196+
197+
let bump = ypt < 3952 * SCALE;
198+
let shift = if bump { SHIFT_1 } else { SHIFT_0 };
199+
200+
let N = (yrs % 4) * (16 * SCALE) + shift - ypt;
201+
let M = N / (2048 * SCALE);
202+
let D = ((C3 as u128) * ((N % (2048 * SCALE)) as u128) >> 64) as u32;
203+
204+
let month = M as i8;
198205
let day = (D + 1) as i8;
206+
let year = yrs.wrapping_add(bump as u32) as i16;
207+
199208
IDate { year, month, day }
200209
}
201210

@@ -349,26 +358,23 @@ impl IDate {
349358
#[cfg_attr(feature = "perf-inline", inline(always))]
350359
#[allow(non_upper_case_globals, non_snake_case)] // to mimic source
351360
pub(crate) const fn to_epoch_day(&self) -> IEpochDay {
352-
const s: u32 = 82;
353-
const K: u32 = 719468 + 146097 * s;
354-
const L: u32 = 400 * s;
361+
// Ported from:
362+
// https://github.com/benjoffe/fast-date-benchmarks/blob/7fcf82b07d340ddbec866e15cfe333485439ab7f/algorithms/benjoffe_fast64.hpp#L130
355363

356364
let year = self.year as u32;
357365
let month = self.month as u32;
358366
let day = self.day as u32;
359367

360-
let J = month <= 2;
361-
let Y = year.wrapping_add(L).wrapping_sub(J as u32);
362-
let M = if J { month + 12 } else { month };
363-
let D = day - 1;
364-
let C = Y / 100;
368+
let bump = month <= 2;
369+
let yrs = year.wrapping_add(5880000).wrapping_sub(bump as u32);
370+
let cen = yrs / 100;
371+
let shift = if bump { 8829 } else { -2919 };
365372

366-
let y_star = 1461 * Y / 4 - C + C / 4;
367-
let m_star = (979 * M - 2919) / 32;
368-
let N = y_star + m_star + D;
373+
let year_days = yrs * 365 + yrs / 4 - cen + cen / 4;
374+
let month_days = ((979 * (month as i32) + shift) / 32) as u32;
375+
let epoch_day =
376+
(year_days + month_days + day).wrapping_sub(2148345369) as i32;
369377

370-
let N_U = N.wrapping_sub(K);
371-
let epoch_day = N_U as i32;
372378
IEpochDay { epoch_day }
373379
}
374380

0 commit comments

Comments
 (0)