Сравнение производительности сетевых библиотек в Perl

Когда задумал сделать парсер тИЦ для LJ Add/Remove Fiends — решил для начала проверить, какой способ загрузки страниц с показателями тИЦ из скрипта окажется быстрее, поскольку это в любом случае окажется самым узким местом в производительности парсера (любая обработка уже полученных данных, если она сделана без грубых ошибок типа бесконечного цикла, происходит гораздо быстрее загрузки этих данных по сети).

Для начала проверил на локальной машине (первый тест), затем на сервере (второй тест). Воспользовался модулем Benchmark::Timer, по сути он ставит метки начала и конца, после чего вычисляет разницу между ними (то же самое несложно получить с использованием Time::HiRes). У Benchmark::Timer правда есть дополнительная возможность — он может гонять тест до получения результата с заданной обеспеченностью и заданным процентом ошибки.

Третьим тестом, уже из чистого любопытства (поскольку результат был предсказуем) был тест многопоточной загрузки. Естественно этот вариант оказался быстрее любого из однопоточных, в среднем в два раза (при 15 потоках).

Выводы — библиотеки Perl не только выгодней с точки зрения безопасности, но и работают стабильно быстрее вызовов системных утилит. Самую медленную загрузку показал Wget, основывать парсер на нём — явно плохая идея. Многопточность естественно вне конкуренции. Подробности ниже.

Объекты и условия тестирования

  • Модули Perl:
    • LWP::UserAgent 6.02
    • LWP::Curl 0.07-1
    • WWW::Curl::Easy 4.15
    • LWP::Parallel::UserAgent 2.57 (15 потоков)
  • Внешние загрузчики:
    • Wget 1.12
    • Curl 7.21.3

На локальной машине каждым способом загружалось по 200 страниц, с сервера — 100. Тесты выполнял по 5 раз, результаты усреднил (меня интересовали отношения результатов между собой, а не абсолютные значения). Ссылки передавались скрипту из файла, содержащего запрос тИЦ для 400 различных адресов, например:
http://bar-navig.yandex.ru/u?ver=2&show=32&url=http://nadonenado.livejournal.com

Результаты тестирования скорости загрузки страниц в Perl

200 запросов с локальной машины:
tail -n 200 www-get-speed-data.txt | perl www-get-speed.pl
Trails:
200
LWP-UA 9.523s total 47.615 ms/trial
LWP-Curl 8.500s total 42.499 ms/trial
Wget 1.12 qx{} 15.392s total 76.958 ms/trial
WWW-Curl-Easy 9.081s total 45.405 ms/trial
Curl 7.21.3 qx{} 13.340s total 66.698 ms/trial
100 запросов с сервера (США):
tail -n 100 www-get-speed-data.txt | perl www-get-speed.pl
Trails:
100
LWP-UA 42.861s total 428.611 ms/trial
LWP-Curl 41.762s total 417.615 ms/trial
Wget 1.10.2 qx{} 46.554s total 465.543 ms/trial
WWW-Curl-Easy 40.748s total 407.476 ms/trial
Curl 7.20.0 qx{}

45.750s total),

457.500 ms/trial
Параллельные запросы с сервера (100, по 15 одновременных):
tail -n 100 www-get-speed-data.txt | perl lwp-parallel-ua.pl
100 trial LWP-PUA 22.967s total 229,670 ms/trial

Код тестов для проверки скорости выгрузки страниц на Perl

Загрузка страниц из Perl в один поток (LWP, CURL, Wget)

Спойлер

[cc lang=»perl» escaped=»true»]
#!/usr/bin/perl
use warnings;
use strict;
use utf8;
use Benchmark::Timer;
# проверим производительность разных модулей
use LWP::UserAgent;
use LWP::Curl;
use WWW::Curl::Easy;
# + wget, curl через системный вызов
my $agent = q{‘Mozilla/5.0 (Windows; Windows NT 6.1; rv:2.0) Gecko’};
my $wget = ‘/usr/bin/wget -q -O /dev/null’.» —user-agent=$agent «;
my $curl = ‘/usr/bin/curl —silent —output /dev/null’.» —user-agent $agent «;

# тестовые ссылки получаем из станд. ввода
my @tst_url = <STDIN>;

my $timer = Benchmark::Timer->new(skip => 0);
my $lwp_ua = set_lwp_ua();
my $curl_ua = set_curl_ua();
my $lwp_curl = set_lwp_curl_ua();

foreach (@tst_url) {
chomp;
tr/_ /-/;

$timer->start(‘LWP-UA’);
$lwp_ua->get($_);
$timer->stop(‘LWP-UA’);
}
print $timer->report;

foreach (@tst_url) {
chomp;
tr/_ /-/;

$timer->start(‘LWP-Curl’);
$lwp_curl->get($_);
$timer->stop(‘LWP-Curl’);
}
print $timer->report;

foreach (@tst_url) {
chomp;
tr/_ /-/;

$timer->start(‘Wget 1.12 system’);
qx{$wget $_};
$timer->stop(‘Wget 1.12 system’);
}
print $timer->report;

