Perl: Поделиться реализацией подпрограммы между различными пакетами? - PullRequest
2 голосов
/ 25 октября 2011

Какой-то странный, непрактичный вопрос, возникающий здесь.Вы знаете, что в Perl вы можете хранить подпрограмму в переменной, как в Javascript и других языках.Подпрограмма занимает немного памяти.

Теперь вы можете подумать, что это сэкономит память, чтобы использовать эту одну ссылку на подпрограмму, чтобы обеспечить реализацию множества идентичных подпрограмм в различных пакетах.Как и все эти сеттеры и геттеры.(Да, я должен использовать один из существующих mooses или mouse или еще много чего, но это непрактичный вопрос.) Или, как и другие подпрограммы, которые вы, возможно, захотите использовать в различных пакетах, чтобы все эти пакеты могли выполнять несколько сквозных операций.wtf.

Вы можете иметь делегата в каждом классе, чтобы сделать работу.Но, может быть, это слишком тяжело и слишком ОО.Вы хотите подпрограмму.Может быть, это называется mix-in?В любом случае, вы хотите установить одну подпрограмму в каждом пакете, думая, что это экономит память, и вы можете протестировать ее в одну строку, например:

perl -lwe "$s=sub { print 33 };
  for (1 .. 20000) { $p=qq(Bla${_}::bla); *$p = $s} Bla987::bla(); <>"

(Хорошо, она прошла две строки, чтобы вместить ширину страницыи обратите внимание, что в Windows требуются двойные кавычки, а в Linux вы должны использовать одинарные кавычки, чтобы избежать того, что оболочка пожирает ваши деньги.)

Теперь, что это за забавный кусок кода (сомнительного качества, простов демонстрационных целях) создает 20 000 пакетов и устанавливает эту подпрограмму в каждом пакете (в соответствующей записи таблицы символов).

В {{taskmgr}} можно увидеть, как увеличивается и уменьшается потребление памяти в соответствии с числомпакетов, которые я создаю (12,2 КБ для 20 000 пакетов, 6,7 для 10 000 пакетов, 1,0 КБ для 100 пакетов - совсем немного), но я полагаю, что это не из-за копирования подпрограммы, а скорее из-за дополнительных пакетовсоздается.

Это правильно?Подпрограмма не скопирована, на нее есть только ссылки?

Что хорошего в этой теме?Или это углубляется в детали реализации и материал perl5porters?

Или весь этот ход мыслей просто вводит в заблуждение размышления и удивления?

Обновление

Хорошо, я не уверен, что {{sub}} в {{$ s}} повторно используется - потому что когда я создаю новые подпрограммы в цикле, использование памяти не увеличивается:

perl -lwe "for (1 .. 20000) {
  $p=qq(Bla${_}::bla); *$p = sub {print 33} } Bla987::bla(); <>"

Но возможноэто потому, что подпрограмма постоянна.

Использование памяти действительно возрастет (до 10 КБ для 10 000 пакетов и 20 КБ для 20 000), если я добавлю вариант элемента в подпрограмму, например:

perl -lwe "for my $i (1 .. 10000) {
  $p=qq(Bla${i}::bla); *$p = sub {print $i} } Bla987::bla(); <>"

1 Ответ

5 голосов
/ 25 октября 2011

Perl скомпилирует подпрограмму только один раз.Вы передаете ссылку на него, которая будет той же самой, если вы проверите адреса.

Утечка памяти происходит из-за создания 20 000 пакетов, которые представляют собой 20 000 ссылок на хэш, каждый с некоторой магией, которая требует немного большеобъем памяти.Каждая из этих ссылок на хеш содержит копию строки с именем подпрограммы.И ссылка на каждый из этих хешей помещается в таблицу символов main::.

Если вы хотите, чтобы подпрограмма находилась в области видимости во всех пакетах, используйте полное имя, поместите подпрограмму в лексическийохватывает все пакеты или помещает подпрограмму в символьную переменную:

 sub some::name {}  # call as `some::name()` from anywhere

 my $x = sub {};    # call as `$x->()` anywhere $x is in scope

 *!    = sub {};    # call as `&!()` from anywhere

Любое из приведенных выше решений предоставит вам доступ из других пакетов, не тратя много памяти.

Заобновление, вы видите увеличение размера с замыканиями, потому что, хотя каждый подрефкат одинаков, у каждого должна быть новая лексическая панель, созданная для хранения переменной замыкания, которая будет уникальной для каждого замыкания.

Вы можететакже сделайте методы доступными повсеместно, используя пакет UNIVERSAL, но это может быть проблематично, поэтому возьмите следующее с небольшим количеством соли:

sub UNIVERSAL::foo {say "foo(@_)"}

xyz->foo;  #  foo(xyz)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...