This is part of The Pile, a partial archive of some open source mailing lists and newsgroups.
To: modperl@perl.apache.org From: Stas Bekman <stas@stason.org> Subject: updated benchmarks for Apache::args Date: Thu, 09 May 2002 18:06:30 +0800 Finally I was able to rerun the benchmarks for Apache::args vs. Apache::Request::param vs. CGI::param, using Apache::Request 1.0. And indeed it's much faster now and is the fastest among the three. Here is the updated section (soon to appear in the guide): =head1 Apache::args vs. Apache::Request::param vs. CGI::param C<Apache::args>, C<Apache::Request::param> and C<CGI::param> are the three most common ways to process input arguments in mod_perl handlers and scripts. Let's write three C<Apache::Registry> scripts that use C<Apache::args>, C<Apache::Request::param> and C<CGI::param> to process a form's input and print it out. Notice that C<Apache::args> is considered identical to C<Apache::Request::param> only when you have single valued keys. In the case of multi-valued keys (e.g. when using check-box 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: rules => 'mod_perl' pair will get stored. With C<CGI.pm> or C<Apache::Request> you can solve this by extracting the whole list by its key: my @values = $q->params('rules'); In addition C<Apache::Request> and C<CGI.pm> have many more functions that ease input processing, like handling file uploads. However C<Apache::Request> is 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 following almost identical scripts, by trying to pass various query strings. Here's the code: file:processing_with_apache_args.pl ----------------------------------- use strict; my $r = shift; $r->send_http_header('text/plain'); my %args = $r->args; print join "\n", map {"$_ => ".$args{$_} } keys %args; file:processing_with_apache_request.pl -------------------------------------- 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; file:processing_with_cgi_pm.pl ------------------------------ use strict; use CGI; my $r = shift; $r->send_http_header('text/plain'); my $q = new CGI; my %args = map {$_ => $q->param($_) } $q->param; print join "\n", map {"$_ => ".$args{$_} } keys %args; All three scripts are preloaded at server startup: <Perl> use Apache::RegistryLoader (); 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" ); </Perl> 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 value is 50 characters long (total 105 characters). The third and the forth strings are made from 26 key/value pairs, with the value lengths of 5 and 10 characters respectively, with total lengths of 207 and 337 characters respectively. The C<query_len> column in the report table is one of these four total lengths. We conduct the benchmark with concurrency level of 50 and generate 5000 requests for each test. And 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 C<apreq> stands for C<Apache::Request::param()>, C<r_args> stands for C<Apache::args()> or C<$r-E<gt>args()> and C<cgi_pm> stands for C<CGI::param()>. You can see that C<Apache::Request::param> and C<Apache::args> have similar performance with a few key/value pairs, but the former is faster with many key/value pairs. C<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 C<Apache::Request::param> method and first test several query strings made of 5 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; } And the results: ----------------------------------- 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 lengths of the values influence the speed very little, as we can see that the average processing time almost doesn't change as the length of the values 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); And the results: ------------------------------- 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. ===