Как я могу выполнять неэффективный код только во время компиляции при использовании mod perl?
я сравнивал производительность фреймворка, который я пишу в Perl, и я получаю 50% снижение запросов в секунду по сравнению с нашей существующей кодовой базой (некоторый хит понятен, потому что мы переходим от процедурного кода спагетти к фреймворку OOP MVC).
приложение работает под mod_perl, и я добавил Лось и весь мой код фреймворка в startup.pl скрипт, что само по себе удвоило мои запросы в секунду. Я глядя на дальнейшее увеличение этого числа, чтобы получить его как можно ближе к существующей сумме. Существует аргумент, что это преждевременная оптимизация, но есть несколько вопиющих неэффективностей, которые я хотел бы исправить и посмотреть, как это влияет на производительность.
как и большинство фреймворков, у меня есть файл конфигурации и диспетчер. Часть конфигурации обрабатывается Config:: General, так что немного ввода-вывода и синтаксического анализа участвует, чтобы получить мой файл конфигурации загружен в приложение. Этот самая большая проблема, которую я вижу здесь, заключается в том, что я делаю это для каждого запроса, который приходит!
запуск Devel::Dprof в моем приложении указывает на Config::General:: BEGIN и кучу связанных модулей ввода-вывода Как одну из основных медленных точек, которая не является лосем. Итак, что я хотел бы сделать, и что имеет гораздо больше смысла в ретроспективе, это воспользоваться настойчивостью mod_perl и startup.pl компиляция материала, чтобы выполнить работу только для загрузки в конфигурационный файл один раз-при запуске сервера.
В проблема в том, что я не слишком хорошо знаю, как это будет работать.
В настоящее время каждый проект имеет класс начальной загрузки PerlHandler, который довольно тощий и выглядит так:
use MyApp;
MyApp->new(config_file => '/path/to/site.config')->run();
MyApp.pm наследует от модуля framework Project, который имеет этот код:
my $config = Config::General->new(
-ConfigFile => $self->config_file,
-InterPolateVars => 1,
);
$self->config({$config->getall});
чтобы сделать это только во время компиляции, оба моих модуля bootstrap и Project base должны будут измениться( я думаю), но я довольно не уверен, какие изменения сделать и по-прежнему держать код хорошим и худой. Кто-нибудь может указать мне правильное направление?
обновление
я попробовал начать блок в каждом подходе модуля проекта, как описано ysth в его ответе. Так что теперь у меня есть:
package MyApp::bootstrap;
use MyApp;
my $config;
BEGIN
{
$config = {Config::General->new(...)->getall};
}
sub handler { ..etc.
MyApp->new(config => $config)->run();
это быстрое изменение само по себе дало мне 50% увеличение запросов в секунду, подтверждая мои мысли о том, что файл конфигурации был основным узким местом, которое стоит исправить. Контрольная цифра на нашей капризной старой машине dev-60rps, и мой рамки с 30rps в 45rps только это изменение. Для тех, кто говорит, что лось медленный и имеет время компиляции хит.. Я получил такое же (50%) увеличение при компиляции всего моего кода лося при запуске, как и при предварительной компиляции файла конфигурации.
единственная проблема, которую я сейчас имею, это то, что это нарушает сухой Принципал, так как тот же Config::General->новый код находится в каждом блоке BEGIN, только путь к файлу конфигурации отличается. У меня есть несколько различных стратегий, чтобы ограничить это, но я просто хотел опубликовать результаты этого изменения.
6 ответов
предполагая, что ваши приложения вообще не изменяют конфигурацию, переместите ее в блок begin:
# this code goes at file scope
my $config;
BEGIN {
$config = { Config::General->new( ... )->getall }
}
# when creating a new instance
$self->config( $config );
и убедитесь, что все ваши модули скомпилированы в startup.pl.
вы можете получить fancier и иметь одноэлементный класс, предоставляющий хэш конфигурации, но тебе и не нужно.
Если вы можете сделать ваши занятия Муз неизменяемые, это может дать вам еще один удар.
модуля импорт sub выполняется во время компиляции, поэтому мы могли бы использовать это для уменьшения / устранения сухого ysth-х.
в следующем примере мы используем метод import для чтения файла конфигурации с приведенными аргументами, а затем помещаем эту конфигурацию в вызывающий пакет.
оговоркой, либо $config
переменная в вызывающем пакете будет уничтожена этим.
package Foo_Config;
use English qw(-no_match_vars);
sub import {
my ($self, @cfg) = @ARG;
my $call_pkg = caller;
my $config = {Config::General->new(@cfg)->getall};
do{ # this will create the $config variable in the calling package.
no strict 'refs';
${$call_pkg . '::config'} = $config;
};
return;
}
package MyApp;
# will execute Foo_Config->import('/path/to/site.config') at compile time.
use Foo_Config '/path/to/site.config';
у меня были те же проблемы в HTML::Mason framework install, и я обнаружил, что это работает довольно хорошо: В httpd.conf:
PerlRequire handler.pl
<FilesMatch "\.mhtml$">
SetHandler perl-script
PerlHandler YourModule::Mason
</FilesMatch>
в вашем handler.pl файл, вы определяете все ваши статические элементы, такие как ваша конфигурация, дескрипторы базы данных и т. д. Это определяет их в области YourModule:: Mason, которая компилируется при запуске потока apache (новые потоки, очевидно, будут иметь неотъемлемые накладные расходы). YourModule:: Mason тогда имеет handler
метод, который обрабатывает запрос.
Я признаю, что в HTML::Mason может быть какая-то магия, которая помогает мне в этом, но она работает для меня, может быть, для вас?
обычный способ ускорения таких вещей с несколькими изменениями-просто использовать глобальные переменные и состояние кэша в них между вызовами одного и того же процесса Apache:
use vars qw ($config);
# ...
$config = Config::General->new( ... )->getall
unless blessed($config); # add more suitable test here
Это не очень чисто и может привести к неясным ошибкам (хотя "мой $var" приводит к большему в моем опыте), и это иногда съедает много памяти, но многих (повторных) дорогостоящих операторов инициализации можно избежать таким образом. Преимущество перед использованием только кода BEGIN {}; заключается в том, что вы можете повторно инициализировать на основе других событий, а также без необходимости перезагрузки apache или убийства процесса (например, путем включения метки времени файла на диске в тесте выше).
следите за gotchas, хотя:простой способ взломать
JackM имеет право идея.
загрузив все ваши классы и создав экземпляр объектов уровня приложения (в вашем случае, конфигурации) в "мать" процесс Apache, вам не нужно компилировать их каждый раз, когда появляется новый рабочий, так как они уже доступны и в памяти. Очень дотошные среди нас добавляют линию "использования" для каждого модуля, который их приложение использует регулярно. Если вы не загружаете свои пакеты и модули на материнский корабль, каждый работник принимает не только удар производительности загрузки модулей, но и не получает преимущества совместного использования памяти, которые обеспечивают современные операционные системы.
Это действительно другая половина разницы между mod_perl и CGI. С первой половиной, являющейся постоянным perl-движком mod_perl против CGI для каждого вызова.