foreach (@tst_url) {
chomp;
tr/_ /-/;

$timer->start(‘WWW-Curl-Easy’);
$curl_ua->setopt(CURLOPT_URL, $_);
$curl_ua->perform;
$timer->stop(‘WWW-Curl-Easy’);
}
print $timer->report;

foreach (@tst_url) {
chomp;
tr/_ /-/;

$timer->start(‘Curl system’);
qx{$curl $_};
$timer->stop(‘Curl system’);
}
print $timer->report;

exit 0;

sub set_lwp_curl_ua {
my $ua = LWP::Curl->new(
headers => 1, # возвращать заголовки
user_agent => $agent,
followlocation => 0, # отключить обработку редиректов
auto_encode => 1, # url encode
);
return $ua;
}

sub set_curl_ua {

my $ua = WWW::Curl::Easy->new;
$ua->setopt(CURLOPT_HEADER, 1); # возвращать ответ вместе с заголовком
$ua->setopt(CURLOPT_USERAGENT, $agent);
#$ua->setopt(CURLOPT_AUTOREFERER, 1);
$ua->setopt(CURLOPT_CRLF, 1);
$ua->setopt(CURLOPT_ENCODING, ‘deflate, gzip, identity’);
my $null = ‘/dev/null’;
$ua->setopt(CURLOPT_WRITEDATA, \$null);

return $ua;
}

sub set_lwp_ua {

my $ua = LWP::UserAgent->new;
$ua->agent($agent);
$ua->default_header(
‘Accept’ => ‘text/html, application/xml;q=0.9, application/xhtml+xml;q=0.1’,
‘Accept-Charset’ => ‘utf-8; *;q=0.1’,
‘Accept-Language’ => ‘ru,en-us;q=0.7,en;q=0.3’,
‘Accept-Encoding’ => ‘deflate, gzip, x-gzip, identity, *;q=0’,
);
return $ua;
}
[/cc]

[свернуть]

Многопоточная загрузка страниц из Perl (15 потоков)

Спойлер

[cc lang=»perl» escaped=»true» nowrap=»true»]
#!/usr/bin/perl
use warnings;
use strict;
use utf8;
use Benchmark::Timer;
use HTTP::Request;
# проверим производительность разных модулей
use LWP::Parallel::UserAgent;

my $agent = q{‘Mozilla/5.0 (Windows; Windows NT 6.1; rv:2.0) Gecko’};

# тестовые ссылки получаем из станд. ввода
my @tst_url = <STDIN>;

my $timer = Benchmark::Timer->new(skip => 0,
#confidence => 97.5,
#error => 2,
);

my $pua = LWP::Parallel::UserAgent->new();
$pua->agent($agent);
$pua->default_header(
‘Accept’ => ‘text/html, application/xml;q=0.9, application/xhtml+xml;q=0.1’,
‘Accept-Charset’ => ‘utf-8; *;q=0.1’,
‘Accept-Language’ => ‘ru,en-us;q=0.7,en;q=0.3’,
‘Accept-Encoding’ => ‘deflate, gzip, x-gzip, identity, *;q=0’,
);
$pua->in_order (1); # handle requests in order of registration
$pua->duplicates(0); # ignore duplicates
$pua->timeout (2); # in seconds
$pua->redirect (0); # follow redirects
$pua->max_req (15); # max parallel requests per server (def 5)

$timer->start(‘LWP-PUA’);

foreach ( @tst_url ) {
shift;
chomp;
tr/_ /-/;

my $req = HTTP::Request->new(‘GET’, $_);

if ( my $res = $pua->register($req) ) {
print STDERR $res->error_as_HTML;
}
}

# отправка запросов в указанном числе потоков,
# аргумент wait задаёт макс. время ожидания ответа
my $entries = $pua->wait();

$timer->stop(‘LWP-PUA’);
print $timer->report;

exit 0;
[/cc]

[свернуть]

4 мысли о “Сравнение производительности сетевых библиотек в Perl”

  1. Меня сильно беспокоят ваши результаты, особенно касающиеся wget. По идее при загрузке намного важнее скорость соединения, а не используемая качалка. Возможное объяснение — долгая инициализация wget’а при каждом запуске. Возможно, следовало передать файл с url и использовать ключ -i.

    1. Может на «ты»? Я-то у тебя не на «вы» обращаюсь 🙂

      Тогда условия не будут единообразны для всех. Меня интересовала именно загрузка URL, переданных из скрипта.

      1. По поводу на «вы» — не обращайте внимания. Это привычка со студеньческих времен, когда все в инете казались взрослыми бородатыми дядьками 🙂

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

        1. Есть предложения по уточнению теста? Как обеспечить им всем одинаковые условия? Просто на данный момент ситуация такова — требовалось выяснить, что в конкретных условиях позволит быстрее загружать ссылки. Без дополнительных ухищрений, штатными средствами — результат получен.
          Если есть способ ускорить wget — я буду только рад его узнать (но с сохранением вводной — получать ссылки так, как это происходит сейчас).

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *