Hi,

while configuring an mp2 with worker mpm I found it hard to guess how many 
interpreters are needed for my needs. The best way to measure the utilization 
of the interpreters I thought would be the time a request is blocked waiting 
for an interpreter to become available. The patch below measures this time 
and computes an exponential moving average similar to what Linux does to 
compute load averages.

I am aware the patch below is not ready to be included in mod_perl. It is 
rather a hack. But I think something like this is necessary to guess the 
right values for PerlInterpMax & co. If you like it please let me know how to 
shape it up.

The patch uses Brian Atkins' mod_slotmem (see 
http://www.gossamer-threads.com/lists/apache/dev/318465#318465) as shared 
memory implementation and hooks into mod_status for reporting.

I am using mod_perl mostly from Uri Translation to Fixup. So I have 
configured "PerlInterpScope handler". Now I see I can handle 800 parallel 
requests with 20 interpreters without much blocking whereas 1000 parallel 
requests lead to significant blocking intervals during a ramp-up phase. But 
when enough server processes are started even that load is handled without 
much blocking. Now my apache handles much more connections than with the 
prefork mpm and uses much less memory. Now network io or cpu speed is rather 
the limiting factor than memory.

Torsten
--- mod_perl-2.0.3/src/modules/perl/modperl_tipool.h	2006-11-20 00:30:59.000000000 +0100
+++ mod_perl-2.0.3.new/src/modules/perl/modperl_tipool.h	2007-03-21 12:19:30.000000000 +0100
@@ -17,6 +17,14 @@
 #ifndef MODPERL_TIPOOL_H
 #define MODPERL_TIPOOL_H
 
+#include <mod_slotmem.h>
+#include <mod_status.h>
+struct interp_slotitem {
+  apr_time_t timestamp;
+  double avg_L1, avg_L5, avg_L15;
+};
+typedef struct interp_slotitem interp_slotitem_t;
+
 #ifdef USE_ITHREADS
 
 modperl_list_t *modperl_list_new(void);
@@ -51,7 +59,7 @@
                                          void *data,
                                          modperl_list_t **listp);
 
