http://www.perlmonks.org?node_id=267972

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

I've been looking at using source filters as a way of optimizing run time code. The idea would be to remove debug and verbose calls in the code based on some criteria. I'm thinking along the lines of Carp::Assert but I don't want to add the if DEBUG after each line that could come out. Instead, I want to use source filters to remove the lines. My question is: Is there a way to do this in one place and have it affect all modules, without modify the Perl source code? Otherwise, I have to add use lines for any filter(s) I write to all existing modules.

Replies are listed 'Best First'.
Re: global source filters
by BrowserUk (Patriarch) on Jun 22, 2003 at 19:47 UTC

    You might be able to use the environment variable PERL5OPT. Set it to contain '-MYour::Filter', and this will be added to the command line of every perl script run.

    See the "Environment" section of perlrun.


    Examine what is said, not who speaks.
    "Efficiency is intelligent laziness." -David Dunham
    "When I'm working on a problem, I never think about beauty. I think only how to solve the problem. But when I have finished, if the solution is not beautiful, I know it is wrong." -Richard Buckminster Fuller


Re: global source filters
by adrianh (Chancellor) on Jun 22, 2003 at 20:00 UTC

    You might be interested in the compile time assertions that look like they will be coming in perl 5.8.1.

Re: global source filters
by steves (Curate) on Jun 22, 2003 at 19:56 UTC

    My understanding of -M is that it basically does a use in main. Source filters need to be use'd in every module to be filtered if I understand them correctly. So, given an existing library of a few hundred modules, I was hoping to be able to turn this on and off in one place without having to add a new use line to each of those modules.

      Note: I'm not advocating this a good solution to your problem. It is simply the only solution that I can think of to your problem as stated.

      The idea of using PERL5OPT & -MYour::Filter could (be made to?) work, given a little imagination and a lot of need.Your correct that -M effectively does a use in the main script, but the combination has several things going for it.

      • Environment vars a a convenient way of enabling/and disabling thing globally.
      • Modules loaded this way are loaded beforethe modules used within the script.
      • The module you are loading is a source filter.

      The combination of factors means that your source filter will have the opportunity to filter all the other use & require statements in the main module.

      You could therefore, intervene in those statements and apply your own logic to them. You might for example, create a temporary additional libpath to @INC and whenever you see a use or require statement, copy the referenced module from it's standard location to the appropriate place in your extra libtree, adding a use Your::Filter into the source of the module just after the package statement as you copied it. You then pass the use or require statement back to the compiler unchanged. It does it's usual thing of looking in @inc to locate and read the module, except it reads your modified copy from the temporary libtree you added to the front of @INC instead, and whizzo:). You now get to filter the module. Your filter then gets called recursively to filter any modules imported by that module ...

      Were I going to do this, I would code my asserts and debug code in the modules as comments rather than code using some notation on the front of the comment to indicate that this is a debug/test line. Eg.

      ##debug## assert( $thing_to_check ); ##debug## warn 'doobree=', $whatsit;

      and the filter, when enabled would look for these special comments and remove the comment card. This simplifies the filter in that you a) only need inspect comments and b) have a very definitive and easy edit to make. It also means that you only need the filter for testing and debugging, and you disable it in production which reduces the overhead for the production code not only by removing the debug and trace code, by also the filtering itself.

      That said, unless your test and debug code was already coded this way to start, you would need to edit the 100's of modules to add the comment cards, in which case you may consider it much simpler to write a script to iterate through your 100's of modules and insert the appropriate use Your::Filter after the package statement.

      In truth, I probably wouldn't consider doing any of this on my codebase, but then I can't see your codebase, and I don't see the true nature of the problem that you are trying to address. Suffice it to say. If you really need to do this, perl does provide all the tools you need to do it. They may not be as simply to apply as you would like, but there is rarely a time when you could say "It can't be done.".

      I thoroughly enjoyed thinking about how I might tackle the problem you posed.

      Should you choose to take the mission, I wish you the best of luck, but will deny any and all involvement should you get caught.

      This post will self-destruct 15 seconds after you take the idea seriously:)


      Examine what is said, not who speaks.
      "Efficiency is intelligent laziness." -David Dunham
      "When I'm working on a problem, I never think about beauty. I think only how to solve the problem. But when I have finished, if the solution is not beautiful, I know it is wrong." -Richard Buckminster Fuller


        Oh, I enjoy thinking about it too ... it's coding it that's a pain in the *ss. 8-)

        Along these lines though: I considered just writing a "compiler" that would batch do what you say: Filter out what I want into a separate production tree. Keep both trees and muck with @INC at start-up to point to one or the other. I don't currently have any special install type interface, so the work there is formalizing code installation. I should be doing that anyway, but of the 3 Perl virtues, I have an extra dose of Laziness.

