2016-09-10 3 views
3

Я слишком долго использовал предупреждения; и строгий; в Perl, но теперь я это сделал, я вижу преимущества.Где определить локальные временные переменные в подпрограмме Perl?

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

Так (вырезания код для вычисления вещи) Мне интересно, если

sub doStuff 
{ 
    my $temp; 
    for my $x (1..50) 
    { 
    $temp = $x**2; 
    } 
    for my $x (1..50) 
    { 
    $temp = $x**3; 
    } 
} 

Или

sub doStuff 
{ 
    for my $x (1..50) 
    { 
    my $temp = $x**2; 
    } 
    for my $x (1..50) 
    { 
    my $temp = $x**3; 
    } 
} 

Менее/более эффективными, или если один нарушает некоторые Perl кодирования я не знаю все же.

ответ

6

Эффективность между этими двумя объектами достаточно близка, и это затмевается любой реалистичной обработкой.Поэтому я бы пошел по коду –, если $tmp действительно временный и ненужный после цикла, тогда лучше сохранить его внутри (область действия) по всем другим причинам.

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

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

Просьба сравнить, как пример базового использования основного модуля Benchmark. Я делаю дополнительную операцию и добавляю другие случаи, когда нет временных.

use warnings 'all'; 
use strict;  
use Benchmark qw(cmpthese); 

my $x; 

sub tmp_in { 
    for (1..10_000) { 
     my $tmp = 2 * $_; 
     $x = $tmp + $_; 
    } 
    return $x; 
} 

sub tmp_out { 
    my $tmp; 
    for (1..10_000) { 
     $tmp = 2 * $_; 
     $x = $tmp + $_; 
    } 
    return $x; 
} 

sub no_tmp { 
    for (1..10_000) { $x = 2 * $_ + $_ } 
    return $x; 
} 

sub base { 
    for (1..10_000) { $x += $_ } 
    return $x; 
} 

sub calc { 
    for (1..10_000) { $x += sin sqrt(rand()) } 
    return $x; 
}   

cmpthese(-10, { 
    tmp_in => sub { tmp_in }, 
    tmp_out => sub { tmp_out }, 
    no_tmp => sub { no_tmp }, 
    base => sub { base },   
    calc => sub { calc }, 
}); 

выход (на v5.16)

 
      Rate calc tmp_in tmp_out no_tmp base 
calc  623/s  -- -11% -26% -44% -59% 
tmp_in 698/s  12%  -- -17% -37% -54% 
tmp_out 838/s  34%  20%  -- -25% -44% 
no_tmp 1117/s  79%  60%  33%  -- -26% 
base 1510/s 142% 116%  80%  35%  -- 

Таким образом, они отличаются, и, видимо, декларация расходов цикла. Но версии tmp находятся в списке. Кроме того, это часто просто накладные расходы, поэтому он сильно преувеличен. И есть и другие аспекты: – no_tmp работает в одном из утверждений, например. Эти вещи могут иметь значение только в том случае, если ваша обработка является главным образом итерациями. Просто генерировать (высокое качество) псевдослучайное число дорого.

Это может также отличаться (дико) на разных версиях аппаратного и программного обеспечения. Мои результаты с v5.10 на более качественной машине немного разные. Замените образец «расчеты» на вашу обработку и запустите на самом аппаратном обеспечении для соответствующей меры, независимо от того, имеет ли это значение вообще.

+0

Просто для полноты: я попробовал свой код и добавил измененную версию 'no_tmp()', которая также выполняет умножение (как и остальные), т. Е. Вместо вашего '$ x + = $ _;' Я сделал '$ x = 2 * $ _ + $ _; ', так что все подпрограммы дают тот же результат. Из-за умножения мой 'no_tmp_mod()' медленнее вашего и ближе к 'tmp_out()'. [См. Hastebin] (http://hastebin.com/hubewisehi.pl). Но, как вы уже сказали, реальная разница будет получена от фактического расчета, а не от _in_ vs. _out_. – PerlDuck

+0

@PerlDog Да, это другое, спасибо за комментарий. Я хотел добавить добавление «ссылки», которое не то же самое, но я должен добавить больше, вы правы. Кроме того, есть проблемы с оптимизацией '$ tmp' и т. Д. Это должно было быть базовым образцом для OP и простой демонстрацией. Я планирую улучшить, поскольку получаю немного времени. Спасибо – zdim

+0

@PerlDog Наконец-то обошел его и обновил, как и планировалось. Спасибо за комментарий! – zdim

4

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

Также premature optimization является anti-pattern

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

+0

Хотя прямое «спасибо» - это не то, о чем комментируют, я хочу сказать - спасибо за объяснение, а также даю мне облегчение, когда я оптимизировал свои собственные проекты. Часто, когда я нахожу оптимизацию, я говорю «почему я этого не видел раньше», но хорошо знать, что это не то, что я должен искать, за исключением случаев экстремального замедления. – aschultz

2

Если вы:

for my $x (1 .. 50){ 
    my $temp = $x**2; 
}# $temp goes out of scope 

$ температура будет вне сферы после того, как цикл. Это в основном означает, что он больше не существует.

Если вы:

my $temp; 
for my $x (1 .. 50){ 

    $temp = $x**2; 

}# $temp is now 50**2; 

$ температура будет в рамках подпрограммы, и может быть изменен или возвращен позже в суб.

Посмотрите на variable scoping in perl, чтобы узнать больше.

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

Если вам нужна дополнительная скорость, рассмотрите возможность обновления perl - например, perl 5.24 имеет некоторые хорошие показатели производительности, которые значительно перевесят некоторые сохраненные переменные.

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

Смежные вопросы