Очевидные вещи
Ищите лучшие алгоритмы
Допустим, код выполняется с максимальной эффективность с Вашей точки зрения. Но, может быть, кто-то подошел к данной проблеме с совершенно другой стороны и нашел алгоритм, работающий в сотни раз быстрее. Вы уверены, что используете лучший алгоритм? Воспользуйтесь поисковиком, возможно, вам удастся найти лучшее решение.
mod_perl
Использование mod_perl может ускорить выполнение скрипта в 10 раз. Если Ваш скрипт по каким-либо причинам не работает под ним, то существует fastcgi. Или, возможно, Вам подойдет модуль PPerl, позволяющий превратить скрипт в практически полноценный “демон”.
Используйте сильные стороны языка
Хэши
Использование хэшей зачастую более эффективное решение, чем использование простых массивов.
Регулярные выражения
Переписывание и оптимизация регулярных выражений могут дать значительный прирост быстродействия.
Pack и unpack
Pack и unpack обладают широкими возможностями. Иногда удается заменить здоровую функцию одним анпаком.
Чем может быть вызвана медленная работа?
Процессор
Очевидно, что если скрипт потребляет слишком много процессорного времени, то для более быстрой работы следует постараться его сократить.
Оперативная память
В меньшей степени на скорость выполнения влияет потребление оперативной памяти. Во время работы перл “выбирает” алгоритмы, потребляющие больше памяти для ускорения выполнения. Со временем процессоры становятся быстрее, оперативная память тоже. Однако, в сравнении с процессорами, оперативная память прогрессирует медленнее, таким образом ,со временем придется смещаться в сторону потребления процессорного времени, имейте это в виду.
Организация памяти напоминает пирамиду. Памяти всегда не хватает и она всегда работает недостаточно быстро. Организация памяти в компьютере выглядит следующим образом: на верхушке находится процессор и его регистры, которые очень быстро работают, однако очень малы; далее идут несколько уровней кэша, который больше по объему и довольно быстро работает; потом идет оперативная память, которой довольно много, но скорость работы значительно меньше; и, наконец, жесткий диск, который используется для кеширования содержимого оперативной памяти и обладает большим объемом, но работает очень медленно. Пытайтесь оценить “узкие” места в Вашем коде, используя это представление.
Аналитика
Как заставить скрипт работать быстрее? Четкой методики нет, однако можно выделить 4 ключевых момента.
Что может “тормозить”?
Ищите вещи, которые действительно медленно работают. Не стоит тратить много усилий на попытки ускорить места, которые и так довольно быстрые.
Задумайтесь о переписывании
Полное переписывание скрипта может дать хорошие результаты, особенно если текущий скрипт включает в себя кучу “доработок” на скорую руку, ухудшающих качество кода.
Записывайте результаты
Комментирование кода – полезная вещь. Если оптимизаторская идея не дала желаемого результата – напишите об этом, это избавит других от повторения той же ошибки. Если оптимизация удалась, то об этом тоже стоит написать, это избавит других (возможно, и Вас через месяц) от переписывания оптимизированного куска и потери достигнутой производительности.
Импортирование
Большинство модулей экспортируют кучу модулей и переменных в Ваше пространство имен. Если Вы пишите:
1 |
use POSIX; |
то POSIX экспортирует все переменные и функции, экспортируемые им по-умолчанию. Если Вы пишите:
1 |
use POSIX (); |
то ничего не будет экспортировано. Вы по-прежнему можете использовать функции и переменные из модуля POSIX, для этого надо использовать полные имена, добавляя POSIX:: перед обращением к ним.
Использование данного приема позволяет значительно сократить время запуска скрипта.
Регулярные выражения
Избегайте использования $&
Переменная $& возвращает последнее удачное совпадение в любом регулярном выражении. Однако она не ограничена лексической зоной видимости, т.е. она не очищается после покидания блока видимости. Однако, если Вы её не используете, то Perl это видит и не хранит её на протяжении работы скрипта.
Избегайте использования бесполезного захвата
Если вы используете круглые скобки для группировки, то не стоит писать (…), так как в таком случае Perl копирует найденное значение в переменную $1, вместо этого лучше использовать (?:…), тогда в переменную $1 ничего не будет занесено.
/…/o
При использовании переменных в регулярном выражении, Perl каждый раз проверяет и “компилирует” выражение, однако, если содержимое переменных не меняется на протяжении использования выражения, то стоит воспользоваться модификатором /o, который укажет перлу, что “рекомпиляция” и проверка выражения при повторном использовании не требуется.
Benchmark
Используйте модуль Benchmark для оценки скорости выполнения кода. Его очень легко использовать:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
use Benchmark ':all'; sub one { my $str = 'test' x 1000; for(my $i = 0; $i < length $str; $i++) {} } sub two { my $str = 'test' x 1000; for(my ($i, $j) = (0, length $str); $i < $j; $i++) {} } cmpthese(10000, {one => \&one, two => \&two}); |
После выполнения, Вы получите сравнительную таблицу, которая поможет оценить быстродействие.
Rate one two
one 1689/s -- -22%
two 2162/s 28% --
Чем может быть вызвана медленная работа Perl?
Что же в действительности замедляет работу? Когда Perl выполняет скрипт, он разбивает выполнение на последовательность операций (ОП’ов). Т.е. когда выполняется код $a = $b + $c, то в действительности он разбивается на следующие оп’ы:
- Поместить $b в стек
- Поместить $c в стек
- Сложить значения $a и $b и поместить результат в стек
- Получить адрес $a
- Поместить содержимое верхушки стека по этому адресу
Компьютеры довольно быстро выполняют арифметические операции, однако, довольно большие издержки вызывают такие моменты, как “какая операция выполняется в данный момент” и “где находится следующая операция”. Таким образом, количество операций зачастую является более важным моментом, чем используемое процессорное время и оперативная память.
Как оценить количество операций? Очень просто:
perl -MO=Terse -e "print 1+1"
На выходе получим:
LISTOP (0x193b290) leave [1]
OP (0x193b274) enter
COP (0x193b2b4) nextstate
LISTOP (0x193b30c) print
OP (0x193b2f0) pushmark
SVOP (0x193b330) const [2] IV (0x237060) 2
Таким образом, есть возможность оценить, какой алгоритм является менее “затратным”.
Операторы транслитерации
y и tr - это операторы транслитерации. Они не настолько мощные, как базовые регулряные выражения, однако, в некоторых случаях они работают быстрее.
tr/символ//
Это наиболее быстрый способ посчитать, сколько раз встречается определенный символ в строке.
tr/a/A/ быстрее чем s/a/A/g
tr выполняет замену быстрее, чем стандартные регулярные выражения.
tr/0-9//d быстрее чем s/[0-9]//g
Также tr быстрее в плане удаления диапазона символов из строки, однако, для удаления одиночных символов s///g быстрее. Всегда тестируйте производительность, прежде чем остановиться на одном из решений.
最適化-完成への道
>Допустим, код выполняется с максимальной эффективность с Вашей точки зрения. Но, может быть, кто-то подошел к данной проблемы с совершенно другой стороны
внимательно перечитай. мне кажется, что нужно больше спать)
Kaimi, будь добр, объясни, что обозначает конструкция:
$| = 1;
? :)
Это отключение буферизации вывода, т.е. буфер очищается после каждой команды print (printf, write)
Про оптимизацию очень хорошо написано в книге Ларри Уолла "Программирование на Perl"
У меня никак не получается работать с mod_perl :(
какие книжки посоветуете для начала почитать?
Я особо не интересовался perl'ом в разрезе создания веб-сайтов и подобного. А так, видимо, Practical mod_perl, правда не знаю есть ли на русском.
и еще не нашел где можно подписаться на ответы в комментариях?
Уведомления о ответах вроде бы должны отправляться на email, есть rss-лента всех комментариев: http://kaimi.io/comments/feed/