agianni has asked for the wisdom of the Perl Monks concerning the following question:

We run a number of applications in a mod_perl environment and seperate much of our business logic from display logic using CGI::Prototype and Template Toolkit. We'd like to cut down on the size of the memory footprint of some of our applications and one of the worst offenders is the building of large popup menus. Currently, we build the array and hash for the popup menus and pass them to the template which calls the CGI->popup_menu method for us.

What I'd like to do is take advantage of the convenience of the CGI module's helper methods like popup_menu, but I'd like to pass in a statement handle and have it iterate through the results to save on memory bloat. I have ideas of how I could do this myself -- sub-class CGI, and override the popup_menu method to check if $args->{-value}->isa('DBI::st') perhaps? -- but I'm curious if anyone knows of existing CPAN modules that would provide this functionality for us? My searches to this point have not turned up anything.

perl -e 'split//,q{john hurl, pest caretaker}and(map{print @_[$_]}(joi +n(q{},map{sprintf(qq{%010u},$_)}(2**2*307*4993,5*101*641*5261,7*59*79 +*36997,13*17*71*45131,3**2*67*89*167*181))=~/\d{2}/g));'

Replies are listed 'Best First'.
Re: Passing a sth to CGI form element methods
by holli (Abbot) on Apr 10, 2007 at 19:55 UTC
    1)
    Your plan won't help you. TT concatenates all the output of a script to a single string before spitting it out. At least it does so until you tell it to do otherwise.

    2)
    Why do you use CGI::Prototype just to build a popup menu? Why not just use TT's loops and perhaps the DBI-Plugin?


    holli, /regexed monk/
      Your plan won't help you. TT concatenates all the output of a script to a single string before spitting it out. At least it does so until you tell it to do otherwise.

      That hack might be useful, I'll definitely look into it. But won't avoiding populating those data structures in the first place potentially cut down on the size of the memory footprint at any given time? I'm still not certain that the returns will be worth the optimization, but it should be work something, shouldn't it?

      Why do you use CGI::Prototype just to build a popup menu? Why not just use TT's loops and perhaps the DBI-Plugin?

      Because we use CGI::Prototype for a whole lot more than loading popup menus :) otherwise your advice would be absolutely applicable. In fact, the generation of dropdowns is really view code, but it's complicated enough (lots of dropdowns loaded from the database) that a decision has been made to avoid doing too much of that type of heavy lifting in TT. It's not necessrily optimal, but that decision is mostly beyond my control.

      perl -e 'split//,q{john hurl, pest caretaker}and(map{print @_[$_]}(joi +n(q{},map{sprintf(qq{%010u},$_)}(2**2*307*4993,5*101*641*5261,7*59*79 +*36997,13*17*71*45131,3**2*67*89*167*181))=~/\d{2}/g));'
Re: Passing a sth to CGI form element methods
by ikegami (Patriarch) on Apr 10, 2007 at 19:26 UTC

    What's probably a better solution than adding support for database statement handles would be the more generic approach of adding support for iterators. Then, you'd only have to check if the value for values is a code ref instead of checking its class.

    The problem I see is that the popup is generated entirely in memory (and returned to be printed), so you're only going to save a fraction of the memory you could save.

      What's probably a better solution than adding support for database statement handles would be the more generic approach of adding support for iterators...

      I definitely like the idea of that, although I'm not likely going to write a CPAN module for this right now, so I don't think I would implement it with quite that much generality.

      The problem I see is that the popup is generated entirely in memory (and returned to be printed), so you're only going to save a fraction of the memory you could save.

      Actually, won't it save me about 2/3 of the memory? I'm passing in an arrayref and a hashref anyway and generating another data structure. If I never have to pass in the first two, won't that save me on the memory that would be used for their storage?

      perl -e 'split//,q{john hurl, pest caretaker}and(map{print @_[$_]}(joi +n(q{},map{sprintf(qq{%010u},$_)}(2**2*307*4993,5*101*641*5261,7*59*79 +*36997,13*17*71*45131,3**2*67*89*167*181))=~/\d{2}/g));'

        I definitely like the idea of that, although I'm not likely going to write a CPAN module for this right now, so I don't think I would implement it with quite that much generality.

        It's actually quite simple. Here's the difference:

        while (my $ar = $sth->fetch()) { my $item = $ar->[0]; ... }

        vs

        sub make_sth_iterator { my ($sth) = @_; return sub { my $ar = $sth->fetch(); return $ar ? $ar->[0] : (); }; } my $iter = make_sth_iter($sth); # Parens around $item required. Without, # them an $item with value "" or "0" # will exit the loop prematurely. while (my ($item) = $iter->()) { ... }

        Actually, won't it save me about 2/3 of the memory?

        Well, 2/3 is a fraction :) but I think it might be less than that. The HTML text is more verbose than the data, and you probably have two copies of the HTML text in memory during the concatenation, but it's true that Perl variables have a lot of overhead. I'm not sure why you'd eliminate a hash.

        Frankly, I'm concerned about your site's user experience. If the HTML is truly big enough to be worrisome on the server side, it's big enough to be worrisome on the client side.

Re: Passing a sth to CGI form element methods
by Rhandom (Curate) on Apr 10, 2007 at 21:33 UTC
    We'd like to cut down on the size of the memory footprint of some of our applications and one of the worst offenders is the building of large popup menus.

    ...but I'd like to pass in a statement handle and have it iterate through the results to save on memory bloat.
    I'm a bit curious. How many entries do you have in your pull down lists? How much memory are you considering memory bloat? Are you using CGI.pm before the call to popup_menu or is the memory bloat you are seeing include all of CGI.pm?

    If the problem is CGI.pm - well then don't use CGI.pm. If the problem is the size of you pulldown lists - then you may not be able to do anything other than taking the drastic step of leaving the pulldowns as place holders - splitting on the place holders -- streaming the chunks of html down to server and generating the popup lists on the fly when you hit a place holder. But that is very drastic and in 10 years of web application development I have never seen a need to go to that extreme.

    Personally I'd either generate and cache huge lists in external files so that I don't have to manipulate everything in memory all the time.

    Or I'd just use Template Toolkit similar to the following:
    my $template = ' <select name="foo"> [%- FOREACH row = sth.fetchrow_hashref %] <option value="[% row.id %]">[% row.text %]</option> [%- END %] </select> '; Template->process(\$template, {sth => $sth});


    my @a=qw(random brilliant braindead); print $a[rand(@a)];

      I'll take that as a vote for keeping it simple, and I think I agree. The popup_menu method doesn't buy us that much. The truth of the matter is that I don't expect this will buy us much. One of my colleagues is suggesting that we pass in statement handles instead of the data structures themselves and I wanted to see what reaction I got here. So far I'm getting the feeling that we should probably just leave well enough alone :)

      perl -e 'split//,q{john hurl, pest caretaker}and(map{print @_[$_]}(joi +n(q{},map{sprintf(qq{%010u},$_)}(2**2*307*4993,5*101*641*5261,7*59*79 +*36997,13*17*71*45131,3**2*67*89*167*181))=~/\d{2}/g));'