понедельник, 28 декабря 2009 г.

Проблемы логирования отладочной информации


Как известно вывод отладочной информации значительно замедляет выполнение программы, особенно если Вам приходиться дампить сложные структуры данных. После завершения этапа тестирования отключают вывод лишней информации, но все же вызовы функций остаются, и они съедают системные ресурсы. Особенно необходимо избегать такого - logger("Message1", Message2", Dumper($mega_structure_ref); Дампер будет вызываться всегда для формирования списка аргументов.
Есть разные варианты решения данной проблемы: использовать Smart::Comments, вставлять условия( logger("debug info") if DEBUG_ON #DEBUG_ON - константа) - эти варианты я сейчас не рассматриваю.
Я рассмотрел 2 варианта:
Вариант 1 (передача ссылки на функцию) 
sub logger(&){}
logger{"Message1", Message2", Dumper($mega_structure_ref)}; 
Таким образом дампер вызовется только во время формирования аргументов для переданной анонимной функции, то есть когда мы точно решим, что нам это необходимо.

Вариант2 (Определяю внутри функции, что мне передали ссылку и дамплю ее):
logger("Message1", Message2", $mega_structure_ref);

Выводы
Преимущества 1-го варианта:
  1. Гибкость.  Он гибче чем 2-й вариант, поскольку позволяет вычислять аргументы, только если мы действительно хотим что-то вывести в лог. А в аргументах могут быть любые операции.
Преимуществ 2-го варианта:
  1. Скорость.  Он быстрее в 6-7 раз по сравнению с 1-м, хотя при логировании константой строки всего в 1.5 раза. Тест на скорость приатачен.
  2. Удобство. Он удобнее, нет необходимости подключать Data::Dumper и прописывать постоянно Dumper(...).
  3. Опциональный дампинг. Позволяет выборочно дампить ссылки. Если мы не хотим дампить структуру, то мы можем заключить ее в двойные кавычки.

Для меня 2-вариант безоговорочно лучший. Он проигрывает только тогда когда нам необходимо в аргументах передать данные, получение которых требуют серьезных затрат ресурсов ( logger("Users online:" . calc_statistics(get_data_from_db)) ) .  Но для решения данной проблемы мы можем попросту разрешить логеру принимать ссылки на функции как один из аргументов  - logger("Users online:" , sub{ calc_statistics(get_data_from_db) } ). Это не так удобно, ква вариант 1, но поскольку такие ситуации возникают очень редко - то это для меня приемлим.

Приложение
Результаты тестов
                      Rate  log_sub log_string log_const_sub log_const_string
log_sub           506414/s       --       -85%          -88%             -92%
log_string       3478006/s     587%         --          -17%             -44%
log_const_sub    4211435/s     732%        21%            --             -32%
log_const_string 6227402/s    1130%        79%           48%               --



Скачать бенчмарк

суббота, 21 ноября 2009 г.

Удобства perl 5.10 или Modern::Perl

Как известно в perl 5.10 появилось много всяких вкусностей:
все новые операторы доступны по-умолчанию
Пример 1:
$a = 0 unless defined $a; #раньше 
$a //= 0;
 # perl 5.10
Пример 2:
Допустим у нас есть массив 
@types = qw/type1 type2 type3/;
и необходимо проверить наличие в нем некого элемента
if (grep {$a eq $_} @types ) {}
  # раньше
if ($a ~~ @types ) {}  #  perl 5.10  (самый быстрый способ для поиска элемента в массиве)

Так же появились новые конструкции и функции - state, given/when, say. Но они доступны после use feature ':5.10';
Решение:
use Modern::Perl;
Написав это, Вы получите use strict, use warnings, use feature ':5.10' и mro c3; 

среда, 11 ноября 2009 г.

Правильное использование временной переменной в цикле foreach


Классический перловый foreach цикл:

Вариант 1:
foreach my $element ( @list ) {
    #some code    
}

Вариант 2:
my $element;
foreach $element ( @list ) {
    #some code    
}

Сравнение:
Большинство, не задумываясь, используют первый вариант... и это правильно :). Но есть "программисты-оптимизаторы", которые используют второй вариант, аргументируя это тем, что он "быстрее" (забивая на то, что они засоряют пространство имен родительского блока). Не слушайте их!!! Первый вариант на 40% быстрее!!! (в атаче бенчмарк; хотя, естественно, чистое время перебора элементов может занимать меньше 1% времени обработки элементов )
Многих этот факт может удивить но все объясняется следующим образом: перл просто не использут предварительно  объявленую вами переменную и объявляет свою с таким же именем.

Попробуйте сами:
my $element = "NOT_USED";
foreach $element ( 1..2 ) {
    #some code
}
print $element; #выведет "NOT_USED";

вторник, 20 октября 2009 г.

Подсветка ошибок при просмотре логов

Мы часто юзаюем команду tail для логов( например  tail -f file.log ) и часто не хватает подсветки строчек, которые содержат строку "error".

Пример 1.
Следующий код подсветит целую строку красным, если она содержит слово "error":
tail -f file.log | perl -pe 's/^.*error.+$/\e[1;32;41m$&\e[0m/gim'
#ключ -p обрамляет программу в while (<>) { ... print  $_}

Пример 2.
Тут строки со словами "error", "warn", "debug" будут подсвечиваться тремя разными цветами:
tail -f file.log | perl -pe 's/^.*error.+$/\e[1;32;41m$&\e[0m/gim  or s/^.*debug.+$/\e[1;33;44m$&\e[0m/gim or s/^.*warn.+$/\e[1;33;45m$&\e[0m/gmi'

я написал небольшой скриптик который подсвечивает нужные строки по шаблону.
примеры использования:
tail -f file.log | hl -p error          #подсветить ошибки
tail -f file.log | hl              # подсвечивать ошибки и варнинги
cat file.log | hl -p warn ownpattern # подсвечивать варнинги и строки содержащие  ownpattern



Скачать приложение

понедельник, 10 августа 2009 г.

Term::ANSIColor (стандартный модуль) - раскрась терминал

Очень простой стандартный модуль, который великолепно справляется со своей задачей, а именно подсветить текст необходимым Вам цветом.
За деталями лезем в  perldoc Term::ANSIColor
Пример 1.
use Term::ANSIColor;
print color 'bold blue';
print "This text is bold blue.\n";
print color 'reset';
print "This text is normal.\n";
print colored ("Yellow on magenta.\n", 'yellow on_magenta');
print "This text is normal.\n";
print colored ['yellow on_magenta'], "Yellow on magenta.\n";

Пример 2.
use Term::ANSIColor qw(:constants);
print BOLD BLUE ON_WHITE "This text will be bold blue on white background", RESET;

суббота, 25 июля 2009 г.

Сортировка Шварца

Представим, что у нас есть масив @ar , нужно получить масив @ar_new отортировав масив @ar следующим образом :
@ar_new = sort { slow_sub ($a) <=> slow_sub($b) } @ar 
Все бы хорошо но для сортировки n елементов блок сравнения будет вызван примерно n log n раз, а каждое сравнение это двойной вызов slow_sub. И в данной ситуации сортировка Шварца все решает.

@ar_new = map  { $_->[0] } 
                       sort { $a->[1] <=> $b->[1] }  
          map  { [ $_, slow_sub($_) ] } @ar;
Теперь slow_sub будет вызвана для каждого значени всего один раз.

среда, 15 июля 2009 г.

При присвоении перл возвращает переменную (вернее - lvalue), а не ее значение

Что это дает?
Пример 1.
Можно сделать замену в скопированной переменной(сохранив при этом оригинальную)
(my $new_var = $original_var)  =~ s/pattern/replacement/;

Пример 2.
Запись вида :
$a += 2;
$a *= 3;
можно заменить на :
($a += 2) *= 3;
Пример 3.
Запись вида :

if ($x) {
    $a->{key} = 1;
}else{
    $b->{key} = 1;
}

Можно заменить на : ($x ? $a : $b)->{key} = 1

ЗЫ: с это особенностью нужно быть осторожным.
Например, код:  0 ? $a = 1 : $a = 2; print $a; # выведет 2(вроде правильно)
1 ? $a = 1 : $a = 2; print $a; # выведет тоже  2. Хотя это не самый лучший  пример, в данном случае можно (и нужно) было  бы записать $a = 1? 1: 2;

пятница, 10 июля 2009 г.

Вы знаете про стандартную перловую утилиту find2perl ?!

find2perl транслирует параметры команды  "find" в перловый код,  который использует стандартный модуль  File::Find
смотреть perldoc perlutils 

Пример:
find2perl . -user root -perm 4000 -print
выдаст следующий платформонезависимый код (функция wanted нас интересует больше всего )

#здесь было начало. выполнив пример можно увидеть весь код  ;)
sub wanted {
    my ($dev,$ino,$mode,$nlink,$uid,$gid);

    (($dev,$ino,$mode,$nlink,$uid,$gid) = lstat($_)) &&
    ($uid == $uid{'root'}) &&
    (($mode & 0777) == 04000) &&
    print("$name\n");
}

воскресенье, 5 июля 2009 г.

Просмотр исходного текста модуля установленого в систему

perldoc -m Module

Пример:
perldoc -m File::Path

Модуль открылся и нет подсветки синтаксиса - просто нажмите "v". Модуль переоткроется в вашем редакторе(переменная EDITOR, у меня, например, vim) со всеми выплывающими прелестями. (этот фокус работает, если вы используете less как стандартный пейджер)

Как узнать где находится модуль установленный в систему ?

perldoc -l Module

Пример 1:
perldoc -l File::Path
/usr/lib64/perl5/site_perl/5.8.8/File/Path.pm

Пример 2:
Открыть системный модуль в vim можно следующим образом: vim `perldoc -l File::Path`