Re: global source filters
by Beatnik (Parson) on Jun 22, 2003 at 20:52 UTC
    Using source filters to speed things up is not a good idea. Source filters are slow by definition. You CAN use it just fine for stuff you're doing... you could also just overload operators, use ties or use a plain debugger ;)

    Just my € 0.02

    Greetz
    Beatnik
    ... I'm belgian but I don't play one on TV.
      Recommending overload and tie in Perl for performance reasons is...a novel concept.

      I wondered about this. My theory was this: source filters would slow the compilation phase, but that should be offset by greater run time savings. The tools in question are mostly large batch data manipulation packages. Some read hundreds of thousands or millions of rows from databases and other sources and then feed them to a set of filters and outputs. We've build a nice framework around this. The tradeoff is that we can plug in a new data transform very fast, but run-time performance is sometimes poor. When I profile the code, there are several areas where I see potential savings -- nothing native to base Perl -- it's all layers we built on top to make coding easier/faster. Among those areas are interfaces for debug and verbose messages, and a few other assert type checkers.

      But here's the thing: When I profile Carp::Assert I seem to get mixed results. I see the assert calls disappear from the profile when I set PERL_NDEBUG but run-time is sometimes about even or a bit slower. But I'm not sure if this is due to using too small of a sample set. I've found that profiling the million row extracts takes too long so I usually start with 1000 or 10,000. I've considered what you said but the source for Carp::Assert looks like it's just replacing the functions with no-ops's -- not filtering. That should be relatively fast so I haven't figured this one out yet. But that leads to a meta-question: What is the best way to do this? Maybe filtering isn't the right way ... The basic problem is you have many existing lines of code with known patterns you'd like to remove from the code for performance. Also, unlike Carp::Assert some of these are class/object methods inherited from a base class. Not sure if that matters or not for a no-op replacement approach which I'm considering now ... The way Carp::Assert appears to do it looks like it uses contant functions which would then be inlined. Need to examine that code some more ...

      Update:Here's what Carp::Assert does. It's import method looks at the environment. If one of the NDEBUG indicators is set, it exports a DEBUG constant function that evaluates to zero and sets the debugging methods for the calling package to no-op's. If debugging is not disabled, it exports a DEBUG that evaluates to one and sets the debugging methods to the real thing. So no code is removed. Instead, it's relying on that DEBUG constant to stop any call or argument evaluation from being performed. That's why it relies on the if DEBUG: Just no-op'ing the methods would mean arguments would get evaluated.

Re: global source filters
by tilly (Archbishop) on Jun 23, 2003 at 07:04 UTC
    There is always another way to do it in Perl. One of the crazier ones is to put a function reference in @INC as Abigail does at Re: The Future - Managing Modules. The idea would be that when Perl goes to find your module, you would hand it a filehandle which is going to hand Perl filtered text. All of your modules would only be found this way, so they are all filtered with no per module change. (I have seen a tutorial on how to use this feature of @INC, but I can't find it right now.)

    In any case the direction that you are headed for sounds exactly like Aspect-Oriented Programming. If you google for that, you can find discussion of how the direction that you are thinking about has turned out for others in other languages. Of a sample set of one person whom I know who has tried it (I only read a couple of articles), all liked it. For more details, ask him.

    UPDATE: The Aspect-Oriented Programming that I was talking about and the AOP that mvc mentions below are the same thing. (I think I knew once that there was a Perl implementation...)

Re: global source filters
by mvc (Scribe) on Jun 23, 2003 at 07:16 UTC

    This type of cross-cutting concern is best handled with AOP using the amazing Aspect module.