--- /dev/null
+#!/usr/bin/perl -w
+
+=head1 NAME
+
+memory_app.pl - Test memory usage and benchmark speed comparison with CGI::Application
+
+=cut
+
+use Benchmark qw(cmpthese timethese);
+use strict;
+
+my $swap = {
+ one => "ONE",
+ two => "TWO",
+ three => "THREE",
+ a_var => "a",
+ hash => {a => 1, b => 2},
+ code => sub {"($_[0])"},
+};
+
+my $form = q{([% has_errors %])(<TMPL_VAR has_errors>)<form name=foo><input type=text name="bar" value=""><input type=text name="baz"></form>};
+my $str_ht = $form . (q{Well hello there (<TMPL_VAR script_name>)} x 20) ."\n";
+my $str_tt = $form . (q{Well hello there ([% script_name %])} x 20) ."\n";
+
+my $template_ht = \$str_ht;
+my $template_tt = \$str_tt;
+
+###----------------------------------------------------------------###
+use Scalar::Util;
+use Time::HiRes;
+use CGI;
+use CGI::Ex::Dump qw(debug);
+use Template::Alloy load => 'Parse', 'Play', 'HTML::Template', 'Template';
+$Template::VERSION = 2.18;
+#use HTML::Template;
+
+my $tests = {
+ 'C::A - bare' => sub {
+ package FooBare;
+ require CGI::Application;
+ @FooBare::ISA = qw(CGI::Application);
+
+ sub setup {
+ my $self = shift;
+ $self->start_mode('main');
+ $self->mode_param(path_info => 1);
+ $self->run_modes(main => sub { "Simple test" });
+ }
+
+ FooBare->new->run;
+ },
+ 'C::E::A - bare' => sub {
+ package FooBare;
+ require CGI::Ex::App;
+ @FooBare::ISA = qw(CGI::Ex::App);
+
+ sub main_run_step {
+ my $self = shift;
+ print "Content-Type: text/html\r\n\r\n";
+ #$self->cgix->print_content_type;
+ print "Simple test";
+ 1;
+ }
+
+ FooBare->navigate({form => {}});
+ },
+ 'Handwritten - bare' => sub {
+ package FooBare2;
+
+ sub new { bless {}, __PACKAGE__ }
+
+ sub main {
+ my $self = shift;
+ print "Content-Type: text/html\r\n\r\n";
+ print "Simple test";
+ }
+
+ FooBare2->new->main;
+ },
+ #'CGI::Prototype - bare' => sub {
+ # package FooBare;
+ # require CGI::Prototype;
+ #},
+
+ ###----------------------------------------------------------------###
+
+ #'C::A - simple htonly' => sub {
+ # require CGI::Application;
+ # my $t = CGI::Application->new->load_tmpl($template_ht, die_on_bad_params => 0);
+ # $t->param(script_name => 2);
+ # print $t->output;
+ #},
+ #'C::E::A - simple htonly' => sub {
+ # require CGI::Ex::App;
+ # my $out = '';
+ # CGI::Ex::App->new->template_obj({SYNTAX => 'hte'})->process($template_ht, {script_name=>2}, \$out);
+ # print $out;
+ #},
+
+ 'C::A - simple ht' => sub {
+ package FooHT;
+ require CGI::Application;
+ @FooHT::ISA = qw(CGI::Application);
+
+ sub setup {
+ my $self = shift;
+ $self->start_mode('main');
+ $self->mode_param(path_info => 1);
+ $self->run_modes(main => sub {
+ my $self = shift;
+ my $t = $self->load_tmpl($template_ht, die_on_bad_params => 0);
+ $t->param('script_name', $0);
+ return $t->output();
+ });
+ }
+
+ FooHT->new->run;
+ },
+ 'C::E::A - simple ht' => sub {
+ package FooHT;
+ require CGI::Ex::App;
+ @FooHT::ISA = qw(CGI::Ex::App);
+
+ sub main_file_print { $template_ht }
+ sub template_args { {SYNTAX => 'hte'} } # , GLOBAL_CACHE => 1, COMPILE_PERL => 2} }
+ sub fill_template {}
+ sub print_out { my ($self, $step, $out) = @_; print "Content-Type: text/html\r\n\r\n$$out" }
+
+ FooHT->navigate({no_history => 1, form => {}});
+ },
+ 'C::A - simple tt' => sub {
+ package FooTT;
+ require CGI::Application;
+ @FooTT::ISA = qw(CGI::Application);
+ require CGI::Application::Plugin::TT;
+ CGI::Application::Plugin::TT->import;
+
+ sub setup {
+ my $self = shift;
+ $self->start_mode('main');
+
+ $self->run_modes(main => sub {
+ my $self = shift;
+ return $self->tt_process($template_tt, {script_name => $0});
+ });
+ }
+
+ FooTT->new->run;
+ },
+ 'C::E::A - simple tt' => sub {
+ package FooTT;
+ require CGI::Ex::App;
+ @FooTT::ISA = qw(CGI::Ex::App);
+ sub main_file_print { $template_tt }
+ sub fill_template {}
+ sub print_out { my ($self, $step, $out) = @_; print "Content-Type: text/html\r\n\r\n$$out" }
+ FooTT->navigate({no_history => 1, form => {}});
+ },
+
+ ###----------------------------------------------------------------###
+
+ 'C::A - complex ht' => sub {
+ package FooComplexHT;
+ require CGI::Application;
+ @FooComplexHT::ISA = qw(CGI::Application);
+ require CGI::Application::Plugin::ValidateRM;
+ CGI::Application::Plugin::ValidateRM->import('check_rm');
+ require CGI::Application::Plugin::FillInForm;
+ CGI::Application::Plugin::FillInForm->import('fill_form');
+
+ sub setup {
+ my $self = shift;
+ $self->start_mode('main');
+ $self->mode_param(path_info => 1);
+ $self->run_modes(main => sub {
+ my $self = shift;
+ my ($results, $err_page) = $self->check_rm('error_page','_profile');
+ return $err_page if $err_page;
+ die "Got here";
+ });
+ }
+
+ sub error_page {
+ my $self = shift;
+ my $errs = shift;
+ my $t = $self->load_tmpl($template_ht, die_on_bad_params => 0);
+ $t->param('script_name', $0);
+ $t->param($errs) if $errs;
+ $t->param(has_errors => 1) if $errs;
+ my $q = $self->query;
+ $q->param(bar => 'BAROOSELVELT');
+ return $self->fill_form(\$t->output, $q);
+ }
+
+ sub _profile { return {required => [qw(bar baz)], msgs => {prefix => 'err_'}} };
+
+ FooComplexHT->new->run;
+ },
+ 'C::E::A - complex ht' => sub {
+ package FooComplexHT;
+ require CGI::Ex::App;
+ @FooComplexHT::ISA = qw(CGI::Ex::App);
+
+ sub main_file_print { $template_ht }
+ sub main_hash_fill { {bar => 'BAROOSELVELT'} }
+ sub main_hash_validation { {bar => {required => 1}, baz => {required => 1}} }
+ sub main_finalize { die "Got here" }
+ sub template_args { {SYNTAX => 'hte'} } # , GLOBAL_CACHE => 1, COMPILE_PERL => 2} }
+ sub print_out { my ($self, $step, $out) = @_; print "Content-Type: text/html\r\n\r\n$$out" }
+
+ local $ENV{'REQUEST_METHOD'} = 'POST';
+ FooComplexHT->navigate({no_history => 1, form => {}});
+ },
+ 'C::A - complex tt' => sub {
+ package FooComplexTT;
+ require CGI::Application;
+ @FooComplexTT::ISA = qw(CGI::Application);
+ require CGI::Application::Plugin::TT;
+ CGI::Application::Plugin::TT->import;
+ require CGI::Application::Plugin::ValidateRM;
+ CGI::Application::Plugin::ValidateRM->import('check_rm');
+ require CGI::Application::Plugin::FillInForm;
+ CGI::Application::Plugin::FillInForm->import('fill_form');
+
+ sub setup {
+ my $self = shift;
+ $self->start_mode('main');
+
+ $self->run_modes(main => sub {
+ my $self = shift;
+ my ($results, $err_page) = $self->check_rm('error_page','_profile');
+ return $err_page if $err_page;
+ die "Got here";
+ });
+ }
+
+ sub error_page {
+ my $self = shift;
+ my $errs = shift;
+ my $out = $self->tt_process($template_tt, {script_name => $0, %{$errs || {}}, has_errors => ($errs ? 1 : 0)});
+ my $q = $self->query;
+ $q->param(bar => 'BAROOSELVELT');
+ return $self->fill_form(\$out, $q);
+ }
+
+ sub _profile { return {required => [qw(bar baz)], msgs => {prefix => 'err_'}} };
+
+ FooComplexTT->new->run;
+ },
+ 'C::E::A - complex tt' => sub {
+ package FooComplexTT;
+ require CGI::Ex::App;
+ @FooComplexTT::ISA = qw(CGI::Ex::App);
+ sub main_file_print { $template_tt }
+ sub main_hash_fill { {bar => 'BAROOSELVELT'} }
+ sub main_hash_validation { {bar => {required => 1}, baz => {required => 1}} }
+ sub main_finalize { die "Got here" }
+ sub print_out { my ($self, $step, $out) = @_; print "Content-Type: text/html\r\n\r\n$$out" }
+
+ local $ENV{'REQUEST_METHOD'} = 'POST';
+ FooComplexTT->navigate({no_history => 1, form => {}});
+ },
+
+ #'Template::Alloy - bare ht' => sub { require Template::Alloy; Template::Alloy->import('HTE') },
+ #'Template::Alloy - bare tt' => sub { require Template::Alloy; Template::Alloy->import('TT') },
+};
+
+#perl -d:DProf samples/devel/memory_app.pl ; dprofpp tmon.out
+#select($_) if open($_, ">>/dev/null");
+$tests->{'C::E::A - complex tt'}->()
+# for 1 .. 1000
+ ;
+#exit;
+
+###----------------------------------------------------------------###
+
+my %_INC = %INC;
+my @pids;
+foreach my $name (sort keys %$tests) {
+ my $pid = fork;
+ if (! $pid) {
+ $0 = "$0 - $name";
+ my $fh;
+ select($fh) if open($fh, ">>/dev/null");
+ $tests->{$name}->() for 1 .. 1;
+ sleep 1;
+ select STDOUT;
+ print "$name times: (@{[times]})\n";
+ print "$name $_\n" foreach sort grep {! $_INC{$_}} keys %INC;
+ sleep 15;
+ exit;
+ }
+ push @pids, $pid;
+}
+
+sleep 2;
+# print "Parent - $_\n" foreach sort keys %INC;
+print grep {/\Q$0\E/} `ps fauwx`;
+kill 15, @pids;
+
+###----------------------------------------------------------------###
+
+exit if grep {/no_?bench/i} @ARGV;
+
+
+foreach my $type (qw(bare simple complex)) {
+ my $hash = {};
+ open(my $fh, ">>/dev/null") || die "Can't access /dev/null: $!";
+ foreach my $name (keys %$tests) {
+ next if $name !~ /\b$type\b/;
+ (my $copy = $name) =~ s/\s*\b$type\b//;
+ $hash->{$copy} = sub {
+ select $fh;
+ $tests->{$name}->();
+ select STDOUT;
+ };
+ }
+ print "-------------------------------------------------\n";
+ print "--- Testing $type\n";
+ cmpthese timethese -2, $hash;
+}
+
+=head1 NOTES
+
+Abbreviations:
+
+ C::E::A - CGI::Ex::App
+ C::A - CGI::Application
+
+The tests are currently run with the following code:
+
+ use Template::Alloy load => 'Parse', 'Play', 'HTML::Template', 'Template';
+
+This assures that CGI::Application will use the same templating system
+as CGI::Ex::App so that template system issues don't affect overall
+performance. With the line commented out and CGI::Application using
+HTML::Template (ht), C::A has a slight speed benefit, though it still
+uses more memory. With the line commented out and CGI::Application
+using Template (tt), C::E::A is 2 to 3 times faster and uses a lot
+less memory.
+
+=head1 SAMPLE OUTPUT
+
+ paul 23927 4.3 0.5 8536 6016 pts/1 S+ 11:36 0:00 | \_ perl samples/devel/memory_app.pl
+ paul 23928 1.0 0.5 8988 5992 pts/1 S+ 11:36 0:00 | \_ samples/devel/memory_app.pl - C::A - bare
+ paul 23929 2.0 0.6 9988 7152 pts/1 S+ 11:36 0:00 | \_ samples/devel/memory_app.pl - C::A - complex ht
+ paul 23930 2.5 0.7 10172 7336 pts/1 S+ 11:36 0:00 | \_ samples/devel/memory_app.pl - C::A - complex tt
+ paul 23931 1.0 0.5 8988 6024 pts/1 S+ 11:36 0:00 | \_ samples/devel/memory_app.pl - C::A - simple ht
+ paul 23932 1.5 0.6 9308 6276 pts/1 S+ 11:36 0:00 | \_ samples/devel/memory_app.pl - C::A - simple tt
+ paul 23933 0.0 0.5 8536 5200 pts/1 S+ 11:36 0:00 | \_ samples/devel/memory_app.pl - C::E::A - bare
+ paul 23934 1.0 0.6 9328 6384 pts/1 S+ 11:36 0:00 | \_ samples/devel/memory_app.pl - C::E::A - complex ht
+ paul 23935 1.0 0.6 9328 6392 pts/1 S+ 11:36 0:00 | \_ samples/devel/memory_app.pl - C::E::A - complex tt
+ paul 23936 0.0 0.5 8536 5272 pts/1 S+ 11:36 0:00 | \_ samples/devel/memory_app.pl - C::E::A - simple ht
+ paul 23937 0.0 0.5 8668 5344 pts/1 S+ 11:36 0:00 | \_ samples/devel/memory_app.pl - C::E::A - simple tt
+ paul 23938 0.0 0.4 8536 5076 pts/1 S+ 11:36 0:00 | \_ samples/devel/memory_app.pl - Handwritten - bare
+ -------------------------------------------------
+ --- Testing bare
+ Benchmark: running C::A -, C::E::A -, Handwritten - for at least 2 CPU seconds...
+ C::A -: 3 wallclock secs ( 2.08 usr + 0.01 sys = 2.09 CPU) @ 3196.17/s (n=6680)
+ C::E::A -: 3 wallclock secs ( 1.99 usr + 0.19 sys = 2.18 CPU) @ 6164.68/s (n=13439)
+ Handwritten -: 1 wallclock secs ( 2.15 usr + 0.00 sys = 2.15 CPU) @ 266711.16/s (n=573429)
+ Rate C::A - C::E::A - Handwritten -
+ C::A - 3196/s -- -48% -99%
+ C::E::A - 6165/s 93% -- -98%
+ Handwritten - 266711/s 8245% 4226% --
+ -------------------------------------------------
+ --- Testing simple
+ Benchmark: running C::A - ht, C::A - tt, C::E::A - ht, C::E::A - tt for at least 2 CPU seconds...
+ C::A - ht: 2 wallclock secs ( 2.04 usr + 0.00 sys = 2.04 CPU) @ 709.80/s (n=1448)
+ C::A - tt: 2 wallclock secs ( 2.12 usr + 0.01 sys = 2.13 CPU) @ 600.47/s (n=1279)
+ C::E::A - ht: 2 wallclock secs ( 2.14 usr + 0.01 sys = 2.15 CPU) @ 663.26/s (n=1426)
+ C::E::A - tt: 3 wallclock secs ( 2.16 usr + 0.01 sys = 2.17 CPU) @ 589.40/s (n=1279)
+ Rate C::E::A - tt C::A - tt C::E::A - ht C::A - ht
+ C::E::A - tt 589/s -- -2% -11% -17%
+ C::A - tt 600/s 2% -- -9% -15%
+ C::E::A - ht 663/s 13% 10% -- -7%
+ C::A - ht 710/s 20% 18% 7% --
+ -------------------------------------------------
+ --- Testing complex
+ Benchmark: running C::A - ht, C::A - tt, C::E::A - ht, C::E::A - tt for at least 2 CPU seconds...
+ C::A - ht: 2 wallclock secs ( 2.00 usr + 0.00 sys = 2.00 CPU) @ 438.50/s (n=877)
+ C::A - tt: 3 wallclock secs ( 2.16 usr + 0.00 sys = 2.16 CPU) @ 383.80/s (n=829)
+ C::E::A - ht: 2 wallclock secs ( 2.14 usr + 0.01 sys = 2.15 CPU) @ 457.21/s (n=983)
+ C::E::A - tt: 2 wallclock secs ( 2.13 usr + 0.00 sys = 2.13 CPU) @ 417.37/s (n=889)
+ Rate C::A - tt C::E::A - tt C::A - ht C::E::A - ht
+ C::A - tt 384/s -- -8% -12% -16%
+ C::E::A - tt 417/s 9% -- -5% -9%
+ C::A - ht 438/s 14% 5% -- -4%
+ C::E::A - ht 457/s 19% 10% 4% --
+
+=cut