Beefy Boxes and Bandwidth Generously Provided by pair Networks
Problems? Is your data what you think it is?
 
PerlMonks  

use vs. require in string eval?

by lachoy (Parson)
on Mar 11, 2004 at 21:50 UTC ( #335990=perlquestion: print w/replies, xml ) Need Help??

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

andreychek recently emailed me with some very interesting profiling results from Class::Factory. It's a simple module that doesn't do very much, but one of the things it does do is bring in classes dynamically. So you can do something like:

$my_factory->register_factory_type( my_type => 'My::Impl::Class' );

And 'My::Impl::Class' doesn't actually get brought in until it's used for the first time. Easy enough.

The problem Eric found is in this statement that brings in the module:

eval "require $object_class";

Simple enough, right? Apparently not. According to the profiling he did that procedure took 4.5% of his program's execution time. Unacceptable for something so simple.

Here's the interesting part: when he changed it to:

eval "use $object_class";

the time took by the procedure dropped tenfold, to 0.44%.

I have no idea why this would happen. I was always under the impression that 'require' actually did less than 'use' since it doesn't invoke 'import()' automatically. But then it takes less time to work. So what's the scoop?

NB: I haven't looked at the code Eric is profiling, and it might be something as simple as the filesystem caching the modules so on successive runs they take less time to load. But then again...

Chris
M-x auto-bs-mode

Replies are listed 'Best First'.
Re: use vs. require in string eval?
by perrin (Chancellor) on Mar 11, 2004 at 22:17 UTC
    His test is broken. Try it yourself:

    #!/usr/bin/perl -w use Benchmark qw/ cmpthese /; my $count = shift; our $module = 'CGI ()'; cmpthese( $count, { require => sub { eval "require $module"; }, use => sub { eval "use $module"; }, } );
    It's always faster to use "require", even if you explicitly import nothing, as I've done here.
      There is practically no difference between the two. Your benchmark is flawed, here is a correction (only the addition of delete $INC{'CGI.pm'}; is required):
      use Benchmark 'cmpthese'; use CGI(); undef %CGI::; delete $INC{'CGI.pm'}; cmpthese( shift, { use => sub { eval "use CGI();"; undef %CGI::; delete $INC{'CGI.pm'}; return (); }, require => sub { eval "require CGI;"; undef %CGI::; delete $INC{'CGI.pm'}; return (); }, }); __END__ $$ perl eval.use.v.eval.require.pl -3 Benchmark: running require, use, each for at least 3 CPU seconds... require: 3 wallclock secs ( 3.09 usr + 0.13 sys = 3.22 CPU) @ 37 +.28/s (n=120) use: 3 wallclock secs ( 3.06 usr + 0.17 sys = 3.23 CPU) @ 37 +.11/s (n=120) Rate use require use 37.1/s -- -0% require 37.3/s 0% -- $$ perl eval.use.v.eval.require.pl -5 Benchmark: running require, use, each for at least 5 CPU seconds... require: 5 wallclock secs ( 5.14 usr + 0.14 sys = 5.28 CPU) @ 37 +.31/s (n=197) use: 5 wallclock secs ( 5.05 usr + 0.25 sys = 5.30 CPU) @ 37 +.19/s (n=197) Rate use require use 37.2/s -- -0% require 37.3/s 0% -- $$ perl eval.use.v.eval.require.pl 50 Benchmark: timing 50 iterations of require, use... require: 1 wallclock secs ( 1.30 usr + 0.03 sys = 1.33 CPU) @ 37 +.65/s (n=50) use: 1 wallclock secs ( 1.31 usr + 0.03 sys = 1.34 CPU) @ 37 +.23/s (n=50) Rate use require use 37.2/s -- -1% require 37.7/s 1% -- $$ perl eval.use.v.eval.require.pl 100 Benchmark: timing 100 iterations of require, use... require: 3 wallclock secs ( 2.61 usr + 0.06 sys = 2.67 CPU) @ 37 +.41/s (n=100) use: 2 wallclock secs ( 2.63 usr + 0.05 sys = 2.67 CPU) @ 37 +.43/s (n=100) Rate require use require 37.4/s -- -0% use 37.4/s 0% -- $$

      MJD says "you can't just make shit up and expect the computer to know what you mean, retardo!"
      I run a Win32 PPM repository for perl 5.6.x and 5.8.x -- I take requests (README).
      ** The third rule of perl club is a statement of fact: pod is sexy.

        I was under the impression that the module being discussed was going to run the eval "require $foo" statement many times, even for modules it had already loaded. Looking at the code, it actually only does it once when you add a new type, so it is acting more like your benchmark than mine.

        I hear what you're saying, but I'm having trouble arguing with the results I've seen :-)

        Indeed, my test case was trivial -- it launched, performed the application initialization, did some trivial tasks, and quit.

        I was testing this with OpenPlugin, which internally makes use of Class::Factory. So, when OpenPlugin starts up, it calls Class::Factory's add_factory_type for each plugin (in my test case, that included 7 plugins). Each plugin then loads it's driver, and each driver in turn loads it's dependencies. So off the top of my head, let's say add_factory_type ends up loading roughly 20 modules.

        And this is where I'm saying I saw the performance difference. I did not benchmark this per se, I profiled it. Using Devel::Profile, I realized that my app was spending quite a bit of time in add_factory_type, where it would be loading all these modules.

        I ran that several times, and found the results to be consistent -- each call to add_factory_method was taking about 0.010652 seconds per call. Changing the code within add_factory_type to use "use" instead of "require" lowered the execution time of that method to about 0.000904 seconds per call, and lowered the overall execution time of the application.

        If I change it back to "require", the execution time for the entire app goes back up.

        Could there be something with OpenPlugin, perhaps the amount of modules it ends up loading, or maybe even something in one of the modules that it's loading, that has it showing different results than what one might expect?

        Thanks for your thoughts. Have a good one,

        -Eric


        --
        Lucy: "What happens if you practice the piano for 20 years and then end up not being rich and famous?"
        Schroeder: "The joy is in the playing."
      Hello,

      While I agree that require's are probably a tad faster than uses's,
      I am not sure that trying to benchmark these like this is really a valid test.
      Is'nt it true that once you require(or use) a module, perl knows that you have done this,
      and shorts out any further calls for that same module? Hence you can have multiple requires
      peppered throughout your App, and it does not make your performace suffer,
      even if the statement is hit multiple times.

      Best Regards,
      Wonko

        First of all, there is no way this could be taking significant amounts of time unless the app is doing basically nothing. However, it is not correct to say that perl "shorts out any further calls for that same module." It at least involves perl running some code that will check %INC for the module in question, and with "use" it would also involve running an import(), although a use statement typically only happens once because it is a virtual BEGIN block.

        So, what I'm saying is that require and use definitely do take some time inside of a string eval, even if you have already run them before, and there is a difference in the amount of time they take.

Re: use vs. require in string eval?
by Wonko the sane (Deacon) on Mar 11, 2004 at 22:15 UTC
    Hello,

    I think something else is going on there, if you are getting benchmarks that are that different,
    with these two statements. Require and use should take up about the same amount of time,
    and they do "essentially" the same thing. The main difference being of course that require's are done at runtime,
    while use's are done at compile time.
    Unless of course you have them wrapped in an eval, which would cause them both to be done at runtime.

    Wonko

Re: use vs. require in string eval?
by leriksen (Curate) on Mar 12, 2004 at 03:25 UTC
    Isn't the whole point that use happens at compile time and require happens at run time. You can only benchmark run time behaviour...

    So your eval "use $object_class"; is effectively trying to benchmark nothing except the eval overhead itself - which in this case is efectively nothing.

    use is a declaration - require is a function - confusingly both are in the functions listed in chapter 3 of the camel book.

    I would thus suspect that eval "use $object_class"; is actually wrong.

    +++++++++++++++++
    #!/usr/bin/perl
    use warnings;use strict;use brain;

      It doesn't matter if "use" is a declaration, eval happens at runtime, and things that happen inside of string eval happen when eval happens.

      MJD says "you can't just make shit up and expect the computer to know what you mean, retardo!"
      I run a Win32 PPM repository for perl 5.6.x and 5.8.x -- I take requests (README).
      ** The third rule of perl club is a statement of fact: pod is sexy.

      I actually suspected this as well, but all the tests for Class::Factory still ran okay. If the 'eval "use..."' worked out to a no-op they would have failed all over the place.

      Chris
      M-x auto-bs-mode

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://335990]
Approved by gmax
Front-paged by broquaint
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others perusing the Monastery: (1)
As of 2022-05-22 15:04 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    Do you prefer to work remotely?



    Results (80 votes). Check out past polls.

    Notices?