tag:blogger.com,1999:blog-74981415302769543272024-03-13T13:59:46.731+02:00Laziness, Impatience and HubrisЗаметки программиста, в основном, о perl.
Название блога происходит от трех главных добродетелей программиста: Лень, Нетерпение и Высокомерие.
К некоторым статьям следует относиться с определенной долей юмора.
<a href="http://laziness-impatience-hubris.blogspot.com/2008/10/blog-post.html">Содержание</a>.Unknownnoreply@blogger.comBlogger77125tag:blogger.com,1999:blog-7498141530276954327.post-49199731688178954562021-09-14T07:05:00.002+03:002021-09-14T08:26:25.036+03:00Об unicode в Perl<p>В очередной раз задумался о том, какие строки оптимальней использовать в основном коде: characters или octets.</p>
<p>Обычно мне очень редко приходиться работать непосредственно с unicode.
Когда нужно, я преобразовываю octets в characters, а затем обратно.</p>
<p>Но HTML::Parser требует на вход unicode, и JSON::XS корректно работает только с unicode. Поэтому хоть непосредственно я сам не использую characters, приходиться перекодировать при помощи модуля Encode. </p>
<p>Кроме этих модулей, обычно все работают с octets.</p>
<p>А вот при работе над магазинчиком вышивки бисером и нитками <a href="https://embroidery-kits.com/">Embroidery Kits</a> выяснилось, что остальные используемый модули могут работать как с octets, так и с characters (кроме Digest::SHA).</p>
<p>HTML::Parser и JSON::XS перетянули одеяло на себя. Задумался.</p>
<p>P.S.<br />
Utf8 флаг в сложных структурах данных расставляю, сбрасываю при помощи нижеприведенного кода.</p>
<pre>
use Encode;
use Scalar::Util qw(looks_like_number);
sub data_walk($$);
sub data_walk($$) {
my ($d,$s) = @_;
if (ref $d eq "ARRAY") {
$d = [ map { data_walk($_, $s) } @$d ];
} elsif (ref $d eq "HASH") {
$d = { map { $s->($_) => data_walk($$d{$_}, $s) } keys %$d };
} elsif (not ref $d) {
$d = $s->($d)
}
return $d;
}
sub data_encode_utf8 { data_walk(shift,
sub {
my $data = shift;
if ($data and not looks_like_number($data)) {
$data = Encode::encode_utf8($data) if Encode::is_utf8($data);
}
return $data;
}
)
}
sub data_decode_utf8 { data_walk(shift,
sub {
my $data = shift;
if ($data and not looks_like_number($data)) {
$data = Encode::decode_utf8($data, Encode::FB_QUIET) unless Encode::is_utf8($data);
$data ||= "ERROR: FOR SOME REASON, TEXT CONVERSION TO UTF8 FAILED";
}
return $data;
}
)
}
</pre>
Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-7498141530276954327.post-69714407484280083332019-05-31T11:38:00.005+03:002019-05-31T11:38:58.742+03:00HTML::Parser vs HTML::TreeBuilder vs HTML::Gumbo<div dir="ltr" style="text-align: left;" trbidi="on">Benchmark для http://perl.org/ страницы. <br />
Извлекаем текс и ссылки.<br />
<br />
<pre> Rate Tree Gumbo pure Gumbo Gumbo cb SAX
Tree 46/s -- -63% -72% -80% -87%
Gumbo pure 122/s 167% -- -24% -47% -64%
Gumbo 161/s 252% 32% -- -30% -53%
Gumbo cb 230/s 403% 89% 43% -- -32%
SAX 339/s 642% 178% 111% 47% --
</pre><br />
где:<br />
<pre>SAX - это HTML::Parser
Tree - HTML::TreeBuilder
Gumbo - HTML::Gumbo with tree output format
Gumbo cb - HTML::Gumbo with callback output format
</pre><br />
Обнако обнаружил, что HTML::Gumbo with tree output format на некоторых HTML страничах течет.<br />
https://rt.cpan.org/Public/Bug/Display.html?id=128667<br />
<br />
HTML::Gumbo строит DOM при помощи HTML::Elements со стороны XSUB.<br />
<br />
Поэтому решил попробовал строить DOM при помощи HTML::Elements с стороны pure perl, а не XSUB.<br />
Это вариант обозначен как "Gumbo pure" в таблице результата Benchmark.<br />
<br />
</div>Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-7498141530276954327.post-15410610761203099002015-11-26T16:18:00.000+02:002015-11-26T16:18:27.526+02:00Упрощаем работу с многоуровневыми структурами данных из внешних источников<div dir="ltr" style="text-align: left;" trbidi="on">В Perl, благодаря "самооживлению" ссылок, очень удобна работа с многоуровневыми структурами данных. Например:<br />
<pre> my %foo = ();
my $k = "k";
$foo{$k}{a} = "b";
$foo{$k}{c} = "d";
</pre>Однако, если %foo - это внешняя база данных, например BerkeleyDB, то значения необходимо запаковать тем же Storable или JSON.<br />
При этом удобство работы с многоуровневыми структурами данных снижается.<br />
<br />
Необходимо извлечь значение, распаковать его, а если значения не было, то создать. Затем после изменения - запаковать и поместить обратно.<br />
<pre> my %foo = ();
my $k = "k";
my $f;
if (my $_f = $foo{$k}) {
$f = decode_json $_f;
} else {
$f = {};
}
$$f{a} = "b";
$$f{c} = "d";
$foo{$k} = encode_json $f;
</pre>Теперь представим, что это необходимо делать в разных местах программы.<br />
Проще написать функцию для внесения изменений, которая будет вызываться примерно вот как:<br />
<pre> $foo->($k, sub {
my ($v) = @_;
$$v{a} = "b";
$$v{c} = "d";
return $v;
} );
</pre>Немножко многословно. Но в Perl есть "магическая" переменная $_, которая позволяет сделать следующие:<br />
<pre> $foo->($k, sub {
$$_{a} = "b";
$$_{c} = "d";
} );
</pre>Соответственно, сама функция будет выглядеть так:<br />
<pre> my %foo = ();
my $foo = sub {
my ($k, $sub) = @_;
local $_;
if (my $_f = $foo{$k}) {
$_ = decode_json $_f;
} else {
$_ = {};
}
$sub->();
$foo{$k} = encode_json $_;
};
</pre>Хотя можно и так:<br />
<pre> sub foo(&$);
my %foo = ();
local *foo = sub {
my ($sub, $k) = @_;
local $_;
if (my $_f = $foo{$k}) {
$_ = decode_json $_f;
} else {
$_ = {};
}
$sub->();
$foo{$k} = encode_json $_;
};
foo {
$$_{a} = "b";
$$_{c} = "d";
} "k";
</pre>Даже не знаю как лучше... :-)<br />
<br />
</div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-7498141530276954327.post-63620013338805040862014-12-24T10:45:00.000+02:002014-12-24T10:45:19.658+02:00HTTP content encoding<div dir="ltr" style="text-align: left;" trbidi="on">Решил прикрутить к AnyEvent::HTTP Accept-Encoding и к LWP в response_data handler, но перед этим выяснить какой процент серверов понимает gzip и deflate.<br />
<br />
deflate кодирование реализовано в серверах двумя способами и поэтому авторы nginx отказались от его реализации и используют только gzip<br />
(<a href="http://sysoev.ru/mod_deflate/readme.html#mehtods">http://sysoev.ru/mod_deflate/readme.html#mehtods</a>).<br />
<br />
Под рукой оказался файл с 53587 доменами со следующим распределением по зонам:<br />
<pre> 29244 com 1000 de 627 it
3102 org 935 info 553 ca
2786 uk 861 nl 504 si
2734 net 853 au 467 fr
1315 ru 682 br 368 ua
</pre><br />
Для каждого домена запрашивал HTTP содержимое с указанием "Accept-Encoding" в трех различных вариантах: "gzip, deflate" (приоритет gzip), "deflate, gzip" (приоретет deflate) и "deflate". Результаты как закодировал ответ сервер представлены в нижеприведенной таблице (прочерк означает, что сервер вернул не закодированное содержимое):<br />
<pre> | "gzip, deflate" | "deflate, gzip" | "deflate"
------------|-----------------|-----------------|----------
- | 22888 | 22764 | 50462
gzip | 30612 | 30442 | 145
deflate | 67 | 361 | 2959
iso-8859-1 | 1 | 1 | 1
none | 16 | 16 | 16
none; | 1 | 1 | 1
UTF-8 | 2 | 2 | 2
</pre><br />
Как видим, можно ограничиться поддержкой лишь одного gzip.<br />
<br />
Заодно узнал популярность серверов:<br />
<pre> Все зоны | ru зона | ua зона
------------------------|---------|--------
Apache 27327 | 978 | 443
nginx 6924 | 922 | 332
Microsoft-IIS 6886 | 323 | 148
- 6116 | 212 | 103
</pre></div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-7498141530276954327.post-27319989444599685392014-06-13T11:25:00.000+03:002014-06-13T11:25:04.896+03:00COW in perl-5.20<div dir="ltr" style="text-align: left;" trbidi="on">В perl-5.20 реализовали механизм копирования при записи (copy-on-write) для строк. Теперь при присвоении одному скаляру значения другого, копирования буфера строки сразу не происходит. Это значительно повышает производительность и снимает необходимость передачи аргументов функций по ссылке (если они не будут изменяться).<br />
<br />
Сравним скорость вызова подпрограмм с различными комбинация передачи параметра и возвращения результата для предыдущей версии perl и для версии с COW: <br />
<pre>> perlbrew use perl-5.18.2
> perl ref_and_val.pl
Rate val -> val val -> ref ref -> val ref -> ref
val -> val 68213/s -- -51% -51% -97%
val -> ref 138122/s 102% -- -1% -93%
ref -> val 139276/s 104% 1% -- -93%
ref -> ref 2000000/s 2832% 1348% 1336% --
> perlbrew use perl-5.20.0
> perl ref_and_val.pl
(warning: too few iterations for a reliable count)
(warning: too few iterations for a reliable count)
(warning: too few iterations for a reliable count)
Rate ref -> val val -> val ref -> ref val -> ref
ref -> val 2083333/s -- -17% -21% -29%
val -> val 2500000/s 20% -- -5% -15%
ref -> ref 2631579/s 26% 5% -- -11%
val -> ref 2941176/s 41% 18% 12% --
</pre>Результаты впечатляют, так как длина тестируемой строки 100000 символов!<br />
<br />
А теперь возьмем реальное приложение. Оно сетевое, занимается "перекладыванием байтиков" с одного источника в 4 на основе srs32. <br />
Ниже приведены количество запрос в секунду для 3 различных типов запросов в простом и pipeline режимах. Уточнение: сеть не является узким местом. <br />
<pre> 1 2 3
perl-5.14.4 7272 6134 3886
perl-5.18.2 7610 6439 4139
perl-5.20.0 7581 6459 4338
pipeline mode:
perl-5.14.4 21141 13869 5998
perl-5.18.2 21367 14025 6269
perl-5.20.0 21598 14367 6518
</pre>Как видим, в реальном приложении выигрыш от COW не заметен.<br />
</div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-7498141530276954327.post-15144213392150183292012-11-12T15:52:00.000+02:002012-11-12T15:52:37.980+02:00Деструкторы для замыканий, часть 2<div dir="ltr" style="text-align: left;" trbidi="on"><a href="/2012/05/blog-post.html">Первая часть.</a><br />
<p>А если замыкание используется лишь в самом начале блока, то рациональней сразу после использования освободить ресурсы, а не в конце.</p><pre> use Carp;
sub with_foo {
my $foo = shift;
my $sub = shift;
print "INIT\n";
my $destroyed = 0;
my $closure = sub {
unless ($destroyed) {
$foo += shift;
print $foo, "\n";
} else {
carp "Already destroyed";
}
};
my $destructor = sub {
unless ($destroyed) {
$destroyed = 1;
print "DESTROY\n";
} else {
carp "Already destroyed";
}
};
eval { $sub->($closure, $destructor) };
$destructor->() unless $destroyed;
}
{
with_foo(3, sub {
my ($foo, $_foo) = @_;
$foo->(1);
$foo->(1);
$foo->(2);
$foo->(2);
$_foo->();
print "END\n"
});
}
</pre></div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-7498141530276954327.post-29376965273407565612012-09-27T14:06:00.000+03:002012-09-27T14:06:15.310+03:00Very simple Multithreading with Continuation-passing style<pre>{
my @cc = ();
sub cc(&) {
my ($sub) = @_;
push @cc, $sub;
}
sub cc_run() {
while (my $sub = shift @cc) {
$sub->();
sleep 1;
}
}
}
sub first {
my ($thread) = @_;
print "$thread: 1 first\n";
cc { third($thread) };
}
sub second {
my ($thread) = @_;
print "$thread: 2 second\n";
cc { fourth($thread) };
}
sub third {
my ($thread) = @_;
print "$thread: 3 third\n";
cc { first($thread) };
}
sub fourth {
my ($thread) = @_;
print "$thread: 4 fourth\n\n";
cc { second($thread) };
}
cc { first("+") };
cc { second("*") };
cc_run;
</pre>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-7498141530276954327.post-25119870090332963632012-09-24T10:55:00.001+03:002012-09-24T10:55:57.749+03:00Добавил quit в IPC::MPS<div dir="ltr" style="text-align: left;" trbidi="on">Очень плотно и давно использую модуль IPC::MPS для распараллеливания задач, но только сейчас понадобился выход из receive блока (из вложенного receive). Поэтому добавил подпрограмму quit.<br />
</div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-7498141530276954327.post-51490851044957220232012-06-22T10:57:00.000+03:002012-06-22T10:57:05.493+03:00Сравниваем MongoDB с MySql как Key-Value<div dir="ltr" style="text-align: left;" trbidi="on">Уговорили меня включить MongoDB в сравнение баз данных для Key-Value (смотрите <a href="/2012/05/postgresql-mysql-and-mariadb-as-key.html">PostgreSQL, MySql and MariaDB as Key-Value storage</a>).<br />
<br />
Оказалось, что MongoDB использует для хранения данных отображение памяти в файлы, поэтому для баз больше 2G, надо использовать 64-bit архитектуру.<br />
После того как узнал это, надо было сразу выкинуть MongoDB, но все таки решил проложить. Поставил FreeBSD 9.0 amd64 и для чистоты эксперимента прогнал некоторые тесты для MySql и PostgreSql.<br />
<br />
Решил тестировать только на коротких ключах (подробности смотрите тут <a href="/2012/03/berkeleydb-tokyocabinet.html">BerkeleyDB и TokyoCabinet</a>).<br />
<br />
Обнаружил, что MongoDB занимает всю память, поэтому рекомендуют запускать его на выделенной машине.<br />
Также, когда работает MongoDB, отзывчивость диска намного хуже, чем при работе других баз.<br />
<br />
Результаты:<br />
Чтение где-то в 2 раза медленней, чем у MySql.<br />
Вставка также медленней, но периодически скорость вставки настолько замедляется, что даже не удается вставить запись в течении 30 секунд!<br />
<br />
Поиск информации интернете показал, что это проблеме не связана с FreeBSD и наблюдается и под Linux.<br />
А может это такой Perl модуль? Но ведь автор его является также разработчиком ядра MongoDB!<br />
<br />
Некоторые значения полей отличаются после извлечения из базы. Может MongoDB чудит, считая, что это utf8.<br />
<br />
<b>Выводы</b>. <br />
MongoDB, с диском надо работать как с диском, а не как с памятью!<br />
MySql и PostgreSql - фавориты. А в PostgreSql 9.2 появился index only scan, - так что PostgreSql еще повысил свою привлекательность.<br />
<br />
Как люди работают с MongoDB? Или у них базы маленьких относительно оперативной памяти?<br />
</div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-7498141530276954327.post-90963151397941758282012-06-08T09:40:00.002+03:002012-06-08T09:40:54.671+03:00Одна история с Perl, Coro и IPC::MPS<div dir="ltr" style="text-align: left;" trbidi="on">Где-то два года назад была сделана некоторая система обработки получаемой из Web информации. Так как была сделана грамотно, то легким движением руки превращалась либо в многопроцессный, либо в основанный на сопрограммах вариант. В первом случае использовался модуль межпроцессного взаимодействия на основе сообщений IPC::MPS и EV, а во втором - Coro и EV.<br />
<br />
На моем десктопе оба варианта работали отлично, а у хостера вариант с Coro слишком долго выполнял задания и был выведен из эксплуатации. Система работала на 7 дешевых виртуальных серверах.<br />
<br />
Долгие выяснения, что не так с Coro вариантам ни к чему не привели. И лишь сегодня на рассвете я понял, что "ларчик просто открывался"! Я просто перегрузил Coro вариант, посчитав, что раз сопрограммы легкие, то можно одновременно обрабатывать задание побольше.<br />
<br />
Рассмотрим этот ситуацию подробней. Время выполнения задания равно сумме времени получения информации и времени обработки ее. Поскольку сопрограммы выполняются в рамках одного процесса, то оптимальное количество одновременно выполняемых заданий равно результату деления времени получения информации на время ее обработки. Если оно меньше, то процесс будет простаивать, если больше, что вырастет время выполнения задания, пропорционально этому превышению.<br />
<br />
У меня интернет медленный, поэтому 100 сопрограмм показывали отличный результат.<br />
У хостера интернет быстрый, поэтому у него Coro вариант выполнял заданий лишь в 2 раза больше, чем однопоточный. Но при этом время выполнения заданий было больше раз в 50. Перегрузил.<br />
<br />
То есть, вариант с сопрограммами оптимален для медленных каналов связи или неторопливой второй стороны.<br />
<br />
Теперь рассмотрим многопроцессный вариант. Для него следует учитывать также время переключения между процессами и время на межпроцессное взаимодействие. На однопроцессорной машине этот вариант должен быть лишь немного медленней, чем оптимально настроенный вариант с сопрограммами. Однако, если компьютер используется и для других задач, то этот вариант покажет лучшую производительность за счет того, что суммарно его процессы получат больше процессорного времени. Но увлекаться особо не стоит, помните о затратах на межпроцессное взаимодействие.<br />
<br />
Ну, а если процессоров много, что многопроцессорный вариант находиться вне конкуренции. Кончено, самую высокую производительность покажет гибрид многопроцессного варианта и варианта с сопрограммами. Но если памяти на сервере хватает, каналы быстрые и источники информации резвые, то, наверно не стоит тратить время на гибрид. Хотя в каждом конкретном случае необходим индивидуальных подход.<br />
<br />
Разумеется, если не ты задаешь управляешь интенсивность работы системы, а должен быть готов обработать запросы множества медленных клиентов, то тут без событийной машины или сопрограмм не обойтись.<br />
</div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-7498141530276954327.post-58348291090084463162012-06-05T11:22:00.000+03:002012-06-05T13:51:50.168+03:00IPC::MPS was updated for Multiplicative Agent<div dir="ltr" style="text-align: left;" trbidi="on">IPC::MPS was updated for Multiplicative Agent.<br />
<br />
Changes:<br />
- connected flag in NODE_CLOSED<br />
- your own pack and unpack functions, instead of Storable<br />
<br />
First change is need to distinguish "Cannot connect to node" and "Node closed" states. <br />
</div>Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-7498141530276954327.post-69922904134009096362012-05-25T10:40:00.000+03:002012-05-25T10:40:08.830+03:00Деструкторы для замыканий<div dir="ltr" style="text-align: left;" trbidi="on">
Периодически ловил себя на мысли, что хорошо бы, чтобы у замыканий были дестркторы.
И лишь вчера осознал, что это можно легко организовать "вывернув" замыкания на изнанку.
Рассмотрим в качестве примера следующий код:
<pre>
sub make_foo {
my $foo = shift;
print "INIT\n";
return sub {
$foo += shift;
print $foo, "\n";
};
}
{
my $foo = make_foo(3);
$foo->(1);
$foo->(1);
$foo->(2);
$foo->(2);
}
</pre>
А теперь сделаем так, что замыкание не возвращается в функцию с действием, а передается ей в качестве аргумента.
Это позволяет добавить деструктор сразу после вызова этой функции:
<pre>
sub with_foo {
my $foo = shift;
my $sub = shift;
print "INIT\n";
$sub->(sub {
$foo += shift;
print $foo, "\n";
});
print "DESTROY\n";
}
{
with_foo(3, sub {
my $foo = shift;
$foo->(1);
$foo->(1);
$foo->(2);
$foo->(2);
});
}
</pre>
</div>Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-7498141530276954327.post-61647177626182629132012-05-15T11:27:00.000+03:002012-05-15T11:27:25.653+03:00PostgreSQL, MySql and MariaDB as Key-Value storage<div dir="ltr" style="text-align: left;" trbidi="on">
После сравнения <a href="http://laziness-impatience-hubris.blogspot.com/2012/03/berkeleydb-tokyocabinet.html">BerkeleyDB и TokyoCabinet</a> настало время посмотреть на PostgreSQL, MySql и MariaDB как на хранилища ключ-значения.<br />
<br />
Использовался тот же маленких сервачек.<br />
<br />
<b>PostgreSQL</b><br />
<br />
<br />
postgresql-server v9.1.2<br />
<br />
SET synchronous_commit TO OFF<br />
commit_delay = 1<br />
<br />
Во всех вариантах PostgreSQL заметно быстрей чем BerkeleyDB и TokyoCabinet! Разумеется это, когда объем базы превышал размер оперативной памяти.<br />
<br />
С ростом базы замечено существенное снижение производительности, когда используется btree индекс.<br />
Поэтому для очень больших баз и когда этого достаточно, стоит использовать hash индекс.<br />
<br />
<br />
<b>MySql and MariaDB</b><br />
<br />
<br />
Для perl модуля DBD::mysql используется патч <a href="https://rt.cpan.org/Public/Bug/Display.html?id=76462">https://rt.cpan.org/Public/Bug/Display.html?id=76462</a>,<br />
чтобы при mysql_server_prepare не было утечки памяти.<br />
<br />
mysql-server-5.1.61<br />
mariadb-server-5.2.10<br />
<br />
У сравнении участвовали:<br />
MyISAM<br />
ARIA<br />
XtraDB (Innodb_flush_log_at_trx_commit=0)<br />
<br />
HandlerSocket не использовался.<br />
<br />
На маленьких базах и длинных ключах TokyoCabinet hash (не btree) быстрей в 3-5 раз чем MyISAM, ARIA и XtraDB.<br />
На коротких ключах MyISAM быстрей в 5 раз.<br />
<br />
Когда базы большие, то MySql and MariaDB вырываются вперед, даже XtraDB быстрей, чем TokyoCabinet.<br />
<br />
Как и у PostgreSQL, с ростом базы замечено существенное снижение производительности, когда используется btree индекс.<br />
Преимущества hash индекса (XtraDB) не сказалось, в отличие от PostgreSQL.<br />
<br />
<br />
<b>Выводы</b><br />
<br />
<br />
Когда базы маленькие можно использовать TokyoCabinet или BerkeleyDB.<br />
Когда данных больше, то стоить посмотреть в сторону MyISAM, ARIA (MariaDB) или PostgreSQL.<br />
Когда базы огромные, то лучше использовать PostgreSQL с HASH индексами.</div>Unknownnoreply@blogger.com2tag:blogger.com,1999:blog-7498141530276954327.post-44704009209125328302012-03-26T09:29:00.005+03:002012-03-29T09:02:58.587+03:00BerkeleyDB и TokyoCabinetРезультаты двух недельноего стравнения BerkeleyDB и TokyoCabinet на стареньком компьютере.<br /><br />BerkeleyDB::Recno, которые, кстати, сделаны поверх Btree, можно заменить "ручными очередями", сделаными на основе BerkeleyDB::Btree или TokyoCabinet::BDB, без потери производительности.<br /><br />Настройками по умолчанию TokyoCabinet предназначены для маленьких баз. BerkeleyDB - для средних.<br /><br />Для больших баз с соответствующими настройками BerkeleyDB::Btree и TokyoCabinet::BDB примерно одинаковы по производительности и размеру. Большие базы 5000000 записей, key 1040 - байт, value - 1000 байт.<br /><br />Тестовый сервер:<br />CPU: AMD Sempron(tm) Processor 2800+ (1608.27-MHz 686-class CPU)<br />real memory = 8589934592 (8192 MB)<br />ada0: <ST380011A 8.01> ATA-6 device<br />ada0: 100.000MB/s transfers (UDMA5, PIO 8192bytes)<br />ada0: 76318MB (156299375 512 byte sectors: 16H 63S/T 16383C)<br /><br />А вот TokyoCabinet::HDB выигрывает в скорости и в размене у BerkeleyDB::Hash почти в два раза.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-7498141530276954327.post-49196642141803568122011-06-23T10:30:00.001+03:002011-06-23T10:32:59.829+03:00Закорючки, продолжениеПредположим, что вам необходимо множества раз писать данные в сокет, при этом задача не позволяет объединить все операции записи в одну.<br /><pre><br /> Perl:<br /> send $sock, $msg1;<br /> send $sock, $msgN;<br /><br /> OZ:<br /> {send Sock Msg1}<br /> {send Sock MsgN}<br /><br /> Haskell:<br /> send sock msg1<br /> send sock msgN<br /></pre><br />Много раз повторяется "send sock" - попробуем избавиться от дублирования.<br /><pre><br /> Perl:<br /> my $snd = sub { send $sock, @_ };<br /> $snd->($msg1);<br /> $snd->($msgN);<br /> # или &$send($msgN);<br /> <br /> OZ:<br /> local Snd = fun {$ M} send Sock M end<br /> {Snd Msg1}<br /> {Snd MsgN}<br /> <br /> Haskell:<br /> let snd = send sock<br /> snd msg1<br /> snd msgN<br /></pre><br />Как видим, Perl закорючки (@#$%&) путаются под ногами.Unknownnoreply@blogger.com2tag:blogger.com,1999:blog-7498141530276954327.post-46624025448476661952011-06-02T11:30:00.001+03:002011-06-02T11:33:04.155+03:00ЗакорючкиВ Perl 5.14 можно передавать функциям, ожидающим в качестве аргументов хеши и массивы, не только их, а и ссылки на них.<br /><pre><br /> 1. |----------------------------+---------------------------|<br /> 2. | Traditional syntax | Terse syntax |<br /> 3. |----------------------------+---------------------------|<br /> 4. | push @$arrayref, @stuff | push $arrayref, @stuff |<br /> 5. | unshift @$arrayref, @stuff | unshift $arrayref, @stuff |<br /> 6. | pop @$arrayref | pop $arrayref |<br /> 7. | shift @$arrayref | shift $arrayref |<br /> 8. | splice @$arrayref, 0, 2 | splice $arrayref, 0, 2 |<br /> 9. | keys %$hashref | keys $hashref |<br /> 10. | keys @$arrayref | keys $arrayref |<br /> 11. | values %$hashref | values $hashref |<br /> 12. | values @$arrayref | values $arrayref |<br /> 13. | ($k,$v) = each %$hashref | ($k,$v) = each $hashref |<br /> 14. | ($k,$v) = each @$arrayref | ($k,$v) = each $arrayref |<br /> 15. |----------------------------+---------------------------|<br /></pre><br />То есть, все идет к тому, что скоро закорючки (@#$%&) станут не нужны.<br />Если честно, раньше думал, что их наличии упрощает код. Меньше надо выдумывать имен и идентификаторов.<br />Однако, пописав на еще более высоком уровне (OZ, Haskell), понял, что иногда без них проще.<br /><br />P.S.<br />А может просто руки болят?Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-7498141530276954327.post-34188546949600206862011-03-23T08:53:00.002+02:002011-03-23T09:08:20.962+02:00не-PerlНа прошлой неделе мне сразу два человека сказало (кажется даже в один день), что я пишу на Perl как не на Perl.<br />Забавно, но я использую обычно очень маленькое подмножество многоликого языка Perl.<br /><br />P.S.<br /><a href="http://github.com/kni/redis-sharding/tree/v0.2">http://github.com/kni/redis-sharding/tree/v0.2</a><br /><a href="http://search.cpan.org/perldoc?IPC::MPS">http://search.cpan.org/perldoc?IPC::MPS</a>Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-7498141530276954327.post-50463810341985913752011-03-14T10:16:00.006+02:002011-03-14T10:21:47.766+02:00Redis Sharding<a href="http://github.com/kni/redis-sharding/tree/v0.2">http://github.com/kni/redis-sharding/tree/v0.2</a><br /><br />Redis Sharding is a multiplexed proxy-server, designed to work with the database divided to several servers.<br />It's a temporary substitution of Redis Cluster (http://redis.io) that is under development.<br /><br />Redis Sharding is used for horizontal Redis database scaling (with connecting of additional servers) as long as load distribution between the cores on the multiprocessor servers (as Redis server is single-threaded, several copies of the server can be run, one for each free core).<br /><pre><br /> /- Redis (node 1)<br /> Client 1 --- /-- Redis (node 2)<br /> Redis Sharding --- Redis (node 3)<br /> Client 2 --- \-- Redis (node 4)<br /> \- Redis (node 5)<br /></pre><br />Sharding is done based on the CRC32 checksum of a key or key tag ("key{key_tag}").Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-7498141530276954327.post-82338172942915771662010-12-02T14:57:00.005+02:002010-12-02T15:04:15.038+02:00Стиль передачи продолжений и связывание<b>Оператор связывания как синтаксический сахар</b><br /><br />По мотивам цикла заметок "Сегодня без...", а именно заметки <a href="http://laziness-impatience-hubris.blogspot.com/2009/05/return_20.html">"Сегодня без return"</a>.<br /><br />Возьмем пример кода из вышеупомянутой заметки и сразу лишим его магии Perl прототипов: все равно в последующем коде они работать не будут:<br /><pre><br /> sub mul {<br /> my ($sub, $x, $y) = @_;<br /> my @r = map { $$x[$_] * $$y[$_] } 0 .. $#$x;<br /> $sub->(@r);<br /> }<br /> <br /> sub minus {<br /> my ($sub, $x, $y) = @_;<br /> my @r = map { $$x[$_] - $$y[$_] } 0 .. $#$x;<br /> $sub->(@r);<br /> }<br /><br /> sub say {<br /> my $sub = shift;<br /> print join(" ", @_), "\n";<br /> $sub->();<br /> }<br /><br /> my @i = (1, 2, 3);<br /> my @j = (2, 3, 4);<br /> my @k = (3, 4, 5);<br /><br /> mul sub {<br /> minus sub {<br /> say sub {}, @_<br /> }, \@_, \@k<br /> }, \@i, \@j;<br /></pre><br />Результат работы это программы - вывод на печать строки "-1 2 7".<br /><br />А теперь представим, что подпрограмма minus не вызывает продолжение, а возвращает результат:<br /><pre><br /> sub minus {<br /> my ($x, $y) = @_;<br /> map { $$x[$_] - $$y[$_] } 0 .. $#$x;<br /> }<br /></pre><br />Поэтому напишем для обертку:<br /><pre><br /> sub bind_minus {<br /> my ($sub, $x, $y) = @_;<br /> my @r = minus($x, $y);<br /> $sub->(@r);<br /> }<br /></pre><br />Которую и будем использовать:<br /><pre><br /> mul sub {<br /> bind_minus sub {<br /> say sub {}, @_<br /> }, \@_, \@k<br /> }, \@i, \@j;<br /></pre><br />Затем сделаем подпрограмму bind_minus ленивой, чтобы только связывала, но ничего не вычисляла сразу (это пригодиться потом):<br /><pre><br /> sub bind_minus {<br /> my ($sub) = @_;<br /> sub {<br /> my ($x, $y) = @_;<br /> my @r = minus($x, $y);<br /> $sub->(@r);<br /> }<br /> }<br /><br /> # ...<br /><br /> mul sub {<br /> bind_minus(sub {<br /> say sub {}, @_<br /> })->(\@_, \@k)<br /> }, \@i, \@j;<br /></pre><br />Ленивость bind_minus позволяет по ее образу сделать универсальную подпрограмму Bind для связывания функции и продолжения:<br /><pre><br /> sub Bind {<br /> my ($sub, $cont) = @_;<br /> sub {<br /> my @r = $sub->(@_);<br /> $cont->(@r);<br /> }<br /> }<br /><br /> # ...<br /><br /> mul sub {<br /> Bind(\&minus, sub {<br /> say sub {}, @_<br /> })->(\@_, \@k)<br /> }, \@i, \@j<br /></pre><br />В результате, имея подпрограмму Bind, можно вместо специализированных под стиль передачи продолжений подпрограмм использовать обычные функции:<br /><pre><br /> sub mul {<br /> my ($x, $y) = @_;<br /> map { $$x[$_] * $$y[$_] } 0 .. $#$x;<br /> }<br /> <br /> sub minus {<br /> my ($x, $y) = @_;<br /> map { $$x[$_] - $$y[$_] } 0 .. $#$x;<br /> }<br /><br /> sub say {<br /> print join(" ", @_), "\n";<br /> }<br /><br /> my @i = (1, 2, 3);<br /> my @j = (2, 3, 4);<br /> my @k = (3, 4, 5);<br /><br /> sub Bind {<br /> my ($sub, $cont) = @_;<br /> sub {<br /> my @r = $sub->(@_);<br /> $cont->(@r);<br /> }<br /> }<br /><br /> Bind(\&mul, sub {<br /> Bind(\&minus, sub {<br /> Bind(\&say, sub {})->(@_)<br /> })->(\@_, \@k)<br /> })->(\@i, \@j)<br /></pre><br />Кстати, можно даже сделать маленькую <a href="http://laziness-impatience-hubris.blogspot.com/2009/07/tailcall-perl5.html">tailcall оптимизацию</a>:<br /><pre><br /> sub Bind {<br /> my ($sub, $cont) = @_;<br /> sub {<br /> @_ = $sub->(@_);<br /> goto &$cont;<br /> }<br /> }<br /></pre><br />А если превратить подпрограмму Bind в оператор, то это становиться на что-то очень-очень похоже... Неужели на Haskell?<br /><br />Стиль передачи продолжений позволяет симулировать состояния, то есть писать код без изменяемых переменных: лишь абсолютная чистота.<br />Уж не являются операторы связывания и, соответственно, монады Haskell своеобразными обертками для более простого использования стиля передачи продолжений?<br /><br />Если да, то bind и монады в Haskell - это не кусочек императивного мира, а его иллюзия.<br />То есть Haskell един, а не состоит из двух частей: чистой и грязной. Он чист, абсолютно чист, также как и Clean!<br /><br />А как же быть с ленивость? Ведь Haskell не только чист, но и ленив. Что-ж рассуждаем дальше.<br /><br /><b>Порядок для ленивых</b><br /><br /><i>Оператор связывания</i><br /><br />Представим, что нам надо получить из вне две числа и разделить второе на первое:<br /><pre><br /> my @numbers = (3, 6);<br /> sub get_number() {<br /> shift @numbers;<br /> }<br /> <br /> my $x1 = get_number();<br /> my $x2 = get_number();<br /> <br /> sub div($$) {<br /> my ($x2, $x1) = @_;<br /> $x2 / $x1;<br /> }<br /> <br /> print div($x2, $x1);<br /></pre><br />Результат работы вышеприведенного кода - деление 6 (второе число) на 3 (первое число).<br />Подпрограмма get_number имитирует получение чисел из внешнего источника.<br /><br />А теперь добавим ленивость:<br /><pre><br /> my @numbers = (3, 6);<br /> sub get_number() {<br /> sub { shift @numbers };<br /> }<br /> <br /> my $x1 = get_number(); # метка 1<br /> my $x2 = get_number(); # метка 2<br /> <br /> sub div($$) {<br /> my ($x2, $x1) = @_;<br /> $x2->() / $x1->(); # метка 4<br /> }<br /> <br /> print div($x2, $x1); # метка 3<br /></pre><br />В результате получим не 2, а 0.5, то есть числа перепутаны местами.<br />Это произошло потому, что в ленивом языке порядок вычисления определен не потоком программы,<br />а необходимостью в результате конкретного вычисления, или если быть точнее - редукцией графов.<br /><br />В нашем примере, добавив ленивость, мы сделали, что при выполнении программы в метке 1 почти ничего не происходит, и в метка 2 также. А вот в метке 3 требуется все таки вывести результат - программа осознает, что хватит лениться и вызывает функцию div, передав ей ленивые x2 и x1. В функции div (метка 4), уже нужны реальные результаты x2 и x1 - происходит получение данных из внешнего источника. Но поскольку нужен сначала x2, а лишь потом x1, то первое число попадает в x2, а не в x1.<br /><br />Чтобы задать порядок вычисления можно воспользоваться Стилем передачи продолжений -<br />для простоты сразу возьмем вышеупомянутую функцию Bind:<br /><pre><br /> my @numbers = (3, 6);<br /> sub get_number() {<br /> sub { shift @numbers };<br /> }<br /> <br /> sub div($$) {<br /> my ($x2, $x1) = @_;<br /> $x2->() / $x1->();<br /> }<br /> <br /> sub Bind {<br /> my ($sub, $cont) = @_;<br /> sub {<br /> @_ = $sub->(@_);<br /> goto &$cont;<br /> }<br /> }<br /> <br /> Bind(get_number(), sub {<br /> my $x1 = shift;<br /> Bind(get_number(), sub {<br /> my $x2 = shift;<br /> Bind(\&div, sub {<br /> print @_;<br /> # print "$x2/$x1=$_[0]\n"<br /> })->(sub {$x2}, sub {$x1})<br /> })->();<br /> })->();<br /></pre><br />Конечно выглядит ужасно!<br /><br />Но если подпрограммы Bind сделать оператором и упростить запись для анонимных подпрограмм, то все намного лучше:<br /><pre><br /> get_number >>= \x1 -> (get_number >>= \x2 -> (div x2 x1 >>= print))<br /></pre><br />А при использовании do нотации - все просто замечательно:<br /><pre><br /> do x1 <- get_number<br /> x2 <- get_number<br /> r <- div x2 x1<br /> print r<br /></pre><br />Примечание: Haskell не знаю - так что эти две записи наверняка с ошибками.<br /><br /><br /><i>Dataflow переменные</i><br /><br />Альтернативный способ задания порядка - это использование unborned dataflow переменных.<br /><br />Они используется для управления порядком в Mozart-OZ потоках.<br /><br />Им подобны "Уникальные типы" в Clean.<br /><br />В Haskell они просматриваются в руководствах посвещенных монадам:<br /><pre><br /> getChar :: RealWorld -> (Char, RealWorld)<br /> <br /> main :: RealWorld -> ((), RealWorld)<br /> main world0 = let (a, world1) = getChar world0<br /> (b, world2) = getChar world1<br /> in ((), world2)<br /></pre><br />Хотя мне не понятно зачем они тут, ведь все красиво делается при помощи связывания?<br />Конечно, можно предположит, что getChar и прочии IO функции настолько ленивы, что им надо передавать всегда новый RealWorld,<br />но ведь это можно делать за кулисами.<br /><br /><b>Выводы</b><br /><br />Время от времени читал о Haskell, о монадах - никак не мог понять их. Казалось, что одни руководства противоречат другим.<br />И только, недавно, когда плюнул на все эти монады, а просто представил чистый и ленивый язык, сразу стало все на свои места.<br />Нашлось там место и оператору bind, и самим монадам, но не как ключевым фигурам...<br />Остался один вопрос: зачем так все путанном объясняется в Haskell? <br /><br /><br />P.S.<br />После того как была написана эта заметка решил посмотреть подробней на Clean и нашел там подтвержение вышесказаному.<br />Может тем, кто хочет понять Haskell монады, следует рекомендовать сначала почитать как в Clean обходятся без них.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-7498141530276954327.post-45235096643795283552010-12-01T11:04:00.009+02:002010-12-01T11:30:13.505+02:00IPS::MPS, AnyEvent::HTTPD, AnyEvent::HTTP and DBIИгрался на perl связкой <a href="http://search.cpan.org/perldoc?IPC::MPS">IPS::MPS</a>, AnyEvent::HTTPD, AnyEvent::HTTP и DBI.<br />Сделал четыре процесса: главный (управляющий), HTTP сервер, HTTP клиент, DBI клиент.<br />Хотя блокируемый процесс тут один: DBI, но этого поиграться с <a href="/2010/11/ipcmps-message-passing-style-of-inter.html">межпроцессным взаимодействием в стиле передачи сообщений</a> хватит:<br /><pre><br />use IPC::MPS::Event;<br />use AnyEvent::HTTPD;<br />use AnyEvent::HTTP;<br />use DBI;<br /><br />my $port = 9090;<br /><br />print "Please contact me at: http://127.0.0.1:$port/?q=foo\n";<br /><br />my $vpid_server = spawn {<br /><br /> my %url2req; # $url => [$req, ...]<br /><br /> my $httpd = AnyEvent::HTTPD->new(port => $port);<br /><br /> $httpd->reg_cb (<br /> '' => sub {<br /> my ($httpd, $req) = @_;<br /> my $q = $req->parm('q');<br /> if ($q) {<br /> my $url = "http://www.google.com/search?q=$q";<br /> snd(0, "req", $url);<br /> push @{$url2req{$url}}, $req;<br /> } else {<br /> $req->respond([404, 'NOT FOUND']);<br /> }<br /> },<br /> );<br /><br /> receive {<br /> msg res => sub {<br /> my ($from, $url, $data, $headers) = @_;<br /> for my $req (@{$url2req{$url}}) {<br /> $req->respond([200, 'OK', {'Content-Type' => 'text/html'}, $data]);<br /> }<br /> delete $url2req{$url};<br /> };<br /> };<br /><br />};<br /><br /><br />my $vpid_client = spawn {<br /> receive {<br /> msg req => sub {<br /> my ($from, $url) = @_;<br /> http_get $url, sub {<br /> my ($data, $headers) = @_;<br /> snd($from, "res", $url, $data, $headers);<br /> };<br /> };<br /> }<br />};<br /><br /><br />my $vpid_dbi = spawn {<br /><br /> # CREATE DATABASE nick OWNER nick ENCODING 'UTF8';<br /> # CREATE TABLE urls (id_url SERIAL, datetime TIMESTAMP DEFAULT now(), url text, PRIMARY KEY (id_url));<br /> # DROP TABLE urls;<br /> my $data_sourse = "DBI:Pg:dbname=nick;host=localhost";<br /> my $dbh = DBI->connect($data_sourse, "nick", "") or die $DBI::errstr;<br /> my $sth = $dbh->prepare("INSERT INTO urls (url) VALUES (?)") or die $dbh->errstr();<br /><br /> receive {<br /> msg res => sub {<br /> my ($from, $url) = @_;<br /> $sth->execute($url) or die $dbh->errstr();<br /> };<br /> }<br />};<br /><br /><br />receive {<br /> msg req => sub {<br /> my ($from, $url) = @_;<br /> snd($vpid_client, "req", $url);<br /> warn "Q; $url";<br /> };<br /> msg res => sub {<br /> my ($from, $url, $data, $headers) = @_;<br /> snd($vpid_server, "res", $url, $data, $headers);<br /> snd($vpid_dbi, "res", $url);<br /> warn "R; $url";<br /> };<br />};<br /></pre><br />Забавно получается.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-7498141530276954327.post-33005393639968059492010-11-24T14:21:00.000+02:002010-11-24T14:34:39.182+02:00IPC::MPS - Message Passing Style of Inter-process communicationНемного на русском о межпроцессном взаимодействии в стиле передачи сообщений.<br /><br /><a href="http://search.cpan.org/perldoc?IPC::MPS">IPC::MPS</a>, - система обмена сообщениями между родительскими и дочерними процессами, а также между дочерними, имеющими общего родителя.<br /><pre><br /> use IPC::MPS;<br /> <br /> my $vpid = spawn { <br /> receive {<br /> msg ping => sub {<br /> my ($from, $i) = @_;<br /> print "Ping ", $i, " from $from\n";<br /> snd($from, "pong", $i);<br /> };<br /> };<br /> };<br /> <br /> snd($vpid, "ping", 1);<br /> receive { <br /> msg pong => sub {<br /> my ($from, $i) = @_;<br /> print "Pong $i from $from\n";<br /> if ($i < 3) {<br /> snd($from, "ping", $i + 1);<br /> } else {<br /> exit;<br /> }<br /> };<br /> };<br /></pre><br /><b>Concurrency programming</b><br /><br />Особенностью системы является то, что передача сообщений между дочерними процессами осуществляется родительскими. Поэтому рекомендуется использовать родительские процессы лишь для координации рабочих, а также для хранения общей информации.<br /><br />Сообщения передаются посредством UNIX сокетов.<br /><pre><br /> $vpid = spawn {<br /> ...<br /> receive {<br /> msg "name 1" => sub {<br /> my ($from, @args) = @_;<br /> ...<br /> };<br /> msg "name 2" => sub { ... };<br /> msg "name 3" => sub { ... };<br /> ...<br /> };<br /> };<br /></pre><br />Создание дочерних процессов происходит не при вызове spawn, а потом, при receive, непосредственно перед вызовом цикла отправки-приема сообщений. Это необходимо чтобы все vpid были определены до вызовов fork. vpid - адрес ссылки на сокет с главного процесса в дочерний.<br /><br />Внутри spawn можно делать другие spawn. Если spawn делается внутри receive, то надо вызвать и receive, чтобы запустить дочерние процессы. При этом новый receive добавит свою информацию к старой и передаст управление циклу передачи сообщений старого receive.<br /><br />Отправка сообщений.<br /><pre><br /> snd($vpid, "msg name", @args);<br /></pre><br />Если vpid равен 0, то это сообщение родительскому процессу.<br /><br />Если дочерний процесс видит, что родительский завершился, то он также завершается.<br /><br /><b>Dataflow programming</b><br /><br />Иногда при обработке сообщения может возникнуть ситуация, когда необходимо получить дополнительную информацию от других процессов, и лишь затем продолжить обработку сообщения. Для этого можно послать сообщения с запросом информации, а затем в нужном месте дождаться получения информации при помощи подпрограммы wt (сокращение от wait), не прерывая обработку текущего сообщения.<br /><pre><br /> snd("vpid_1", "msg_1", @args_1);<br /> snd("vpid_2", "msg_2", @args_2);<br /> <br /> my $r = wt("vpid_1", "msg_1");<br /> ...<br /> my @r = wt("vpid_2", "msg_2");<br /> ...<br /></pre><br />Подпрограмма wt запускает новый цикл ожидания, продожается отправка еще не отправленных и прием новых сообщений, но новые сообщения не обрабатываются, а накапливаются в буфере. Когда получен ответ на необходимое сообщение, этот цикл ожидания завершается и wt возвращает ответ - обработка текущего сообщения продолжается.<br /><pre><br /> my $r = snd_wt($vpid, $msg, @args);<br /></pre><br />является сокращением для:<br /><pre><br /> snd($vpid, $msg, @args);<br /> my $r = wt($vpid, $msg);<br /></pre><br /><br /><b>The main differences from Erlang</b><br /><br />Внимание, это не Erlang, это Perl IPC::MPS. Основные отличия, вытекающие одно из другого:<br /><br />1. Полноценные процессы операционной системы.<br />2. Подпрограмма spawn непосредственно не создает процессы, а лишь осуществляет подготовительные операции. Процессы создаются при вызове receive.<br />3. "receive" - "многоразовый", а не "одноразовый", как в Erlang.<br />4. "receive" внутри "receive" не заменяет временно собой предыдущий, а добавляет новые обработчики сообщений и запускает новые процессы.<br />5. Чтобы дождаться внутри обработчика сообщения ответ на конкретное сообщение следует использовать подпрограмму wt. В Erlang это делается все тем-же "receive".<br /><br /><b>Распределенное программирование</b><br /><br />Чтобы сделать текущий процесс узлом необходимо вызвать подпрограмму listener:<br /><pre><br /> listener($host, $port);<br /></pre><br />Подключение к уделенному узлу осуществляется при помощи подпрограммы open_node:<br /><pre><br /> my $vpid = open_node($host, $port);<br /></pre><br />Чтобы обнаружить закрытие соединения следует определить обработчик сообщения NODE_CLOSED:<br /><pre><br /> msg NODE_CLOSED => sub { <br /> my ($vpid) = @_;<br /> ...<br /> };<br /></pre><br />Это утверждение справедливо как для клиента, так и для сервера.<br /><br /><b>Совместимость с модулями, основанными на Event, EV и AnyEvent</b><br /><br />IPC::MPS::Event, IPC::MPS::EV позволяют использовать сторонние модули на основе модулей Event и EV соответственно (напрямую или через AnyEvent).<br /><br /><b>P.S.</b><br />Примеры смотрите в каталоге demo.Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-7498141530276954327.post-16767819831762860182010-10-18T11:13:00.002+03:002010-10-19T09:18:03.495+03:00Почему PerlФрагмент из "Распределенное программирование на Perl для домохозяек".<br /><br />Если ваш муж программирует на Java, C#, Python или другом подобном языке, то он обязательно с недоумением спросит: "зачем Perl"? Более того, он скажет, что Perl слишком путанный язык со сложным синтаксисом и перегружен излишними возможностями. В ответ можно попытаться объяснить преимущества многогранности и многообразия, но не стоит. Лучше продемонстрируйте на практике. А для наглядности - на его собственном примере.<br /><br />Поведите мужа в Макдоналдс! Пусть с недельку поест там, а не дома. Да, это жестоко, ведь еда из Макдоналдса, как и любой фастфуд, вредна для здоровья. А разве Макдоналдс-языки программирования менее вредны? Только фастфуд наносит вред телу, а эти языки - разуму, так как человек погрязает в тесных рамках тех же ООП (объектно-ориентированных предрассудков) и не видит многообразие мира за ними.<br /><br />Макдоналдс-языки хороши для массового использования и стандартных ситуаций, а вот если нужно что-то оригинальное... Попросите в Макдоналдсе приготовить вам что-то праздничное ко дню рождения. Наверняка вам из гамбургеров выложат высокую пирамиду, зальют все сверху кетчупом и назовут праздничным тортом. Разве это может сравниться с работой шеф-повара, который даже с простого блюда может сделать шедевр?!<br /><br />Колоссальная гибкость и богатые возможности языка Perl являются его визитной карточкой, его изюминкой. Как шеф-повар делает изысканные блюда из простых ингредиентов, добавив лишь маленькую толику нужных пряностей, так при помощи Perl любую сложную задачу можно решить простым способом. Но тут главное не переборщить - специями можно не только украсить трапезу, но и испортить любой продукт, так и используя мощь Perl, можно решение самой простой задачи сделать чрезвычайно сложным и запутанным.<br /><br />Аналогия со специями абсолютна. Когда специи попали в Европу, они совершили настоящий переворот в кулинарии. Но потом дошло до абсурда - их стали использовать в таком количестве, что невозможно было определить, что за блюдо подано к столу. Затем произошел полный отказ от специй. И лишь сейчас они занимают место, по праву принадлежащее им: ведь никто не отрицает, что чуточка кардамона сделает дрожжевое тесто вкуснее. А волшебное сочетание корицы и печеных яблок! А мускатный орех - да это же король соусов!<br /><br />Конечно, чтобы овладеть мастерством и стать шеф-поваром, необходимо время. Чтобы по настоящему изучить Perl, нужно больше времени, чем потребуется для изучения чего-то попроще. Но оно того стоит.Unknownnoreply@blogger.com4tag:blogger.com,1999:blog-7498141530276954327.post-90492542663241963322010-02-03T11:15:00.000+02:002010-02-03T11:16:11.894+02:00Всякая всячина и книгиПора заканчивать заниматься ерундой и писать всякую фигню o Perl - блог закрыт. Сажусь за написание двух книг.<br /><br />Первая книга будет называется "Событийное программирование на Perl для домохозяек или как успеть приготовить тысячу блюд к приходу гостей".<br /><br />Вторая - "Распределенное программирование на Perl для домохозяек".<br />Эта книга предназначена для тех, кто любому кухонному комбайну предпочитает набор хороших кухонных ножей.<br /><br />Логичней было начать с первой, но вторая сейчас интересней, поэтому есть большая вероятность, что первая будет лишь брошюркой.<br /><br />Здесь будет опубликованы анонсы. До встречи.Unknownnoreply@blogger.com6tag:blogger.com,1999:blog-7498141530276954327.post-55814170094934013642010-01-04T11:26:00.004+02:002010-01-04T11:26:00.189+02:00Если бы в Perl не было бы списков :-)Новогодние каникулы продолжаются...<br />Иногда в праздничном веселье, как нечто далекое, мелькают мысли: кто я, что я делал, perl какой-то...<br /><br /><span style="font-weight:bold;">Создание списка</span><br /><pre><br />sub list($;$) {<br /> my ($h, $t) = @_;<br /> return sub {<br /> return $h, $t;<br /> }<br />}<br /><br />my $x1 = list(1);<br />my $x2 = list(2, $x1);<br />my $x3 = list(3, $x2);<br /></pre><br /><span style="font-weight:bold;">Печать списка</span><br /><pre><br />sub print_list($);<br />sub print_list($) {<br /> my ($list) = @_;<br /> my ($h, $t) = $list->();<br /> print_list($t) if $t;<br /> print "$h ";<br />}<br /><br />print_list $x3;<br />print "\n";<br /></pre><br /><span style="font-weight:bold;">map</span><br /><pre><br />sub _Map {<br /> my ($sub, $list) = @_;<br /> my ($h, $t) = $list->();<br /> my $r = $t ? _Map($sub, $t) : undef;<br /> list($sub->($h), $r);<br />}<br />sub Map(&;$) { &_Map }<br /><br />print_list Map { $_[0]**$_[0] } $x3;<br />print "\n";<br /></pre>Unknownnoreply@blogger.com2tag:blogger.com,1999:blog-7498141530276954327.post-46943397894034741272009-12-28T09:26:00.000+02:002009-12-28T09:26:00.215+02:00Open, каналы и ожидание завершения потомкаТрудно искать черную кошку в темной комнате, особенно, если ее там нет.<br />Но как оказалось, еще трудней не замечать черную кошку, сидящую на видном месте в ярко освещенной комнате!<br /><br />Так вот и я потратил приличную часть времени, заметив, что в коде, упрошенном до нижеследующего, родительский процесс ожидает завершения потомка:<br /><pre><br /> my $child_sub = sub { sleep };<br /><br /> my $child_pid = open my $fh, "-|";<br /> defined $child_pid or die "Can't fork: $!";<br /> <br /> if ($child_pid) {<br /> # Родитель.<br /> } else {<br /> # Ребенок.<br /> $child_sub->();<br /> exit;<br /> }<br /></pre><br />Впредь, если для дочернего процесса может истечь тайм-аут, буду использовать вызов pipe:<br /><pre><br /> my $child_sub = sub { sleep };<br /><br /> pipe my $from_child_fh, my $to_parent_fh or die "pipe: $!";<br /> <br /> my $child_pid = fork;<br /> defined $child_pid or die "Can't fork: $!";<br /> <br /> if ($child_pid) {<br /> # Родитель.<br /> close $to_parent_fh;<br /> } else {<br /> # Ребенок.<br /> close $from_child_fh;<br /> $child_sub->();<br /> exit;<br /> }<br /></pre><br />Или буду использовать socketpair.<br /><br />С другой стороны, вред от первого варианта на практике всегда отсутствует, наверно поэтому и не замечал этого явления на протяжении многих лет.Unknownnoreply@blogger.com0