Krishnachandra Sharma
Krishnachandra Sharma

Reputation: 1342

Fastest way of calling a subroutine

As far as I know, in Perl, we can call a subroutine from a Module by using these techniques:

If I am always confused which is better way of calling a subroutine foo. If I have a dynamic script, which I run from the browser and not command line, which approach one should go for so that the script takes less time.

Thanks.

Upvotes: 2

Views: 554

Answers (2)

amon
amon

Reputation: 57640

There is a difference between the fastest, and the best way to call code in Perl.


Edit: please see simbabques answer as well. He especially covers the differences between #1 and #3, and why you would use either.


#1, #3: Function calls

Your #1 and #3 are identical: The subroutine has an unique name in the globally visible namespace. Many names may map to one subroutine via aliases, or importing a module.

If the name of the function you are calling is known at compile time, the sub will be resolved at compile time. This assumes that you don't spontaneously redefine your functions. If the exact function is only known at runtime, this is only a hash lookup away.

There are three ways how functions can be called:

foo(@args);
&foo(@args);
@_ = @args; goto &foo;

Number one (braces sometimes optional) is default, and validates your arguments against the sub prototype (don't use prototypes). Also, a whole call stack frame (with much useful debug information) is constructed. This takes time.

Number two skips the protoype verification, and assumes that you know what you are doing. This is slightly faster. I think this is sloppy style.

Number three is a tail call. This returns from the current sub with the return value of foo. This is fast, as prototypes are ignored, and the current call stack frame can be reused. This isn't useful very often, and has ugly syntax. Inlining the code is about an order of magnitude faster (i.e. in Perl, we prefer loops over recursion ☹).

#2: Method calls

The flexibility of OO comes at a hefty performance price: As the type of the object you call the message on is never known until runtime, the actual method can only be resolved at runtime.

This means that $foo->bar() looks up the function bar in the package that $foo was blessed into. If it can't be found there, it will be searched for in parent classes. This is slow. If you want to use OO, pay attention to shallow hierarchies (→ less lookups). Do also note that Perls default Method Resolution Order is unusual.

You cannot generally reduce a method call to a function call, even if you know the type.

If $foo if of class Foo, and Foo::bar is a sub, then Foo::bar($foo) will skip the method resultution, and might even work. However, this breaks encapsulation, and will break once Foo is subclassed. Also, this doesn't work if Foo doesn't define bar, but the method was defined in a parent class.

I am generally in favour of object orientation, until it is clear from benchmarks that this will not provide the performance you require.

Upvotes: 6

simbabque
simbabque

Reputation: 54333

  • Export subroutine foo, import the module which has this subroutine. Finally call it in your perl script.

In order to do this, you would use Exporter in the module/package that implements the sub. You tell your module what it will export via @EXPORT_OK and @EXPORT. If you use the module, stuff gets imported into your current namespace at compile time. The following to statements are equivalent.

# This is the same...
use Module;
# ... as this

BEGIN {
  require Module;
  Module->import();
}

You want to do this if you have stuff you are going to use in your main script, or you are going to use often. Some examples are List::Util, Data::Dumper or use feature 'say'. Of course you can also use it in other modules.

use Data::Dumper;
use List::Util qw(max);
use feature qw(say);

my @foo = (1, 2, 3, 4, 5, 23);
print Dumper \@foo;
say max(@foo);

The catch is that here, you 'pollute' your namespace. Do this if you must, but keep in mind that it happens at compile time, so it is not conditional. You cannot say:

if ($foo) {
  use Some::Module 'foo';
  foo($foo);
} else {
  use Something::Else 'bar';
  bar();
}

It will load both Some::Module and Something::Else at compile time, thus increasing the time and memory your program consumes. The condition will work of course, but it is not efficient.

  • Create an Object of that Module in your perl script finally call foo using that Object.

This is the OOp approach. It is (as mentioned above) not compairable to the other methods. You don't need to import methods of an object. You just load your class (which is a module) either with use or require (see above), create an instance and use its methods to your liking. However, you need an object oriented module for that. If you are interestend in how that works, start by taking a look at perlootut.

  • Directly call foo using its path, like this myDir::Module::foo();.

It's actually not quite its path, but rather its name(space). For example, Data::Dumper is Dumper.pm located in the folder Data, somewhere in your lib dir. But that is not really important.

The main difference to the first approach is that you ommit the importing part. This is useful if you want to build something that conditionally loads certain modules, or if you are in a huge (maybe legacy) application and do not want to pollute the namespace.

if ($order_has_some_condition) {
  require Very::Long::NameSpace::For::This::Condition::Module;
  Very::Long::NameSpace::For::This::Condition::Module::do_stuff_with_an_order($order);
}

Imagine this piece of code is in a legacy sub with 2k lines and a lot of stuff going on, most of it is never called in our case. We do not want to use our module, making it available for each of the maybe 100 different cases that are handled in this huge piece of code. Instead, we want to only load it if we really need it. Now we require the module and call it's sub directly using the full name.

In conclusing, both the first and the third way have their merits. They both need to exist, and they should both be used if appropriate. In some cases, it is just flavor, but in others it makes sense to decide. The second, OOp, approach is something else entirely.

There are no real speed differences, and as Borodin said, Perl is fast. Of course, if you do not import stuff, you don't have to 'pay' for the import. In a 10-liner script, that doesn't matter. In legacy software with potentially thousands of lines of codes and many use cases in one huge file, it matters a lot.

I hope this helps you decide.

Upvotes: 4

Related Questions