-modperl_list_t *modperl_tipool_pop(modperl_tipool_t *tipool);
+modperl_list_t *modperl_tipool_pop(modperl_tipool_t *tipool, interp_slotitem_t *m);
 
 void modperl_tipool_putback(modperl_tipool_t *tipool,
                             modperl_list_t *listp,
--- mod_perl-2.0.3/src/modules/perl/mod_perl.c	2006-11-20 00:30:59.000000000 +0100
+++ mod_perl-2.0.3.new/src/modules/perl/mod_perl.c	2007-03-18 20:19:43.000000000 +0100
@@ -846,6 +846,9 @@
     ap_hook_post_config(modperl_hook_post_config_last,
                         NULL, NULL, APR_HOOK_REALLY_LAST);
 
+    APR_OPTIONAL_HOOK(ap, status_hook, modperl_interp_status_callback, NULL, NULL,
+                      APR_HOOK_MIDDLE);
+
     ap_hook_handler(modperl_response_handler,
                     NULL, NULL, APR_HOOK_MIDDLE);
 
--- mod_perl-2.0.3/src/modules/perl/modperl_interp.c	2006-11-20 00:30:59.000000000 +0100
+++ mod_perl-2.0.3.new/src/modules/perl/modperl_interp.c	2007-03-21 12:29:24.000000000 +0100
@@ -16,6 +16,8 @@
 
 #include "mod_perl.h"
 
+static ap_slotmem_t *interp_slotmem=0;
+
 /*
  * XXX: this is not the most efficent interpreter pool implementation
  * but it will do for proof-of-concept
@@ -156,14 +158,14 @@
     return APR_SUCCESS;
 }
 
-modperl_interp_t *modperl_interp_get(server_rec *s)
+modperl_interp_t *modperl_interp_get(server_rec *s, interp_slotitem_t *m)
 {
     MP_dSCFG(s);
     modperl_interp_t *interp = NULL;
     modperl_interp_pool_t *mip = scfg->mip;
     modperl_list_t *head;
 
-    head = modperl_tipool_pop(mip->tipool);
+    head = modperl_tipool_pop(mip->tipool, m);
     interp = (modperl_interp_t *)head->data;
 
     MP_TRACE_i(MP_FUNC, "head == 0x%lx, parent == 0x%lx\n",
@@ -238,6 +240,43 @@
     interp_pool_dump,
 };
 
+# define LN100 4.60517018598809136803
+# define L1 (-LN100/(60*APR_USEC_PER_SEC))
+# define L5 (-LN100/(300*APR_USEC_PER_SEC))
+# define L15 (-LN100/(900*APR_USEC_PER_SEC))
+static apr_status_t slotmem_callback( void *mem, void *data, apr_pool_t *p )
+{
+  interp_slotitem_t *m=mem;
+  interp_slotitem_t *d=data;
+
+  d->avg_L1+=m->avg_L1*exp((double)(d->timestamp-m->timestamp)*L1);
+  d->avg_L5+=m->avg_L5*exp((double)(d->timestamp-m->timestamp)*L5);
+  d->avg_L15+=m->avg_L15*exp((double)(d->timestamp-m->timestamp)*L15);
+
+  return APR_SUCCESS;
+}
+
+int modperl_interp_status_callback(request_rec *r, int flags)
+{
+    interp_slotitem_t sl;
+
+    if(!interp_slotmem) return OK;
+
+    memset(&sl, 0, sizeof(sl));
+    sl.timestamp=apr_time_now();
+    ap_slotmem_do( interp_slotmem, slotmem_callback, &sl, NULL );
+
+    if (!(flags & AP_STATUS_SHORT)) {
+      ap_rprintf(r,"<hr />");
+      ap_rprintf(r, "<b>modperl interpreter pool blocking time (avg. 1, 5, 15 minutes)"
+		    " in usec:</b> %.3f %.3f %.3f<br />\n", sl.avg_L1, sl.avg_L5, sl.avg_L15 );
+    } else {
+      ap_rprintf(r, "modperl interpreter pool blocking time (avg. 1, 5, 15 minutes)"
+		    " in usec: %.3f %.3f %.3f\n", sl.avg_L1, sl.avg_L5, sl.avg_L15 );
+    }
+    return OK;
+}
+
 void modperl_interp_init(server_rec *s, apr_pool_t *p,
                          PerlInterpreter *perl)
 {
@@ -250,6 +289,8 @@
     MP_TRACE_i(MP_FUNC, "server=%s\n", modperl_server_desc(s, p));
 
     if (modperl_threaded_mpm()) {
+        ap_slotmem_create(&interp_slotmem, "modperl_interp_pool",
+			  sizeof(struct interp_slotitem), p);
         mip->tipool = modperl_tipool_new(p, scfg->interp_pool_cfg,
                                          &interp_pool_func, mip);
     }
@@ -286,6 +327,7 @@
         MP_dRCFG;
         modperl_config_request_cleanup(interp->perl, r);
         MpReqCLEANUP_REGISTERED_Off(rcfg);
+        interp->request=NULL;
     }
 
     MpInterpIN_USE_Off(interp);
@@ -357,7 +399,7 @@
     }
     else {
         if (!(interp = modperl_interp_pool_get(p))) {
-            interp = modperl_interp_get(s);
+	  interp = modperl_interp_get(s, NULL);
             modperl_interp_pool_set(p, interp, TRUE);
 
             MP_TRACE_i(MP_FUNC, "set interp in request time pool 0x%lx\n",
@@ -383,6 +425,7 @@
     apr_pool_t *p = NULL;
     int is_subrequest = (r && r->main) ? 1 : 0;
     modperl_interp_scope_e scope;
+    interp_slotitem_t *slotitem;
 
     if (!modperl_threaded_mpm()) {
         MP_TRACE_i(MP_FUNC,
@@ -478,7 +521,8 @@
         }
     }
 
-    interp = modperl_interp_get(s ? s : r->server);
+    ap_slotmem_mem(interp_slotmem, c?c:r->connection, (void**)&slotitem);
+    interp = modperl_interp_get(s ? s : r->server, slotitem);
     ++interp->num_requests; /* should only get here once per request */
 
     if (scope == MP_INTERP_SCOPE_HANDLER) {
--- mod_perl-2.0.3/src/modules/perl/modperl_interp.h	2006-11-20 00:30:59.000000000 +0100
+++ mod_perl-2.0.3.new/src/modules/perl/modperl_interp.h	2007-03-18 20:14:30.000000000 +0100
@@ -21,6 +21,7 @@
                          PerlInterpreter *perl);
 
 apr_status_t modperl_interp_cleanup(void *data);
+int modperl_interp_status_callback(request_rec *r, int flags);
 
 #ifdef USE_ITHREADS
 
@@ -60,7 +61,7 @@
 
 void modperl_interp_destroy(modperl_interp_t *interp);
 
-modperl_interp_t *modperl_interp_get(server_rec *s);
+modperl_interp_t *modperl_interp_get(server_rec *s, interp_slotitem_t *slot);
 
 apr_status_t modperl_interp_unselect(void *data);
 
--- mod_perl-2.0.3/src/modules/perl/modperl_tipool.c	2006-11-20 00:30:59.000000000 +0100
+++ mod_perl-2.0.3.new/src/modules/perl/modperl_tipool.c	2007-03-21 12:25:13.000000000 +0100
@@ -246,9 +246,15 @@
                (unsigned long)listp, tipool->size);
 }
 
-modperl_list_t *modperl_tipool_pop(modperl_tipool_t *tipool)
+# define LN100 4.60517018598809136803
+# define L1 (-LN100/(60*APR_USEC_PER_SEC))
+# define L5 (-LN100/(300*APR_USEC_PER_SEC))
+# define L15 (-LN100/(900*APR_USEC_PER_SEC))
+modperl_list_t *modperl_tipool_pop(modperl_tipool_t *tipool, interp_slotitem_t *m)
 {
     modperl_list_t *head;
+    apr_time_t blocking_time, timestamp;
+    double f;
 
     modperl_tipool_lock(tipool);
 
@@ -265,8 +271,24 @@
             }
         }
         /* block until an item becomes available */
+	if(m) blocking_time=apr_time_now();
         modperl_tipool_wait(tipool);
-    }
+	if(m) {
+	    timestamp=apr_time_now();
+	    blocking_time=timestamp-blocking_time;
+
+	    f=exp((double)(timestamp-m->timestamp)*L1);
+	    m->avg_L1=(double)blocking_time*(1-f)+m->avg_L1*f;
+
+	    f=exp((double)(timestamp-m->timestamp)*L5);
+	    m->avg_L5=(double)blocking_time*(1-f)+m->avg_L5*f;
+
+	    f=exp((double)(timestamp-m->timestamp)*L15);
+	    m->avg_L15=(double)blocking_time*(1-f)+m->avg_L15*f;
+
+	    m->timestamp=timestamp;
+	}
+    }             
 
     head = tipool->idle;
 

Attachment: pgpRjzI8d0r0B.pgp
Description: PGP signature

Reply via email to