среда, 21 апреля 2010 г.

Тест на понимание Perl: Вопрос 4

Вопрос:
sub a{&b; print shift}
sub b{print shift} 
a(1,2);

Ответ: 12

Пояснение:
Главная особенность состоит в том, что вызов функции &b происходит без круглых скобок и с использованием амперсанда. То есть вызов &b b(), &b() - это разные вызовы и каждый имеет свою особенность.
b() - классический вызов функции
&b() - аналогично первому варианту, но игнорируются прототипы
&b   - если функция вызывается таким образом, то в нее передается  массив @_, и я думаю, что мало кого это удивит... 
но важно то, что вызовы &b(@_) и &b - имеют отличия
в обоих случаях в функцию b придет список аргументов родительской функции, но в случае с &b(@_) это будет копия(локализированный массив), а в случае с &b, в функцию попадет оригинальный массив @_ и если сделать изменения в b, то они произойдут в @_ родительской функции.


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

Тест на понимание Perl: Вопрос 3

Вопрос:
0 ? $a = 1: $b = 2;
1 ? $a = 1: $b = 2;
print "$a $b"

Ответ: 2 2 

Пояснение: 
Тут есть два важных момента:

  1. Приоритет тернарного оператора выше чем у "=" и поэтому конструкция работает так (0 ? $a = 1: $b) = 2;
  2. После вычесления тернарного оператора возвращается переменная, а не ее значение. И ее можно изменить.

Вот еще наглядный пример: 
конструкцию вида
if ( $x ) { 
    $a->{key} = 1;
}
else { 
    $b->{key} = 1; 
}
можно заменить на
($x ? $a : $b)->{key} = 1;

суббота, 17 апреля 2010 г.

Экономия памяти: YAML::Tiny

Небольшое вступление для тех, кто  еще не использует YAML (позволю себе процитировать википедию )
YAML — человекочитаемый формат сериализации данных, концептуально близкий к языкам разметки, но ориентированный на удобство ввода-вывода типичных структур данныхмногих языков программирования. 
Я использую YAML для разного рода конфигурационной информации.
Почему я использую YAML? :
  • YAML краток и понятен;
  • YAML очень выразительный и расширяемый;
  • YAML допускает простой потоковый интерфейс;
  • YAML использует структуры данных, родные для языков программирования;
  • YAML легко реализуется, возможно, слишком легко;
  • YAML использует цельную модель данных. Нет исключений — нет беспорядка.
Сабж
Идея YAML::Tiny - Читать/Писать  YAML данные с минимальными затратами + минимум внешних зависимостей(не использует ничего, кроме стандартных модулей). 
Просто заменив в коде:
use YAML 'LoadFile';
на 
use YAML::Tiny 'LoadFile'; 
я сэкономил 4МБ памяти в каждом FCGI процессе!!!

Спецификация YAML невероятно огромна.  И Pure Perl реализация - модуль  YAML поддерживает ее полностью и съедает 4МБ оперативной памяти. Yaml::Tiny мал и быстр за счет того, что он не поддерживает все возможности описанные в спецификации.  

ЗЫ: Если не хватает возможностей YAML::Tiny, тогда следует смотреть в сторону YAML::Syck


пятница, 9 апреля 2010 г.

Несостоятельность ithreads в Perl (сам не ожидал)

Предыстория
Никогда до этого не использовал треды(threads)  в Perl - как-то не доверял я им и использовал forkи... и как оказалось не зря!
И настал тот день, когда от тредов никуда не убежишь(на самом деле убежишь, но про это дальше) - нужно было включить многопоточность для модуля Fuse.pm(просто передается дополнительный параметр threaded => 1), и тогда на каждый вызов файлофай системы будет создаваться тред.

Собственно сабж:
  1. Переменные по-умолчанию не являются общими между тредами. Это означает, что каждый раз когда вы стартуете тред, все структуры данных копируются в новый тред. Все - это значит все, включая внутренности всех пакетов, глобальные переменные, лексические...
  2. Общие переменные на самом деле - не общие, а просто связанные(Tied), со всеми вытекающими последствиями - требуют больше памяти и более медленные. Подробней тут http://www.perlmonks.org/index.pl?node_id=288022
  3. Самое печальное. Треды ЛИКАЮТ!!! Каждый запущенный тред после своего завершения не возвращает часть памяти!!!
  4. Треды в Perl требуют больше памяти чем fork-и !!! Да именно так :), поскольку fork-и используют COPY-ON-WRITE при копировании процесса в памяти.
  5. Нужен Perl скомпилированный с поддержкой тредов.
Вот простой тест на лики памяти: 

Достаточно запустить и понаблюдать за потреблением памяти процессом.
use threads;
while (1) {
    threads->create(sub{})->join();
}
Решение.
Использовать fork-и :) (естественно там, где они нативно поддерживаются). По существу:
  1. Fork-и не ликают
  2. Fork-и понятней и не подвержены ошибкам связанными с семафорами и локами данных. 
  3. Сейчас в большинстве unix системах fork-и - дешевые(в Linux в том числе), поскольку используется COPY-ON-WRITE метод. То есть, при создании нового процесса содержимое памяти не копируется. Накладные расходы только на организацию нового процесса.
  4. Работают на любой версии Perl
А что если выхода нет? Как в случае c модулем Fuse.pm? Я уже думал, что придется отказаться от многопоточности, но наткнулся на модуль forks. Этот модуль являет собой прагму, которая заменяет нативную реализацию тредов на использование fork-ов. 
Представьте себе, что есть код написанный с использованием тредов, просто напишите вначале кода use forks; и код будет использовать fork-и вместо тредов ( с Fuse.pm работает ).
Вот пример( и код перестает ликать):
use forks;
use threads;

while (1) {
    threads->create(sub{})->join();
}


четверг, 1 апреля 2010 г.

Мог бы быть вопрос номер 3 на понимание Perl ...

... если бы я сам понимал что тут происходит
perl -e 'my $i=5; print(++$i + ++$i)' #  почему тут 14, а не 13 ???


perl -e 'my $i=5; print(sub{++$i}->() + sub{++$i}->())' #   тут все нормально - выдает 13



perl -e 'my $i=5; print( $i++ + $i++)' # и так нормально - выдает 11