$ _ более эффективен, чем именованная переменная в foreach Perl?

Вы можете указать порог и написать функцию, которая оценивает и устанавливает DateTime в соответствии с ним

var threshold = 15; //minutes

var dt = DateTime.Now.AddMinutes(-20);
//var dt = DateTime.Now;
DateTime resultDt;

DateTime thUp = dt.AddMinutes(15);
DateTime thDown = dt.AddMinutes(-15);

if (thUp.Hour != dt.Hour)
    resultDt = new DateTime(dt.Year, dt.Month, dt.Day, thUp.Hour, 0, 0);
else if (thDown.Hour != dt.Hour)
    resultDt = new DateTime(dt.Year, dt.Month, dt.Day, dt.Hour, 0, 0);
else 
    resultDt = dt;

Fiddle

8
задан Brad Gilbert 11 August 2009 в 05:33
поделиться

8 ответов

Даже знайте, что Преждевременная оптимизация является корнем всего зла

{
  local $\ = "\n";
  print foreach @numbers;
}

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

#!/usr/bin/env perl
use strict;
use warnings;
use Benchmark qw(:all :hireswallclock);

use constant Numbers => 10000;

my @numbers = (1 .. Numbers);

sub no_out (&) {
    local *STDOUT;
    open STDOUT, '>', '/dev/null';
    my $result  = shift()->();
    close STDOUT;
    return $result;
};

my %tests = (
    loop1 => sub {
        foreach my $current (@numbers) {
            print "$current\n";
        }
    },
    loop2 => sub {
        foreach (@numbers) {
            print "$_\n";
        }

    },
    loop3 => sub {
        local $\ = "\n";
        print foreach @numbers;
        }
);

sub permutations {
    return [
        map {
            my $a = $_;
            my @f = grep {$a ne $_} @_;
            map { [$a, @$_] } @{ permutations( @f ) }
            } @_
        ]
        if @_;
    return [[]];
}

foreach my $p ( @{ permutations( keys %tests ) } ) {
    my $result = {
        map {
            $_ => no_out { sleep 1; countit( 2, $tests{$_} ) }
            } @$p
    };

    cmpthese($result);
}

Можно ожидать, что loop2 должен быть быстрее, чем loop1

       Rate loop2 loop1 loop3
loop2 322/s    --   -2%  -34%
loop1 328/s    2%    --  -33%
loop3 486/s   51%   48%    --
       Rate loop2 loop1 loop3
loop2 322/s    --   -0%  -34%
loop1 323/s    0%    --  -34%
loop3 486/s   51%   50%    --
       Rate loop2 loop1 loop3
loop2 323/s    --   -0%  -33%
loop1 324/s    0%    --  -33%
loop3 484/s   50%   49%    --
       Rate loop2 loop1 loop3
loop2 317/s    --   -3%  -35%
loop1 328/s    3%    --  -33%
loop3 488/s   54%   49%    --
       Rate loop2 loop1 loop3
loop2 323/s    --   -2%  -34%
loop1 329/s    2%    --  -33%
loop3 489/s   51%   49%    --
       Rate loop2 loop1 loop3
loop2 325/s    --   -1%  -33%
loop1 329/s    1%    --  -32%
loop3 488/s   50%   48%    --

Иногда я последовательно наблюдал loop1 приблизительно на 15%-20% быстрее, чем loop2 но я не могу определить почему.

Я наблюдался сгенерированный байт-код для loop1 и loop2 и существует различие только один при создании my переменная. Эта переменная внутренняя часть не выделяется и также не копируется таким образом, эта операция является очень дешевой. Различие прибывает, я думаю только от "$_\n" создайте, который не является дешевым. Эти циклы должны быть очень похожими

for (@numbers) {
  ...
}

for my $a (@numbers) {
  ...
}

но этот цикл является более дорогим

for (@numbers) {
  my $a = $_;
  ...
}

и также

print "$a\n";

является более дорогим, чем

print $a, "\n";
11
ответ дан 3 November 2019 в 12:28
поделиться

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

"Преждевременная оптимизация является корнем всего зла" [1]

[1] Knuth, Donald. Структурное программирование с движением к Операторам, Журнал ACM, Вычисляя Обзоры, Vol 6, № 4, декабрь 1974. p.268.

14
ответ дан 3 November 2019 в 12:28
поделиться

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

6
ответ дан 3 November 2019 в 12:28
поделиться

Сравнительный тест:

use Benchmark qw(timethese cmpthese);

my $iterations = 500000;     

cmpthese( $iterations,
  {
    'Loop 1' => 'my @numbers = (1,3,5,7,9);
    foreach my $current (@numbers)
    {
      print "$current\n";
    }', 

    'Loop 2' => 'my @numbers = (1,3,5,7,9);
    foreach (@numbers)
    {
      print "$_\n";
    }'
  }
);

Вывод:

         Rate     Loop 2 Loop 1
Loop 2  23375/s     --    -1%
Loop 1  23546/s     1%     --

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

