|
|
|
#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;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
*
|
|
*/
|
|
|