The call to apr_explode_localtime() in mod_log_config is one of the more
expensive operations in the httpd. This patch attempts to reduce the
overhead by caching the result.
--Brian
Index: modules/loggers/mod_log_config.c
===================================================================
RCS file: /home/cvspublic/httpd-2.0/modules/loggers/mod_log_config.c,v
retrieving revision 1.68
diff -r1.68 mod_log_config.c
447a448,498
>
> /* Cache of expanded timestamps:
> * Because apr_explode_localtime is an expensive function call,
> * we cache the exploded time and re-use it for the rest of the
> * current second.
> */
>
> struct exploded_time_cache_element {
> apr_int64_t t;
> apr_exploded_time_t xt;
> };
>
> #define TIME_CACHE_SIZE 16
> static struct exploded_time_cache_element
exploded_time_cache[TIME_CACHE_SIZE];
>
> static void cached_explode_localtime(apr_exploded_time_t *xt,
apr_time_t t)
> {
> apr_int64_t seconds = t / APR_USEC_PER_SEC;
> struct exploded_time_cache_element *cache =
> &(exploded_time_cache[seconds % TIME_CACHE_SIZE]);
>
> /* The cache is implemented as a ring buffer. Each second,
> * it uses a different element in the buffer. The timestamp
> * in the element indicates whether the element contains the
> * exploded time for the current second (vs the time
> * 'now - TIME_CACHE_SIZE' seconds ago). If the cached
> * value is for the current time, we use it. Otherwise,
> * we compute the apr_exploded_time_t and store it in this
> * cache element. Note that the timestamp in the cache
> * element is updated only after the exploded time. Thus
> * if two threads hit this cache element simultaneously
> * at the start of a new second, they'll both explode the
> * time and store it. I.e., the writers will collide, but
> * they'll be writing the same value.
> */
> if (cache->t >= seconds) {
> /* Note: If this memcpy ever takes more than TIME_CACHE_SIZE
> * seconds, the value will be unpredictable (because this
> * bucket in the ring buffer will have been recycled).
> * This memcpy should never take multiple seconds, but to
> * be safe we use a relatively large value for TIME_CACHE_SIZE.
> */
> memcpy(xt, &(cache->xt), sizeof(apr_exploded_time_t));
> }
> else {
> apr_explode_localtime(xt, t);
> memcpy(&(cache->xt), xt, sizeof(apr_exploded_time_t));
> cache->t = seconds;
> }
> }
>
466c517
< apr_explode_localtime(&xt, apr_time_now());
---
> cached_explode_localtime(&xt, apr_time_now());
468c519
< apr_explode_localtime(&xt, r->request_time);
---
> cached_explode_localtime(&xt, r->request_time);