To: eliz AT is DOT elta DOT co DOT il Cc: djgpp-workers AT delorie DOT com References: Message-Id: <2.7.9.1D88N.H42NCC@pauzner.dnttm.ru> From: "Leonid Pauzner" Date: Wed, 16 Oct 2002 15:17:00 +0400 (MSD) X-Mailer: dMail [Demos Mail for DOS v2.7.9] Subject: Re: libc' ctime.c optimizations MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit Reply-To: djgpp-workers AT delorie DOT com 16-Oct-2002 08:01 Eli Zaretskii wrote: > On Tue, 15 Oct 2002, Leonid Pauzner wrote: >> This will not help too much since other time functions >> patalogically ineffective also. mktime() was my primary target, it is used by gettimeofday() implementation. > ``Patalogically ineffective''? If you have proposals for more efficient > implementation, please tell what they are. > Can you show a design for such caching? Specifically, how do you know > when to invalidate the cached value(s)? I mean a caching within an hour directly inside mktime(), which will greatly optimize sequential calls and leave random calls as efficient as they are now. A simple patch attached; it also checks environ changes and lcl_is_set inside mktime(). As of the implementation itself, we may want struct tm arithmetic, e.g. tm_plus_day(), tm_minus_day(), tm_local2gmt(), tm_gmt2local(). After that, being in gmt zone, we calculate tm -> time_t, - keep seconds within a day and days from EPOCH separately for cache purposes, using tm_diff() - a number of seconds within a day (or hour). We should not break the compatibility though, due to our or BSD bugs. >> Also, since time functions usually called in present > That's not a valid assumption. There are many programs that call time > functions for past and/or future dates. diff -u -p old/ctime.c ./ctime.c --- old/ctime.c Mon Jun 14 12:20:40 1999 +++ ./ctime.c Wed Oct 16 14:11:02 2002 @@ -160,6 +160,15 @@ static int tzload P((const char * name, static int tzparse P((const char * name, struct state * sp, int lastditch)); +/* + tm_diff: if the first argument >= the second argument in time line, + and they corresponds to the same hour, then + returns a difference in seconds (>= 0), + or -1 otherwise. + */ +static int tm_diff P((const struct tm * atmp, + const struct tm * btmp)); + #ifdef ALL_STATE static struct state *lclptr; static struct state *gmtptr; @@ -172,6 +181,7 @@ static struct state gmtmem; #define gmtptr (&gmtmem) #endif /* State Farm */ +static unsigned last_env_changed = 0; static int lcl_is_set; /* 0: no, 1: set by tzset, -1: set by tzsetwall */ static int gmt_is_set; static char lcl_tzstr[512]; @@ -916,7 +926,6 @@ void tzset(void) { const char * name; - static unsigned last_env_changed = 0; /* If environ didn't changed since last time, don't waste time looking at $TZ. */ @@ -1312,6 +1321,35 @@ tmcomp(const struct tm * const atmp, con return result; } + +/* + tm_diff: if the first argument >= the second argument in time line, + and they corresponds to the same hour, then + returns a difference in seconds (>= 0), + or -1 otherwise. + */ +static int +tm_diff(const struct tm * const atmp, const struct tm * const btmp) +{ + int diff = 0; + + if ((atmp->tm_year - btmp->tm_year) != 0 || + (atmp->tm_mon - btmp->tm_mon) != 0 || + (atmp->tm_mday - btmp->tm_mday) != 0 || + (atmp->tm_hour - btmp->tm_hour) != 0) + return -1; /* not within hour */ + + + if ((atmp->tm_min - btmp->tm_min) < 0 || + (atmp->tm_sec - btmp->tm_sec) < 0) + return -1; /* wrond order */ + + diff = (atmp->tm_min - btmp->tm_min)*60 + + (atmp->tm_sec - btmp->tm_sec); + return diff; +} + + static void tmnormalize(struct tm *tmp) { @@ -1490,8 +1528,20 @@ time1(struct tm * const tmp, void (*cons time_t mktime(struct tm * tmp) { + static struct tm prev_tm; + static time_t prev_res; struct tm save_tm = *tmp; - int rv = time1(tmp, localsub, 0L); + int rv; + + /* cache, valid until we get the next hour, or reverse time order */ + /* it is safe to keep less then an hour to not miss TZ swiching. */ + /* also, check there was no changes in $TZ / tzset() */ + if ((rv = tm_diff(tmp, &prev_tm)) >= 0 && + (lcl_is_set > 0 && __environ_changed == last_env_changed)) + return rv + prev_res; + + /* full story */ + rv = time1(tmp, localsub, 0L); if (rv == -1) { /* Try again, off a few hours. This may get us out of the DST @@ -1525,5 +1575,10 @@ mktime(struct tm * tmp) else tmnormalize(tmp); } + + /* store in cache */ + prev_tm = *tmp; + prev_res = rv; + return rv; }