[PATCH] * zdump.c: Tune performance of -c with large numbers on 64-bit hosts.
On my platform (Ubuntu 13.04 x86-64, Xeon E3-1225 V2), this sped up 'zdump -V -c 2147483647,2147483648 America/Los_Angeles' from 13.880 to 0.000 seconds user+system time. (INTMAX_MAX): Define if the system doesn't. (SECSPER400YEARS): New macro. (SECSPER400YEARS_FITS): New constant. (yeartot): Speed up by using division instead of repeated subtraction. --- zdump.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 46 insertions(+), 17 deletions(-) diff --git a/zdump.c b/zdump.c index 788020a..36c1838 100644 --- a/zdump.c +++ b/zdump.c @@ -60,9 +60,15 @@ typedef int int_fast32_t; # if defined LLONG_MAX || defined __LONG_LONG_MAX__ typedef long long intmax_t; # define PRIdMAX "lld" +# ifdef LLONG_MAX +# define INTMAX_MAX LLONG_MAX +# else +# define INTMAX_MAX __LONG_LONG_MAX__ +# endif # else typedef long intmax_t; # define PRIdMAX "ld" +# define INTMAX_MAX LONG_MAX # endif #endif #ifndef SCNdMAX @@ -140,6 +146,17 @@ typedef long intmax_t; #define SECSPERDAY ((int_fast32_t) SECSPERHOUR * HOURSPERDAY) #define SECSPERNYEAR (SECSPERDAY * DAYSPERNYEAR) #define SECSPERLYEAR (SECSPERNYEAR + SECSPERDAY) +#define SECSPER400YEARS (SECSPERNYEAR * (intmax_t) (300 + 3) \ + + SECSPERLYEAR * (intmax_t) (100 - 3)) + +/* +** True if SECSPER400YEARS is known to be representable as an +** intmax_t. It's OK that SECSPER400YEARS_FITS can in theory be false +** even if SECSPER400YEARS is representable, because when that happens +** the code merely runs a bit more slowly, and this slowness doesn't +** occur on any practical platform. +*/ +enum { SECSPER400YEARS_FITS = SECSPERLYEAR <= INTMAX_MAX / 400 }; #ifndef HAVE_GETTEXT #define HAVE_GETTEXT 0 @@ -490,30 +507,42 @@ main(int argc, char *argv[]) static time_t yeartot(const intmax_t y) { - register intmax_t myy; - register int_fast32_t seconds; + register intmax_t myy, seconds, years; register time_t t; myy = EPOCH_YEAR; t = 0; - while (myy != y) { - if (myy < y) { + while (myy < y) { + if (SECSPER400YEARS_FITS && 400 <= y - myy) { + intmax_t diff400 = (y - myy) / 400; + if (INTMAX_MAX / SECSPER400YEARS < diff400) + return absolute_max_time; + seconds = diff400 * SECSPER400YEARS; + years = diff400 * 400; + } else { seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR; - ++myy; - if (t > absolute_max_time - seconds) { - t = absolute_max_time; - break; - } - t += seconds; + years = 1; + } + myy += years; + if (t > absolute_max_time - seconds) + return absolute_max_time; + t += seconds; + } + while (y < myy) { + if (SECSPER400YEARS_FITS && y + 400 <= myy && myy < 0) { + intmax_t diff400 = (myy - y) / 400; + if (INTMAX_MAX / SECSPER400YEARS < diff400) + return absolute_min_time; + seconds = diff400 * SECSPER400YEARS; + years = diff400 * 400; } else { - --myy; - seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR; - if (t < absolute_min_time + seconds) { - t = absolute_min_time; - break; - } - t -= seconds; + seconds = isleap(myy - 1) ? SECSPERLYEAR : SECSPERNYEAR; + years = 1; } + myy -= years; + if (t < absolute_min_time + seconds) + return absolute_min_time; + t -= seconds; } return t; } -- 1.8.1.2
Paul Eggert <eggert@cs.ucla.edu> wrote:
+#define SECSPER400YEARS (SECSPERNYEAR * (intmax_t) (300 + 3) \ + + SECSPERLYEAR * (intmax_t) (100 - 3))
i.e. 12 622 780 800
+ if (INTMAX_MAX / SECSPER400YEARS < diff400) + return absolute_max_time;
I think this is a constraint violation on 32 bit systems that don't have long long (e.g. C89), so it ought to cause a compilation error. C99 section 6.6 "constant expressions" paragraph 4 under "constraints" says: Each constant expression shall evaluate to a constant that is in the range of representable values for its type. C89 has the same requirement. I think (sadly) you'll have to use #ifdef on SECSPER400YEARS_FITS Tony. -- f.anthony.n.finch <dot@dotat.at> http://dotat.at/ Forties, Cromarty: East, veering southeast, 4 or 5, occasionally 6 at first. Rough, becoming slight or moderate. Showers, rain at first. Moderate or good, occasionally poor at first.
Tony Finch wrote:
+ if (INTMAX_MAX / SECSPER400YEARS < diff400)
+ return absolute_max_time; I think this is a constraint violation on 32 bit systems that don't have long long (e.g. C89), so it ought to cause a compilation error.
Thanks for the review. Hmm, I don't see any constraint violation there. Although that expression contains no variables and always returns the same value, in the C standard a "constant expression" is an expression that's *required* to be a constant, which is something slightly different. As the standard doesn't require this expression to be a constant, no constraint is being violated here, and a C implementation is required to compile and run the program. Likewise for the other use of SECSPER400YEARS. We may run into compilers that complain anyway, but I'd rather wait until that happens before worrying about it.
participants (2)
-
Paul Eggert -
Tony Finch