This is a performance patch for apache 1.3b6-dev. You'll need to pick up a snapshot at . Then apply the patch and add -DTOP_FUEL to the EXTRA_CFLAGS in your Configuration, re-Configure and rebuild. TOP_FUEL does the following: - remove accept() locking when there is only one socket (removes two syscalls per connection) - instruct mod_log_config to buffer log lines (still does atomic writes) - disable shared modules, which shrinks one of the structures - replace time() syscalls with an access to a piece of shared memory that is accurate to within a second or so of the real time (removes up to two time() calls per request) - remove the SIGUSR1 graceful restart function (removes three sigaction() calls per request) - instruct mod_include to not bother time doing chdir()s, which are only required by folks using relative paths in #exec file and such. In addition the patch also (unconditionally): - speeds up mod_include by 10% by doing lazy calculation of some less used env variables - inlines ap_get/set_module_config If you use mod_mmap_static in addition to this patch you will achieve one to two syscalls per request (depends on if the client is pipelining), plus ~7 syscalls per connection. With persistant connections those 7 syscalls ammortize really well... and with pipelined persistant connections you can go below an average of 1 syscall per request. Dean Index: src/include/http_config.h =================================================================== RCS file: /export/home/cvs/apache-1.3/src/include/http_config.h,v retrieving revision 1.79 diff -u -r1.79 http_config.h --- http_config.h 1998/04/11 12:00:19 1.79 +++ http_config.h 1998/04/13 18:43:56 @@ -281,6 +281,11 @@ API_EXPORT(void *) ap_get_module_config(void *conf_vector, module *m); API_EXPORT(void) ap_set_module_config(void *conf_vector, module *m, void *val); +#define ap_get_module_config(v,m) \ + (((void **)(v))[(m)->module_index]) +#define ap_set_module_config(v,m,val) \ + ((((void **)(v))[(m)->module_index]) = (val)) + /* Generic command handling function... */ API_EXPORT_NONSTD(const char *) ap_set_string_slot(cmd_parms *, char *, char *); Index: src/include/httpd.h =================================================================== RCS file: /export/home/cvs/apache-1.3/src/include/httpd.h,v retrieving revision 1.202 diff -u -r1.202 httpd.h --- httpd.h 1998/04/11 12:00:22 1.202 +++ httpd.h 1998/04/13 18:43:58 @@ -58,6 +58,14 @@ #ifndef APACHE_HTTPD_H #define APACHE_HTTPD_H +#ifdef TOP_FUEL +#define SINGLE_LISTEN_UNSERIALIZED_ACCEPT +#define BUFFERED_LOGS +#define DYNAMIC_MODULE_LIMIT 0 +#define SHARED_TIME +#define NO_GRACEFUL +#endif + /* * httpd.h: header for simple (ha! not anymore) http daemon */ @@ -968,6 +976,14 @@ #if !defined (MULTITHREAD) && \ (defined (USE_MMAP_SCOREBOARD) || defined (USE_SHMGET_SCOREBOARD)) #define OPTIMIZE_TIMEOUTS +#endif + +#ifdef SHARED_TIME +#ifndef OPTIMIZE_TIMEOUTS +# error "you need OPTIMIZE_TIMEOUTS for SHARED_TIME" +#endif +extern time_t ap_shared_time(time_t *); +#define time(x) ap_shared_time(x) #endif /* A set of flags which indicate places where the server should raise(SIGSTOP). Index: src/include/scoreboard.h =================================================================== RCS file: /export/home/cvs/apache-1.3/src/include/scoreboard.h,v retrieving revision 1.39 diff -u -r1.39 scoreboard.h --- scoreboard.h 1998/04/11 12:00:23 1.39 +++ scoreboard.h 1998/04/13 18:43:58 @@ -134,6 +134,9 @@ typedef struct { int exit_generation; /* Set by the main process if a graceful restart is required */ +#ifdef SHARED_TIME + time_t right_now; /* the current time */ +#endif } global_score; /* stuff which the parent generally writes and the children rarely read */ Index: src/main/http_config.c =================================================================== RCS file: /export/home/cvs/apache-1.3/src/main/http_config.c,v retrieving revision 1.113 diff -u -r1.113 http_config.c --- http_config.c 1998/04/11 12:00:28 1.113 +++ http_config.c 1998/04/13 18:44:00 @@ -115,17 +115,21 @@ * overridden). */ +#ifndef ap_get_module_config API_EXPORT(void *) ap_get_module_config(void *conf_vector, module *m) { void **confv = (void **) conf_vector; return confv[m->module_index]; } +#endif +#ifndef ap_set_module_config API_EXPORT(void) ap_set_module_config(void *conf_vector, module *m, void *val) { void **confv = (void **) conf_vector; confv[m->module_index] = val; } +#endif static void *create_empty_config(pool *p) { Index: src/main/http_main.c =================================================================== RCS file: /export/home/cvs/apache-1.3/src/main/http_main.c,v retrieving revision 1.324 diff -u -r1.324 http_main.c --- http_main.c 1998/04/11 12:00:29 1.324 +++ http_main.c 1998/04/13 18:44:13 @@ -1071,6 +1071,20 @@ timeout_name = NULL; } +#ifdef SHARED_TIME +#undef time +time_t ap_shared_time(time_t *t) +{ + time_t res; + + if (child_timeouts || ap_scoreboard_image == NULL) { + return time(0); + } + res = ap_scoreboard_image->global.right_now; + if (t) *t = res; + return res; +} +#endif /* * More machine-dependent networking gooo... on some systems, @@ -2207,6 +2221,7 @@ static int volatile usr1_just_die = 1; static int volatile deferred_die; +#ifndef NO_GRACEFUL static void usr1_handler(int sig) { if (usr1_just_die) { @@ -2214,6 +2229,7 @@ } deferred_die = 1; } +#endif /* volatile just in case */ static int volatile shutdown_pending; @@ -3188,12 +3204,14 @@ BUFF *conn_io; request_rec *r; +#ifndef NO_GRACEFUL /* Prepare to receive a SIGUSR1 due to graceful restart so that * we can exit cleanly. Since we're between connections right * now it's the right time to exit, but we might be blocked in a * system call when the graceful restart request is made. */ usr1_just_die = 1; signal(SIGUSR1, usr1_handler); +#endif /* * (Re)initialize this child to a pre-connection state. @@ -3350,10 +3368,12 @@ SAFE_ACCEPT(accept_mutex_off()); /* unlock after "accept" */ +#ifndef NO_GRACEFUL /* We've got a socket, let's at least process one request off the * socket before we accept a graceful restart request. */ signal(SIGUSR1, SIG_IGN); +#endif ap_note_cleanups_for_fd(ptrans, csd); @@ -3451,6 +3471,7 @@ clean_child_exit(0); } +#ifndef NO_GRACEFUL /* In case we get a graceful restart while we're blocked * waiting for the request. * @@ -3466,6 +3487,7 @@ */ usr1_just_die = 1; signal(SIGUSR1, usr1_handler); +#endif } /* @@ -3618,6 +3640,10 @@ idle_count = 0; last_non_dead = -1; total_non_dead = 0; + +#ifdef SHARED_TIME + ap_scoreboard_image->global.right_now = now; +#endif ap_sync_scoreboard_image(); for (i = 0; i < ap_daemons_limit; ++i) { Index: src/main/http_protocol.c =================================================================== RCS file: /export/home/cvs/apache-1.3/src/main/http_protocol.c,v retrieving revision 1.211 diff -u -r1.211 http_protocol.c --- http_protocol.c 1998/04/11 12:00:30 1.211 +++ http_protocol.c 1998/04/13 18:44:14 @@ -654,7 +654,7 @@ } } /* we've probably got something to do, ignore graceful restart requests */ -#ifdef SIGUSR1 +#if defined(SIGUSR1) && !defined(NO_GRACEFUL) signal(SIGUSR1, SIG_IGN); #endif /* SIGUSR1 */ ap_bsetflag(conn->client, B_SAFEREAD, 0); Index: src/modules/standard/mod_include.c =================================================================== RCS file: /export/home/cvs/apache-1.3/src/modules/standard/mod_include.c,v retrieving revision 1.82 diff -u -r1.82 mod_include.c --- mod_include.c 1998/04/12 05:50:36 1.82 +++ mod_include.c 1998/04/13 18:44:16 @@ -94,6 +94,13 @@ #include "util_script.h" #endif +#ifdef TOP_FUEL +#ifdef ap_chdir_file +#undef ap_chdir_file +#endif +#define ap_chdir_file(x) do { } while(0) +#endif + #define STARTING_SEQUENCE "" #define DEFAULT_ERROR_MSG "[an error occurred while processing this directive]" @@ -112,32 +119,58 @@ #define NESTED_INCLUDE_MAGIC (&includes_module) /* ------------------------ Environment function -------------------------- */ + +static const char INCLUDE_TIME_FORMAT[] = "INCLUDE_TIME_FORMAT"; -static void add_include_vars(request_rec *r, char *timefmt) +static const char *get_envvar(request_rec *r, const char *name) { + const char *res; + table *e; + + e = r->subprocess_env; + res = ap_table_get(e, name); + if (res) { + return res; + } + if (!strcmp(name, "LAST_MODIFIED")) { + res = ap_ht_time(r->pool, r->finfo.st_mtime, + ap_table_get(e, INCLUDE_TIME_FORMAT), 0); + } + else if (!strcmp(name, "DATE_LOCAL")) { + res = ap_ht_time(r->pool, r->request_time, + ap_table_get(e, INCLUDE_TIME_FORMAT), 0); + } + else if (!strcmp(name, "DATE_GMT")) { + res = ap_ht_time(r->pool, r->request_time, + ap_table_get(e, INCLUDE_TIME_FORMAT), 1); + } #ifndef WIN32 - struct passwd *pw; + else if (!strcmp(name, "USER_NAME")) { + struct passwd *pw; + pw = getpwuid(r->finfo.st_uid); + if (pw) { + res = ap_pstrdup(r->pool, pw->pw_name); + } + else { + res = ap_psprintf(r->pool, "user#%lu", + (unsigned long) r->finfo.st_uid); + } + } #endif /* ndef WIN32 */ + if (res) { + ap_table_set(e, name, res); + } + return res; +} + +static void add_include_vars(request_rec *r) +{ table *e = r->subprocess_env; char *t; - time_t date = r->request_time; - ap_table_setn(e, "DATE_LOCAL", ap_ht_time(r->pool, date, timefmt, 0)); - ap_table_setn(e, "DATE_GMT", ap_ht_time(r->pool, date, timefmt, 1)); - ap_table_setn(e, "LAST_MODIFIED", - ap_ht_time(r->pool, r->finfo.st_mtime, timefmt, 0)); + ap_table_setn(e, INCLUDE_TIME_FORMAT, DEFAULT_TIME_FORMAT); ap_table_setn(e, "DOCUMENT_URI", r->uri); ap_table_setn(e, "DOCUMENT_PATH_INFO", r->path_info); -#ifndef WIN32 - pw = getpwuid(r->finfo.st_uid); - if (pw) { - ap_table_setn(e, "USER_NAME", ap_pstrdup(r->pool, pw->pw_name)); - } - else { - ap_table_setn(e, "USER_NAME", ap_psprintf(r->pool, "user#%lu", - (unsigned long) r->finfo.st_uid)); - } -#endif /* ndef WIN32 */ if ((t = strrchr(r->filename, '/'))) { ap_table_setn(e, "DOCUMENT_NAME", ++t); @@ -536,7 +569,7 @@ memcpy(var, start_of_var_name, l); var[l] = '\0'; - val = ap_table_get(r->subprocess_env, var); + val = get_envvar(r, var); if (val) { expansion = val; l = strlen(expansion); @@ -879,7 +912,7 @@ return 1; } if (!strcmp(tag, "var")) { - char *val = ap_table_get(r->subprocess_env, tag_val); + const char *val = get_envvar(r, tag_val); if (val) { ap_rputs(val, r); @@ -934,16 +967,26 @@ } #endif + +static void set_timefmt(request_rec *r, char *value) +{ + table *e = r->subprocess_env; + + ap_table_setn(e, INCLUDE_TIME_FORMAT, ap_pstrdup(r->pool, value)); + ap_table_unset(e, "DATE_LOCAL"); + ap_table_unset(e, "DATE_GMT"); + ap_table_unset(e, "LAST_MODIFIED"); +} + + /* error and tf must point to a string with room for at * least MAX_STRING_LEN characters */ -static int handle_config(FILE *in, request_rec *r, char *error, char *tf, - int *sizefmt) +static int handle_config(FILE *in, request_rec *r, char *error, int *sizefmt) { char tag[MAX_STRING_LEN]; char *tag_val; char parsed_string[MAX_STRING_LEN]; - table *env = r->subprocess_env; while (1) { if (!(tag_val = get_tag(r->pool, in, tag, sizeof(tag), 0))) { @@ -953,13 +996,8 @@ parse_string(r, tag_val, error, MAX_STRING_LEN, 0); } else if (!strcmp(tag, "timefmt")) { - time_t date = r->request_time; - - parse_string(r, tag_val, tf, MAX_STRING_LEN, 0); - ap_table_setn(env, "DATE_LOCAL", ap_ht_time(r->pool, date, tf, 0)); - ap_table_setn(env, "DATE_GMT", ap_ht_time(r->pool, date, tf, 1)); - ap_table_setn(env, "LAST_MODIFIED", - ap_ht_time(r->pool, r->finfo.st_mtime, tf, 0)); + parse_string(r, tag_val, parsed_string, MAX_STRING_LEN, 0); + set_timefmt(r, parsed_string); } else if (!strcmp(tag, "sizefmt")) { parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0); @@ -1072,7 +1110,7 @@ } } -static int handle_flastmod(FILE *in, request_rec *r, const char *error, const char *tf) +static int handle_flastmod(FILE *in, request_rec *r, const char *error) { char tag[MAX_STRING_LEN]; char *tag_val; @@ -1089,7 +1127,8 @@ else { parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0); if (!find_file(r, "flastmod", tag, parsed_string, &finfo, error)) { - ap_rputs(ap_ht_time(r->pool, finfo.st_mtime, tf, 0), r); + ap_rputs(ap_ht_time(r->pool, finfo.st_mtime, + ap_table_get(r->subprocess_env, INCLUDE_TIME_FORMAT), 0), r); } } } @@ -2018,7 +2057,12 @@ return -1; } parse_string(r, tag_val, parsed_string, sizeof(parsed_string), 0); - ap_table_setn(r->subprocess_env, var, ap_pstrdup(r->pool, parsed_string)); + if (strcmp(var, INCLUDE_TIME_FORMAT)) { + ap_table_setn(r->subprocess_env, var, ap_pstrdup(r->pool, parsed_string)); + } + else { + set_timefmt(r, parsed_string); + } } else { ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r->server, @@ -2041,6 +2085,11 @@ return 1; } else if (!strcmp(tag, "done")) { + /* force these to be defined */ + get_envvar(r, "DATE_LOCAL"); + get_envvar(r, "DATE_GMT"); + get_envvar(r, "LAST_MODIFIED"); + get_envvar(r, "USER_NAME"); for (i = 0; i < arr->nelts; ++i) { ap_rvputs(r, elts[i].key, "=", elts[i].val, "\n", NULL); } @@ -2064,7 +2113,6 @@ static void send_parsed_content(FILE *f, request_rec *r) { char directive[MAX_STRING_LEN], error[MAX_STRING_LEN]; - char timefmt[MAX_STRING_LEN]; int noexec = ap_allow_options(r) & OPT_INCNOEXEC; int ret, sizefmt; int if_nesting; @@ -2072,7 +2120,6 @@ int conditional_status; ap_cpystrn(error, DEFAULT_ERROR_MSG, sizeof(error)); - ap_cpystrn(timefmt, DEFAULT_TIME_FORMAT, sizeof(timefmt)); sizefmt = SIZEFMT_KMG; /* Turn printing on */ @@ -2151,7 +2198,7 @@ } } else if (!strcmp(directive, "config")) { - ret = handle_config(f, r, error, timefmt, &sizefmt); + ret = handle_config(f, r, error, &sizefmt); } else if (!strcmp(directive, "set")) { ret = handle_set(f, r, error); @@ -2166,7 +2213,7 @@ ret = handle_fsize(f, r, error, sizefmt); } else if (!strcmp(directive, "flastmod")) { - ret = handle_flastmod(f, r, error, timefmt); + ret = handle_flastmod(f, r, error); } else if (!strcmp(directive, "printenv")) { ret = handle_printenv(f, r, error); @@ -2311,7 +2358,7 @@ * environment */ ap_add_common_vars(r); ap_add_cgi_vars(r); - add_include_vars(r, DEFAULT_TIME_FORMAT); + add_include_vars(r); } /* XXX: this is bogus, at some point we're going to do a subrequest, * and when we do it we're going to be subjecting code that doesn't