6
ответ дан 3 November 2019 в 12:28
поделиться

Я не знаю, но... хорошо, в первую очередь, Вы сохраняете переменное присвоение во второй версии цикла. Я могу предположить, что, так как $ _ используется очень часто, он должен быть так или иначе оптимизирован. Вы могли попытаться представить его, очень хороший профилировщик Perl является NYTProf 2, записанным Tim Bunce.

Затем действительно достойно оптимизировать это мелочи? Я не думаю, что цикл будет иметь значение. Я предлагаю, чтобы Вы использовали профилировщика, чтобы измерить Ваш уровень и определить реальные узкие места. Обычно проблемы скорости расположены в 10% кода, который работает, 90% времени (возможно, не будет 10-90, но это - "известное" отношение :P).

0
ответ дан 3 November 2019 в 12:28
поделиться

Используя $_ идиома Perl, которая показывает закаленному программисту, что "текущий контекст" используется. Кроме того, много функций берут $_ по умолчанию как параметр, таким образом делая код более кратким.

Некоторые могли бы также просто спорить, это, "было трудно записать, должно быть трудно читать".

1
ответ дан 3 November 2019 в 12:28
поделиться

Я более интересующийся общим представлением использовать $ _ вместо печати...

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

2
ответ дан 3 November 2019 в 12:28
поделиться

Выполнение двух опций через " perl -MO = Concise, -terse, -src test.pl ", дает следующие два дерева операций:

для моего $ n (@num) {...}

LISTOP (0x9c08ea0) leave [1] 
    OP (0x9bad5e8) enter 
# 5: my @num = 1..9;
    COP (0x9b89668) nextstate 
    BINOP (0x9b86210) aassign [4] 
        UNOP (0x9bacfa0) null [142] 
            OP (0x9b905e0) pushmark 
            UNOP (0x9bad5c8) rv2av 
                SVOP (0x9bacf80) const [5] AV (0x9bd81b0) 
        UNOP (0x9b895c0) null [142] 
            OP (0x9bd95f8) pushmark 
            OP (0x9b4b020) padav [1] 
# 6: for my $n (@num){
    COP (0x9bd12a0) nextstate 
    BINOP (0x9c08b48) leaveloop 
        LOOP (0x9b1e820) enteriter [6] 
            OP (0x9b1e808) null [3] 
            UNOP (0x9bd1188) null [142] 
                OP (0x9bb5ab0) pushmark 
                OP (0x9b8c278) padav [1] 
        UNOP (0x9bdc290) null 
            LOGOP (0x9bdc2b0) and 
                OP (0x9b1e458) iter 
                LISTOP (0x9b859b8) lineseq 
# 7:   say $n;
                    COP (0x9be4f18) nextstate 
                    LISTOP (0x9b277c0) say 
                        OP (0x9c0edd0) pushmark 
                        OP (0x9bda658) padsv [6] # <===
                    OP (0x9b8a2f8) unstack 

для (@num) {...}

LISTOP (0x8cdbea0) leave [1] 
    OP (0x8c805e8) enter 
# 5: my @num = 1..9;
    COP (0x8c5c668) nextstate 
    BINOP (0x8c59210) aassign [4] 
        UNOP (0x8c7ffa0) null [142] 
            OP (0x8ccc1f0) pushmark 
            UNOP (0x8c805c8) rv2av 
                SVOP (0x8c7ff80) const [7] AV (0x8cab1b0) 
        UNOP (0x8c5c5c0) null [142] 
            OP (0x8cac5f8) pushmark 
            OP (0x8c5f278) padav [1] 
# 6: for (@num){
    COP (0x8cb7f18) nextstate 
    BINOP (0x8ce1de8) leaveloop 
        LOOP (0x8bf1820) enteriter 
            OP (0x8bf1458) null [3] 
            UNOP (0x8caf2b0) null [142] 
                OP (0x8bf1808) pushmark 
                OP (0x8c88ab0) padav [1] 
            PADOP (0x8ca4188) gv  GV (0x8bd7810) *_ # <===
        UNOP (0x8cdbb48) null 
            LOGOP (0x8caf290) and 
                OP (0x8ce1dd0) iter 
                LISTOP (0x8c62aa8) lineseq 
# 7:   say $_;
                    COP (0x8cade88) nextstate 
                    LISTOP (0x8bf12d0) say 
                        OP (0x8cad658) pushmark 
                        UNOP (0x8c589b8) null [15] # <===
                            PADOP (0x8bfa7c0) gvsv  GV (0x8bd7810) *_ # <===
                    OP (0x8bf9a10) unstack 

Я добавил" <= == ", чтобы обозначить различия между ними.

Если вы заметили, на самом деле больше операций для версии" для (@num) {...} ".

Так что, во всяком случае, версия « для (@num) {...} », вероятно, медленнее, чем « для моей версии $ n (@num) {...} ».

2
ответ дан 3 November 2019 в 12:28
поделиться
Другие вопросы по тегам:

Похожие вопросы: