CGI.pm is a big module that by default postpones the compilation of its methods until they are actually needed, thus making it possible to use it under a slow mod_cgi handler without adding a big startup overhead. That's not what we want under mod_perl—if you use CGI.pm, in addition to preloading the module at server startup, you should precompile the methods that you are going to use. To do that, simply call the compile( ) method:

use CGI;
CGI->compile(':all');

You should replace the tag group :all with the real tags and group tags that you are going to use if you want to optimize memory usage.

We are going to compare the shared-memory footprint using a script that is backward compatible with mod_cgi. You can improve the performance of this kind of script as well, but if you really want fast code, think about porting it to use Apache::Request[39] for the CGI interface and some other module for your HTML generation.

[39]Apache::Request is significantly faster than CGI.pm because its methods for processing a request's arguments are written in C.

The Apache::Registryscript that we are going to use to make the comparison is shown in Example 10-11.

Example 10-11. preload_cgi_pm.pl

use strict;
use CGI ( );
use GTop ( );

my $q = new CGI;
print $q->header('text/plain');
print join "\n", map {"$_ => ".$q->param($_) } $q->param;
print "\n";

my $proc_mem = GTop->new->proc_mem($$);
my $size  = $proc_mem->size;
my $share = $proc_mem->share;
my $diff  = $size - $share;
printf "%8s %8s %8s\n", qw(Size Shared Unshared);
printf "%8d %8d %8d (bytes)\n", $size, $share, $diff;

The script initializes the CGI object, sends the HTTP header, and then prints any arguments and values that were passed to it. At the end, as usual, we print the memory usage.

Again, we are going to use a single child process. Here is part of our httpd.conf file:

MinSpareServers 1
MaxSpareServers 1
StartServers 1
MaxClients 1
MaxRequestsPerChild 100

We always preload the Gtop module:

use Gtop ( );

We are going to run memory benchmarks on three different versions of the startup.pl file:

Version 1
Leave the file unmodified.

Version 2
Preload CGI.pm:

use CGI ( );
Version 3
Preload CGI.pm and precompile the methods that we are going to use in the script:

use CGI ( );
CGI->compile(qw(header param));

Here are the results of the three tests, sorted by the Unshared column. The server was restarted before each new test.

  1. After the first request:

      Test type                         Size     Shared   Unshared
     ------------------------------------------------------------
      (3) preloaded & methods+compiled  3244032  2465792   778240
      (2) preloaded                     3321856  2326528   995328
      (1) not preloaded                 3321856  2146304  1175552
  2. After the second request (the subsequent request showed the same results):

      Test type                         Size     Shared   Unshared
     -------------------------------------------------------------
      (3) preloaded & methods+compiled  3248128  2445312   802816
      (2) preloaded                     3325952  2314240  1011712
      (1) not preloaded                 3325952  2134016  1191936

Since the memory usage stabilized after the second request, we are going to look at the second table. By comparing the first (not preloaded) and the second (preloaded) versions, we can see that preloading adds about 180 KB (2314240 - 2134016 bytes) of shared memory size, which is the result we expect from most modules. However, by comparing the second (preloaded) and the third (preloaded and precompiled methods) options, we can see that by precompiling methods, we gain 207 KB (1011712 - 802816 bytes) more of shared memory. And we have used only a few methods (the header method loads a few more methods transparently for the user). The gain grows as more of the used methods are precompiled. If you use CGI.pm's functional interface, all of the above applies as well.

Even in our very simple case using the same formula, what do we see? Let's again assume that we have 256 MB dedicated for mod_perl.

Version 1:

Figure

Version 3:

Figure

If we preload CGI.pm and precompile a few methods that we use in the test script, we can have 50% more child processes than when we don't preload and precompile the methods that we are going to use.

Note that CGI.pm Versions 3.x are supposed to be much less bloated, but make sure to test your code as we just demonstrated.