Это быстрее, чтобы дать печати Perl список или объединенную строку? - PullRequest
1 голос
/ 17 сентября 2009

вариант A:

print $fh $hr->{'something'}, "|", $hr->{'somethingelse'}, "\n";

вариант B:

print $fh $hr->{'something'} . "|" . $hr->{'somethingelse'} . "\n";

Ответы [ 6 ]

13 голосов
/ 18 сентября 2009

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

Если мы собираемся тратить на это время, давайте хотя бы сделаем это правильно. Ниже приведен код для создания реалистичного теста. Он на самом деле печатает и отправляет информацию о сравнительном анализе в STDERR. Вы запускаете его как perl benchmark.plx > /dev/null, чтобы вывод не затоплял ваш экран.

Вот 5 миллионов итераций, пишущих в STDOUT. Используя timethese() и cmpthese(), мы получаем все данные для сравнения.

$ perl ~/tmp/bench.plx 5000000 > /dev/null
Benchmark: timing 5000000 iterations of concat, list...
    concat:  3 wallclock secs ( 3.84 usr +  0.12 sys =  3.96 CPU) @ 1262626.26/s (n=5000000)
      list:  4 wallclock secs ( 3.57 usr +  0.12 sys =  3.69 CPU) @ 1355013.55/s (n=5000000)
            Rate concat   list
concat 1262626/s     --    -7%
list   1355014/s     7%     --

А вот 5 миллионов записей во временный файл

$ perl ~/tmp/bench.plx 5000000
Benchmark: timing 5000000 iterations of concat, list...
    concat:  6 wallclock secs ( 3.94 usr +  1.05 sys =  4.99 CPU) @ 1002004.01/s (n=5000000)
      list:  7 wallclock secs ( 3.64 usr +  1.06 sys =  4.70 CPU) @ 1063829.79/s (n=5000000)
            Rate concat   list
concat 1002004/s     --    -6%
list   1063830/s     6%     --

Обратите внимание на дополнительный настенный час и системное время, подчеркивая, как то, что вы печатаете, до имеет такое же значение, как и то, что вы печатаете.

Версия списка примерно на 5% быстрее (обратите внимание, что это противоречит логике Павла, подчеркивающей тщетность попыток просто продумать это). Вы сказали, что делаете десятки тысяч таких? Давайте посмотрим ... 100k занимает 146 мс времени на моем ноутбуке (который имеет дерьмовый ввод / вывод), так что лучшее, что вы можете здесь сделать, это сбрить около 7 мс. Поздравляю. Если вы потратили хотя бы минуту на размышления об этом, вам понадобится 40 000 итераций этого кода, прежде чем вы это сделаете. Это не говоря о альтернативных затратах, в эту минуту вы могли бы оптимизировать что-то гораздо более важное.

Теперь, кто-то скажет: «теперь, когда мы знаем, какой путь быстрее, мы должны написать его быстрым способом и сэкономить это время в каждой программе, которую мы пишем, чтобы сделать все упражнение стоящим!» Нет. Это по-прежнему будет составлять незначительную часть времени выполнения вашей программы, гораздо меньше, чем 5%, которые вы получаете, измеряя один оператор. Во-вторых, подобная логика заставляет вас расставлять приоритеты микрооптимизации над обслуживаемостью.

Да, и отличается в 5.8.8 от 5.10.0.

$ perl5.8.8 ~/tmp/bench.plx 5000000 > /dev/null
Benchmark: timing 5000000 iterations of concat, list...
    concat:  3 wallclock secs ( 3.69 usr +  0.04 sys =  3.73 CPU) @ 1340482.57/s (n=5000000)
      list:  5 wallclock secs ( 3.97 usr +  0.06 sys =  4.03 CPU) @ 1240694.79/s (n=5000000)
            Rate   list concat
list   1240695/s     --    -7%
concat 1340483/s     8%     --

Может даже измениться в зависимости от того, какой уровень ввода / вывода Perl вы используете и какой операционной системы. Так что все упражнение бесполезно.

