Project

General

Profile

Feature #16335 » benchmark--cruby--date_core--gregorian.c

colin13rg (Colin Bartlett), 11/12/2019 02:08 PM

 

#include <time.h>
#include <stdio.h>
#include <limits.h>
// #include <stdlib.h>

static double
do_benchmark(double timev, double countv, double loop_cycle_time, int crlf)
{
double timevnet, timevc = (double) clock();
if (countv <= 0)
return timevc;
timev = (timevc - timev) / CLOCKS_PER_SEC;
printf(" %7.3fs ", timev);
timevc = timev / countv;
if (loop_cycle_time < 0)
printf("%7.1fns %.8es;", timevc * 1e9, timevc);
else {
timevnet = timevc - loop_cycle_time;
printf("%+7.1fns %+.8es;", timevnet * 1e9, timevnet);
}
if (crlf)
printf("\n");
return timevc;
}



/*
* Extracts from: https://github.com/ruby/date/blob/master/ext/date/date_core.c
* so that possible improvements (that is making faster) to the C functions
* can be tested and roughly benchmarked in a stand-alone environment.
*
* For example, changing "c_jd_to_civil()" which uses floating point arithmetic
* to "c_jd_to_civil_iv" which only uses integer variables and arithmetic made
* the calculation about 4x (or more, depending on the compiler options) faster.
* (Rough benchmarks made on a Lenovo Thinkpad T420, Microsoft 64 bits Windows 7,
* using MinGW gcc compiler
*
*
* :TODO: What algorithms does Python use for its Gregorian dates?
*
*
*/



//...
//...

/*
date_core.c: Coded by Tadayoshi Funaba 2010-2014
*/

//...

/* copied from time.c */
#define NDIV(x,y) (-(-((x)+1)/(y))-1)
#define NMOD(x,y) ((y)-(-((x)+1)%(y))-1)
#define DIV(n,d) ((n)<0 ? NDIV((n),(d)) : (n)/(d))
#define MOD(n,d) ((n)<0 ? NMOD((n),(d)) : (n)%(d))
//...
/* "-((x)+1)/(y)" is parsed as (-((x)+1))/(y) */

//...

/* base */

//...

inline static int
c_julian_leap_p(int y)
{
return MOD(y, 4) == 0;
}
//:TODO: Why not just: return y % 4 == 0;
inline static int
c_julian_leap_p_pc(int y)
{
return y % 4 == 0;
}

inline static int
c_gregorian_leap_p(int y)
{
return (MOD(y, 4) == 0 && y % 100 != 0) || MOD(y, 400) == 0;
}
//:TODO: Why not just: return (y % 4 == 0 && y % 100 != 0) || y % 400 == 0;
inline static int
c_gregorian_leap_p_pc(int y)
{
return (y % 4 == 0 && y % 100 != 0) || y % 400 == 0;
}
//
//:TODO: And better is: return y % 4 == 0 && (y % 100 != 0 || y % 400 == 0);
inline static int
c_gregorian_leap_p_bb(int y)
{
return y % 4 == 0 && (y % 100 != 0 || y % 400 == 0);
}
//
// If y is not exactly divisible by 4:
// * as currently bracketed both sides of the "||" are evaluated;
// * as rebracketed only the "y % 4 == 0" is evaluated.
//
//:TODO: And this will usually be faster?
inline static int /* Maybe omit the "inline"? */
c_gregorian_leap_p_fi(int y)
{
if (y % 4 != 0)
return 0;
/* Fast way to deal with most likely years exactly divisible by 4. */
if (1900 < y && y < 2100)
return 1;
/* If here then a not so likely case of y % 4 == 0, so code can be slow. */
/* Ensure truncate towards zero division to ensure avoiding overflows. */
int yy = y >= 0 ? y / 100 : y > -100 ? 0 : -((-(y + 100)) / 100) - 1;
return y != yy * 100 || yy % 4 == 0;
}
//
static int /* Omit the "inline"? */
c_gregorian_leap_p_f(int y)
{
if (y % 4 != 0)
return 0;
/* Fast way to deal with most likely years exactly divisible by 4. */
if (1900 < y && y < 2100)
return 1;
/* If here then a not so likely case of y % 4 == 0, so code can be slow. */
/* Ensure truncate towards zero division to ensure avoiding overflows. */
int yy = y >= 0 ? y / 100 : y > -100 ? 0 : -((-(y + 100)) / 100) - 1;
return y != yy * 100 || yy % 4 == 0;
}




/*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/




/* Testing */




