среда, 27 января 2010 г.

Движки сериализации: кто быстрее?

Сегодня наткнулся на модуль Benchmark::Serialize, который тестирует разные движки сериализации(
тестируется сериализация, десериализация и размер сериализированных данных).

У меня в системы оказались следующие движки: Data::Dumper 2.124, JSON::PP 2.27000 JSON::XS 2.27, Storable 2.20, XML::Simple 2.18. 

И я был очень и очень удивлен результатом. Первое место по всем показателям достается модулю JSON::XS, при десериализации он быстрее Storable на 30%, а при сериализации в целых 6 (!!!) раз. Размер данных тоже минимальный.

Привожу результаты (исходник бенчмарка в атаче)
               Size     Deflate    Inflate
JSON::XS       376     313980/s   139750/s   
JSON::PP       376       5307/s     1778/s    
Storable       382      46848/s   109720/s    
XML::Simple    502       4912/s     1249/s 
Data::Dumper   1058     15486/s    18719/s

вторник, 26 января 2010 г.

Генерация произносимых паролей

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

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

Был выбран модуль Text::Password::Pronounceable, результатом работы которого я остался очень доволен.
Модуль простейший, и вся возможности помещаются в два следующих примера генерации произносимого пароля длиной 6-10 символов
пример 1:
Text::Password::Pronounceable->generate(6, 10); 
пример 2:
my $pp = Text::Password::Pronounceable->new(6, 10);  
$pp->generate

Примеры сгенерированных  паролей:
theoro ortofi nditost coftodt thagorth taprnthi ingingr linesror destro icerarado

ЗЫ:  Мне всегда нравились каптчи(CAPTCHA) гугла тем, что они читабельны и этот модуль можно использовать также для генерации текста "вменяемых" каптч.
ЗЫ2: Для генерации более надежных паролей можно использовать Text::Password::Pronounceable::Harden

понедельник, 25 января 2010 г.

Рекурсивное копирование сложных структур данных


Модуль Storable
Классический вариант - это использования функции dclone со стандартного модуля Storable, который предзначен для сериализации данных, и невероятно быстрый. Реализован на 'C' с низкоуровневыми оптимизациями, работающими с внутренней реализацией Perl( инкапсуляция отдданна в жертву в обмен на скорость)
Функция  dclone на самом деле сначала просто сериализирует в память данные, а потом десериализирует в новую структуру( dclone(.) = thaw(freeze(.)) ).
В документации по модулю Storable мы можем найти следующую информацию: 
"There is a Clone module available on CPAN which implements deep cloning natively, i.e. without freezing to memory and thawing the result.  It is aimed to replace Storable's dclone() some day.  However, it does not currently support Storable hooks to redefine the way deep cloning is performed."

Модуль Clone
Модуль Clone позиционирует себя как более производительное решение для клонирования сложных структур данных чем Storable, что прямо написанов документации "For a slower, but more flexible solution see Storable's dclone()."

Результат тестирования(бенчмарк приатачен)
Модуль clone имеет преимущество на простых структурах данных(с минимальным количеством элементов), на сложных структурах - он проигрывает. 
Уже массив с 15 элементов({foo=>'bar', bar=>'foo' })  копируется практически с одинаковой скоростью. При увеличение количества элементов Clone начинает проигрывать. 

                    Rate storable_simple    clone_simple
storable_simple  59644/s              --            -82%
clone_simple    331579/s            456%              --

                     Rate storable_standart    clone_standart
storable_standart 30914/s                --               -3%
clone_standart    31870/s                3%                --

                   Rate    clone_complex storable_complex
clone_complex    3543/s               --             -47%
storable_complex 6719/s              90%               --

Выводы
Практически всегда стандартный модуль Storable явлеется более удачным решением для клонирования структур.

Использование Clone может быть оправдано только в очень ограниченных случаях и при соблюдение следующих условий: 
  1. Копируются очень простые структуры данных 
  2. Эти структуры не являются частью одной сложной структуры( если их много, то скорее всего они в массиве, тогда имеет смысл скопировать весь массив при помощи Storable и это будет однозначно быстрее чем копировать поэлементно или весь массив сразу при помои Clone
  3. Время их копирования является узким местом при выполнении программы.

понедельник, 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



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