Микрооптимизация - дурацкая игра. Всегда делайте профиль первым и обращайтесь к оптимизации вашего алгоритма. Devel :: NYTProf - отличный профилировщик.

#!/usr/bin/perl -w

use strict;
use warnings;
use Benchmark qw(timethese cmpthese);

#open my $fh, ">", "/tmp/test.out" or die $!;
#open my $fh, ">", "/dev/null" or die $!;
my $fh = *STDOUT;
my $hash = {
    foo => "something and stuff",
    bar => "and some other stuff"
};

select *STDERR;
my $r = timethese(shift || -3, {
    list => sub {
        print $fh $hash->{foo}, "|", $hash->{bar};
    },
    concat => sub {
        print $fh $hash->{foo}. "|". $hash->{bar};
    },
});
cmpthese($r);
13 голосов
/ 17 сентября 2009

Если вы не выполняете миллионы этих утверждений, разница в производительности не будет иметь значения. Я действительно предлагаю сконцентрироваться на проблемах с производительностью там, где они существуют - и единственный способ выяснить это - профилировать ваше приложение.

Преждевременная оптимизация - это то, над чем Джоэл и Джефф имели подкаст и жаловались годами. Это просто пустая трата времени, чтобы попытаться что-то оптимизировать, пока вы не ЗНАЕТЕ, что это медленно.

4 голосов
/ 17 сентября 2009

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

Если вас беспокоит скорость ввода-вывода, есть еще много интересных и полезных вещей, которые нужно настроить, прежде чем начинать беспокоиться о запятых и точках. См., Например, обсуждение в разделе Тайна скорости записи Perl .

3 голосов
/ 17 сентября 2009

Вы действительно пытались это профилировать? Это займет всего несколько секунд.

На моей машине кажется, что B быстрее. Однако вам стоит взглянуть на Анализ Парето . Вы уже потратили впустую гораздо больше времени на размышления над этим вопросом, чем когда-либо экономили на любом запуске программы. Для таких простых проблем, как эта (замена символов!), Вам следует подождать, пока не возникнет проблема.

3 голосов
/ 17 сентября 2009

UPDATE : Я только что провела свой собственный тест.

1 000 000 итераций каждой версии занимали каждую <1 секунду. </p>

10-миллиметровые итерации каждой версии занимали в среднем 2,35 секунды для версии списка по сравнению с 2,1 секунды для версии Concat строки

0 голосов
/ 17 сентября 2009

Из трех вариантов я бы, вероятно, сначала выбрал интерполяцию строк и переключился на запятую для выражений, которые нельзя интерполировать. Это, как ни странно, означает, что мой выбор по умолчанию - самый медленный из всех, но, учитывая, что все они настолько близки по скорости и что скорость диска, вероятно, будет медленнее, чем что-либо еще, я не верю, что изменение у этого метода есть какие-то реальные преимущества в производительности.

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

Вы также можете прочитать perldoc perlperf

           Rate string concat  comma
string 803887/s     --    -0%    -7%
concat 803888/s     0%     --    -7%
comma  865570/s     8%     8%     --
#!/usr/bin/perl

use strict;
use warnings;

use Carp;
use List::Util qw/first/;
use Benchmark;

sub benchmark {
    my $subs = shift;

    my ($k, $sub) = each %$subs;
    my $value = $sub->();
    croak "bad" if first { $value ne $_->() and print "$value\n", $_->(), "\n" } values %$subs;

    Benchmark::cmpthese -1, $subs;
}

sub fake_print {
    #this is, plus writing output to the screen is what print does
    no warnings;
    my $output = join $,, @_;
    return $output;
}

my ($x, $y) = ("a", "b");
benchmark {
    comma  => sub { return fake_print $x, "|", $y, "\n"     },
    concat => sub { return fake_print $x .  "|" . $y . "\n" },
    string => sub { return fake_print "$x|$y\n"             },
};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...