Самый быстрый способ вызова подпрограммы

Насколько я знаю, в Perl мы можем вызвать подпрограмму из модуля, используя следующие методы:

* Экспортировать подпрограмму  `foo` , импортировать модуль, который имеет эту подпрограмму. Наконец, назовите его в своем скрипте perl.
* Создайте  `Object`  этого модуля в вашем perl-скрипте, наконец, вызовите  `foo` , используя этот  `Object` .
* Непосредственно вызовите  `foo` , используя его путь, как этот  `myDir :: Module :: foo ();` .

Если меня всегда путают, это лучший способ вызвать подпрограмму  `foo` .
Если у меня есть динамический скрипт, который я запускаю из браузера, а не из командной строки, какой подход следует использовать, чтобы сценарий занимал меньше времени.

Благодарю.

Существует разница между самым быстрым и лучшим способом вызова кода в Perl.

---

Редактировать: см. Также ответ simbabques. Особенно он учитывает различия между # 1 и №3 и почему вы также должны использовать их.

---

### # 1, # 3: вызовы функций

Ваши # 1 и # 3 идентичны: подпрограмма имеет уникальное имя в глобально видимом пространстве имен. Многие имена могут отображаться в одной подпрограмме через псевдонимы или импортировать модуль.

Если имя функции, которую вы вызываете, известно во время компиляции, суб будет разрешено во время компиляции. Это предполагает, что вы не спонтанно не переопределяете свои функции. Если точная функция известна только во время выполнения, это только хэш-поиск.

Существует три способа вызова функций:

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

```

Номер один (скобки иногда необязательные) является значением по умолчанию и проверяет ваши аргументы против подпротопа (не используйте прототипы). Также создается целый стек стека вызовов (с большой полезной информацией отладки). Это требует времени.

Номер два пропускает проверку protoype и предполагает, что вы знаете, что делаете. Это немного быстрее. Я думаю, что это неряшливый стиль.

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: вызов метода

Гибкость OO достигает высокой производительности: поскольку тип объекта, которому вы вызываете сообщение, никогда не известен до времени выполнения, фактический метод может быть разрешен только во время выполнения.

This means that `$foo->bar() looks up the function bar in the package that $foo` was `bless`ed 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.

Обычно вы не можете вызвать вызов метода для вызова функции, даже если вы знаете тип.

Если  `$ foo , если из класса  Foo , а  Foo :: bar  является sub, то  Foo :: bar ($ foo)`  пропустит результат метода и может даже работать. Однако это прерывает инкапсуляцию и будет ломаться один раз, когда  `Foo`  подклассы. Кроме того, это не работает, если  `Foo`  не определяет  `bar` , но метод был определен в родительском классе.

Я вообще поддерживаю объектную ориентацию, пока из тестов не станет ясно, что это не обеспечит требуемую производительность.



> * Экспортировать подпрограмму foo, импортировать модуль, который имеет эту подпрограмму. Наконец, назовите его в своем скрипте perl.
> 

Чтобы сделать это, вы должны использовать  [Экспортер](https://metacpan.org/module/Exporter)  в модуле/ `package` , который реализует подпункт. Вы сообщаете модулю, что он будет экспортировать через  `@EXPORT_OK`  и  `@EXPORT` . Если вы  `используете`  модуль, материал будет импортироваться в ваше текущее пространство имен во время компиляции. Следующие утверждения являются эквивалентными.

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

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

```

Вы хотите сделать это, если у вас есть материал, который вы собираетесь использовать в своем основном скрипте, или вы часто будете использовать его. Вот некоторые примеры:  [List :: Util](https://metacpan.org/module/List%3a%3aUtil) ,  [perlootut](https://metacpan. org/module/Data% 3a% 3aDumper «rel =« nofollow »> Data :: Dumper </a> или <code> использовать функцию« say »</code>. Конечно, вы также можете использовать его в других модулях.</p>

<pre><code>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);
</code></pre>

<p>Уловка в том, что здесь вы «загрязняете» свое пространство имен. Сделайте это, если нужно, но имейте в виду, что это происходит во время компиляции, поэтому оно <strong> не условно </strong>. Вы <strong> не можете сказать </strong>:</p>

<pre><code>if ($foo) {
  use Some::Module 'foo';
  foo($foo);
} else {
  use Something::Else 'bar';
  bar();
}
</code></pre>

<p>Он будет загружать как <code> Some :: Module </code> <strong>, так и </strong> <code> Something :: Else </code> во время компиляции, тем самым увеличивая время и память, которые потребляет ваша программа. Конечно, условие будет работать, но оно неэффективно.</p>

<blockquote><UL>
  <li> Создайте объект этого модуля в своем Perl-скрипте, наконец, вызовите foo с помощью этого объекта. </li>
  </UL></blockquote>

<p>Это подход OOp. Он (как упоминалось выше) не подходит для других методов. Вам не нужно импортировать методы объекта. Вы просто загружаете свой класс (который является модулем) либо с помощью <code> use </code>, либо <code> require </code> (см. Выше), создайте экземпляр и используйте его методы по своему усмотрению. Однако для этого вам нужен объектно-ориентированный модуль. Если вы заинтересованы в том, как это работает, начните с просмотра <a href=) .

> * Непосредственно вызовите foo, используя его путь, например myDir :: Module :: foo ();.
> 

На самом деле это не совсем его путь, а скорее его имя (пространство). Например, Data :: Dumper - это  `Dumper.pm` , расположенный в папке  `Data , где-то в вашем каталоге  `lib` . Но это не очень важно.`

`Основное отличие от первого подхода заключается в том, что вы исключаете часть импорта. Это полезно, если вы хотите создать что-то, что условно загружает определенные модули, или если вы находитесь в огромном (возможно, устаревшем) приложении и не хотите загрязнять пространство имен.

```
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);
}

```

Представьте, что этот фрагмент кода находится в унаследованном подразделении с линиями 2k, и много чего происходит, большинство из них никогда не вызывается в нашем случае. Мы не хотим  `использовать`  наш модуль, что делает его доступным для каждого из, возможно, 100 различных случаев, которые обрабатываются в этой огромной части кода. Вместо этого мы хотим только загрузить его, если нам это действительно нужно. Теперь мы  `требуем`  модуля и вызываем его sub напрямую, используя полное имя.

В завершении, как первый, так и третий способ имеют свои достоинства. Они оба должны существовать, и оба они должны использоваться, если это необходимо. В некоторых случаях это просто аромат, но в других имеет смысл принять решение. Второй подход OOp - это совсем другое.

Нет реальных различий в скорости, и, как сказал Бородин, Perl быстро развивается. Конечно, если вы не используете  `import` , вам не нужно «платить» за импорт. В сценарии с 10 строками это не имеет значения. В устаревшем программном обеспечении с потенциально тысячами строк кодов и множестве случаев использования в одном огромном файле это имеет большое значение.

Надеюсь, это поможет вам решить.`