in reply to How to use modules dynamically?

This piece of code is stolen (and paraphrased) from DB2::db:

# [...] Rather than using # eval STR to eval "require $[...]pm", we do it ourselves. Th +is # is slightly faster (Benchmark shows about 20% faster). (my $pm = $type) =~ s.::./.g; $pm .= '.pm'; eval { require $pm };
In short - I hate eval STR. Anything to avoid it. If I can't avoid it, I'll abuse the heck out of it, but when I can avoid it, I do.

When I noticed Corion talk about UNIVERSAL::require, I was tempted to send in a patch similar to the above. I changed my mind solely because I decided against using that module. Neat idea, but my gut tells me it's just not clean (polluting UNIVERSAL, that is, not having a user-friendly, aka string-friendly, require).

That, and I haven't really gone through the effort of rigorously testing it. :-)

Replies are listed 'Best First'.
Re^2: How to use modules dynamically?
by gaal (Parson) on Feb 25, 2005 at 07:52 UTC
    The source is short and clear enough if you want to review it. You'll notice it too basically boils down to

        my $return = eval "CORE::require $module";

Re^2: How to use modules dynamically?
by fireartist (Chaplain) on Feb 25, 2005 at 11:15 UTC

    That's interesting, but can you show your benchmark code?

    Are you cleaning up %INC for each Benchmark iteration, so that the module is actually being loaded again?

      Not really hard to replicate. First off, in the current directory, I created both a Z.pm, and a Z directory with X.pm - both Z.pm and X.pm had a single line: "1;". This is to eliminate, as much as possible, any constant overhead.

      Benchmark code:

      And the results:
      Rate evalSTR2 evalSTR1 req2 req1 evalSTR2 2474/s -- -2% -40% -48% evalSTR1 2538/s 3% -- -39% -47% req2 4150/s 68% 64% -- -13% req1 4779/s 93% 88% 15% --
      run #2:
      Rate evalSTR1 evalSTR2 req2 req1 evalSTR1 2348/s -- -4% -41% -44% evalSTR2 2438/s 4% -- -39% -41% req2 3964/s 69% 63% -- -5% req1 4158/s 77% 71% 5% --
      Seems like more than 20% now. That said, if you find any faults in my benchmark, please, by all means, correct me.

      As an interesting side-note, if it's possible that you're repeating a module-load (use/require), here's a benchmark showing that (all I did to the above code is remove the delete's):

      Rate evalSTR2 evalSTR1 req2 req1 evalSTR2 14504/s -- -0% -94% -97% evalSTR1 14516/s 0% -- -94% -97% req2 262449/s 1709% 1708% -- -38% req1 424285/s 2825% 2823% 62% --
      In the case of a first load, my require is faster, and in the case of a second or later reload attempt, my require is almost free, whereas recompiling your eval string is quite expensive, relatively speaking.

      The big question is ... is it worth the difference? If I were putting this in time-critical (or possibly time-critical) code, probably. I don't think it's inherently obfu, so it's still pretty clean code. If I were putting this in code that can take its time, I might not waste the effort. Up to you to decide!