static void
test_many_c_leap_p()
{
int y, y0, y2, runv, runv2, repeatv, repeatv2, typev, typev2, countok, countnotok, sumv, errv, xerrv;
double timev, loop_cycle_time;

y0 = -16000;
y2 = 16000;
countok = 0;
countnotok = 0;
xerrv = 0;
for (y = y0; y <= y2; y++) {
errv = 0;
// errv = test_c_gregorian_leap_p(y, 0);
if (errv) countnotok += 1;
else countok += 1;
if (errv) {
// errv = test_c_gregorian_leap_p(y, 4);
xerrv = errv;
}
}
printf("# test_many_c_leap: y %d..%d; ok %d, NOTok %d;\n", y0, y2, countok, countnotok);

// For benchmarking use most likely y.

y0 = -16000;
y2 = -1;

repeatv2 = 30 * 1000;
runv2 = 5;
typev2 = 3;
for (typev = 1; typev <= typev2; typev++) {
if (2 <= typev && typev <= 3) {
y = y2 - y0 + 1;
if (typev == 2) {
y0 = 1;
y2 = 16000;
}
else {
// Most likely range of years and dates.
y0 = 1900 + 1;
y2 = 2100 - 1;
}
repeatv2 = (repeatv2 * y) / (y2 - y0 + 1); // do multiple runs to make benchmark times comparable
}
printf("# benchmark: type %d; y (%d..%d) * %d;\n", typev, y0, y2, repeatv2);
for (runv = 1; runv <= runv2; runv++) {
loop_cycle_time = -1; // to ensure "9.9ns" - not "+9.9ns" - on loop cycle times
sumv = 0;
timev = do_benchmark(0, 0, 0, 0);
for (repeatv = 1; repeatv <= repeatv2; repeatv++) {
for (y = y0; y <= y2; y++) {
sumv += y;
}
}
sumv = 0; // to distinguish loop benchmarks
printf("# loop: type %d; y (%d..%d) * %d; run %2d; sum %11d;", typev, y0, y2, repeatv2, runv, sumv);
loop_cycle_time = do_benchmark(timev, repeatv2 * (y2 - y0 + 1), loop_cycle_time, 1);
sumv = 0;
timev = do_benchmark(0, 0, 0, 0);
for (repeatv = 1; repeatv <= repeatv2; repeatv++) {
for (y = y0; y <= y2; y++) {
sumv += c_julian_leap_p(y);
}
}
printf("# c_julian_leap_p: type %d; y (%d..%d) * %d; run %2d; sum %11d;", typev, y0, y2, repeatv2, runv, sumv);
do_benchmark(timev, repeatv2 * (y2 - y0 + 1), loop_cycle_time, 1);
sumv = 0;
timev = do_benchmark(0, 0, 0, 0);
for (repeatv = 1; repeatv <= repeatv2; repeatv++) {
for (y = y0; y <= y2; y++) {
sumv += c_julian_leap_p_pc(y);
}
}
printf("# c_julian_leap_p_pc: type %d; y (%d..%d) * %d; run %2d; sum %11d;", typev, y0, y2, repeatv2, runv, sumv);
do_benchmark(timev, repeatv2 * (y2 - y0 + 1), loop_cycle_time, 1);
sumv = 0;
timev = do_benchmark(0, 0, 0, 0);
for (repeatv = 1; repeatv <= repeatv2; repeatv++) {
for (y = y0; y <= y2; y++) {
sumv += c_gregorian_leap_p(y);
}
}
printf("# c_gregorian_leap_p: type %d; y (%d..%d) * %d; run %2d; sum %11d;", typev, y0, y2, repeatv2, runv, sumv);
do_benchmark(timev, repeatv2 * (y2 - y0 + 1), loop_cycle_time, 1);
sumv = 0;
timev = do_benchmark(0, 0, 0, 0);
for (repeatv = 1; repeatv <= repeatv2; repeatv++) {
for (y = y0; y <= y2; y++) {
sumv += c_gregorian_leap_p_pc(y);
}
}
printf("# c_gregorian_leap_p_pc: type %d; y (%d..%d) * %d; run %2d; sum %11d;", typev, y0, y2, repeatv2, runv, sumv);
do_benchmark(timev, repeatv2 * (y2 - y0 + 1), loop_cycle_time, 1);
sumv = 0;
timev = do_benchmark(0, 0, 0, 0);
for (repeatv = 1; repeatv <= repeatv2; repeatv++) {
for (y = y0; y <= y2; y++) {
sumv += c_gregorian_leap_p_bb(y);
}
}
printf("# c_gregorian_leap_p_bb: type %d; y (%d..%d) * %d; run %2d; sum %11d;", typev, y0, y2, repeatv2, runv, sumv);
do_benchmark(timev, repeatv2 * (y2 - y0 + 1), loop_cycle_time, 1);
sumv = 0;
timev = do_benchmark(0, 0, 0, 0);
for (repeatv = 1; repeatv <= repeatv2; repeatv++) {
for (y = y0; y <= y2; y++) {
sumv += c_gregorian_leap_p_fi(y);
}
}
printf("# c_gregorian_leap_p_fi: type %d; y (%d..%d) * %d; run %2d; sum %11d;", typev, y0, y2, repeatv2, runv, sumv);
do_benchmark(timev, repeatv2 * (y2 - y0 + 1), loop_cycle_time, 1);
sumv = 0;
timev = do_benchmark(0, 0, 0, 0);
for (repeatv = 1; repeatv <= repeatv2; repeatv++) {
for (y = y0; y <= y2; y++) {
sumv += c_gregorian_leap_p_f(y);
}
}
printf("# c_gregorian_leap_p_f: type %d; y (%d..%d) * %d; run %2d; sum %11d;", typev, y0, y2, repeatv2, runv, sumv);
do_benchmark(timev, repeatv2 * (y2 - y0 + 1), loop_cycle_time, 1);
}
}

}




int main() {
int iminv = INT_MIN, imaxv = INT_MAX;
printf("# VLC %d .. %d;\n", iminv, imaxv);
printf("# ct %f;\n", (double) clock());
test_many_c_leap_p();
return 0;
}




/*
*
*/

(1-1/8)