From bfec4d1e3997ec844dfb36b94238c18c916459a6 Mon Sep 17 00:00:00 2001 From: Michael Chirico Date: Fri, 15 Nov 2024 07:08:00 -0800 Subject: [PATCH] Avoid double-loop and branching for leap years This avoids the variable-length array issue n #429. The previous implementation uses 'leap' to: * Run a full 'nyear' loop to populate whether each year is leap. * Run a second loop to count the number of days from the array (the branch prediction in each iteration will be wrong about half the time). Count the number of days directly in one loop. This is slightly more memory-efficient and should be more computationally efficient too. Closes #430. See #429. --- src/startofyear.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/src/startofyear.c b/src/startofyear.c index 64e44e6e..85738d14 100644 --- a/src/startofyear.c +++ b/src/startofyear.c @@ -36,27 +36,21 @@ SEXP do_startofyear (SEXP _from, SEXP _to, SEXP _origin) int *fromto = INTEGER(_fromto); int nyear[1] = { (to - from + 1) }; - int leap[nyear[0]]; - // generate sequence of dates to work with fromto[0] = from; for(i=1; i < nyear[0]; i++) { fromto[i] = fromto[i-1] + 1; - } - - for(i = 0; i < nyear[0]; i++) { - leap[ i ] = ( (fromto[ i ] % 4 == 0 && fromto[ i ] % 100 != 0) - || - fromto[ i ] % 400 == 0) ? 1 : 0; } for(i=0; i < nyear[0]; i++) { - if(leap[i] == 1) { // a leapyear (366 days) - fromto[i] = 366; - } else { // a non-leapyear (365 days) - fromto[i] = 365; - } + // A leap year is every year divisible by 4 _except_ + // years also divisible by 100. Leap centuries, + // those divisible by 400, also get 366 days. + int leap = ( (fromto[ i ] % 4 == 0 && fromto[ i ] % 100 != 0) + || + fromto[ i ] % 400 == 0) ? 1 : 0; + fromto[i] = 365 + leap; } /*