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.
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:
use CGI ( );
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.
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
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:
Version 3:
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.
 
Continue to: