Apache::args, Apache::Request::param, and CGI::param are the three most common ways to process input arguments in mod_perl handlers and scripts. Let's write three Apache::Registryscripts that use Apache::args, Apache::Request::param, and CGI::param to process a form's input and print it out. Notice that Apache::args is considered identical to Apache::Request::param only when you have single-valued keys. In the case of multi-valued keys (e.g., when using checkbox groups), you will have to write some extra code. If you do a simple:
my %params = $r->args;
only the last value will be stored and the rest will collapse, because that's what happens when you turn a list into a hash. Assuming that you have the following list:
(rules => 'Apache', rules => 'Perl', rules => 'mod_perl')
and assign it to a hash, the following happens:
$hash{rules} = 'Apache'; $hash{rules} = 'Perl'; $hash{rules} = 'mod_perl';
So at the end only the following pair will get stored:
rules => 'mod_perl'
With CGI.pm or Apache::Request, you can solve this by extracting the whole list by its key:
my @values = $q->param('rules');
In addition, Apache::Request and CGI.pm have many more functions that ease input processing, such as handling file uploads. However, Apache::Request is theoretically much faster, since its guts are implemented in C, glued to Perl using XS code.
Assuming that the only functionality you need is the parsing of key-value pairs, and assuming that every key has a single value, we will compare the almost identical scripts in Examples 13-3, 13-4, and 13-5 by trying to pass various query strings.
use strict; my $r = shift; $r->send_http_header('text/plain'); my %args = $r->args; print join "\n", map {"$_ => $args{$_}" } keys %args;
use strict; use Apache::Request ( ); my $r = shift; my $q = Apache::Request->new($r); $r->send_http_header('text/plain'); my %args = map {$_ => $q->param($_) } $q->param; print join "\n", map {"$_ => $args{$_}" } keys %args;
use strict; use CGI; my $r = shift; my $q = new CGI; $r->send_http_header('text/plain'); my %args = map {$_ => $q->param($_) } $q->param; print join "\n", map {"$_ => $args{$_}" } keys %args;
All three scripts and the modules they use are preloaded at server startup in startup.pl:
use Apache::RegistryLoader ( ); use CGI ( ); CGI->compile('param'); use Apache::Request ( ); # Preload registry scripts Apache::RegistryLoader->new->handler( "/perl/processing_with_cgi_pm.pl", "/home/httpd/perl/processing_with_cgi_pm.pl" ); Apache::RegistryLoader->new->handler( "/perl/processing_with_apache_request.pl", "/home/httpd/perl/processing_with_apache_request.pl" ); Apache::RegistryLoader->new->handler( "/perl/processing_with_apache_args.pl", "/home/httpd/perl/processing_with_apache_args.pl" ); 1;
We use four different query strings, generated by:
my @queries = ( join("&", map {"$_=" . 'e' x 10} ('a'..'b')), join("&", map {"$_=" . 'e' x 50} ('a'..'b')), join("&", map {"$_=" . 'e' x 5 } ('a'..'z')), join("&", map {"$_=" . 'e' x 10} ('a'..'z')), );
The first string is:
a=eeeeeeeeee&b=eeeeeeeeee
which is 25 characters in length and consists of two key/value pairs. The second string is also made of two key/value pairs, but the values are 50 characters long (a total of 105 characters). The third and fourth strings are each made from 26 key/value pairs, with value lengths of 5 and 10 characters respectively and total lengths of 207 and 337 characters respectively. The query_len column in the report table is one of these four total lengths.
We conduct the benchmark with a concurrency level of 50 and generate 5,000 requests for each test. The results are:
--------------------------------------------- name val_len pairs query_len | avtime rps --------------------------------------------- apreq 10 2 25 | 51 945 apreq 50 2 105 | 53 907 r_args 50 2 105 | 53 906 r_args 10 2 25 | 53 899 apreq 5 26 207 | 64 754 apreq 10 26 337 | 65 742 r_args 5 26 207 | 73 665 r_args 10 26 337 | 74 657 cgi_pm 50 2 105 | 85 573 cgi_pm 10 2 25 | 87 559 cgi_pm 5 26 207 | 188 263 cgi_pm 10 26 337 | 188 262 ---------------------------------------------
where apreqstands for Apache::Request::param( ), r_argsstands for Apache::args( ) or $r->args( ), and cgi_pmstands for CGI::param( ).
You can see that Apache::Request::param and Apache::args have similar performance with a few key/value pairs, but the former is faster with many key/value pairs. CGI::param is significantly slower than the other two methods.
These results also suggest that the processing gets progressively slower as the number of key/value pairs grows, but longer lengths of the key/value pairs have less of a slowdown impact. To verify that, let's use the Apache::Request::param method and first test several query strings made of five key/value pairs with value lengths growing from 10 characters to 60 in steps of 10:
my @strings = map {'e' x (10*$_)} 1..6; my @ae = ('a'..'e'); my @queries = ( ); for my $string (@strings) { push @queries, join "&", map {"$_=$string"} @ae; }
The results are:
----------------------------------- val_len query_len | avtime rps ----------------------------------- 10 77 | 55 877 20 197 | 55 867 30 257 | 56 859 40 137 | 56 858 50 317 | 56 857 60 377 | 58 828 -----------------------------------
Indeed, the length of the value influences the speed very little, as we can see that the average processing time almost doesn't change as the length of the value grows.
Now let's use a fixed value length of 10 characters and test with a varying number of key/value pairs, from 2 to 26 in steps of 5:
my @az = ('a'..'z'); my @queries = map { join("&", map {"$_=" . 'e' x 10 } @az[0..$_]) } (1, 5, 10, 15, 20, 25);
The results are:
------------------------------- pairs query_len | avtime rps ------------------------------- 2 25 | 53 906 6 77 | 55 869 12 142 | 57 838 16 207 | 61 785 21 272 | 64 754 26 337 | 66 726 -------------------------------
Now by looking at the average processing time column, we can see that the number of key/value pairs makes a significant impact on processing speed.
Copyright © 2003 O'Reilly & Associates. All rights reserved.