Условия в циклах Perl и производительность - PullRequest
1 голос
/ 01 июля 2019

В Perl существует множество идиом с использованием операторов / функций / подпрограмм / методов в условиях цикла. Книги советуют использовать их! Но, как я понимаю, эти условия рассчитываются для каждой итерации. Я прав?

Perl 5:

foreach my $key (keys %hash) { ... }
for my $value (values %hash) { ... }

Perl 6:

for 'words.txt'.IO.lines -> $line { ... }
while $index < $fruit.chars { ... }

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

my @array = keys %hash;
foreach my $keys (@array) { ... } 

Ответы [ 2 ]

3 голосов
/ 01 июля 2019
foreach my $key (keys %hash) { ... }
for my $value (values %hash) { ... }

for и foreach являются синонимами в Perl, поэтому, несмотря на тот факт, что ваши два примера фрагментов работают с разными частями хэша, это одно и то же.

Хорошо, вот что происходит внутри: в каждом случае все ключи или все значения рассчитываются как список, а затем циклическая конструкция выполняет итерацию по этому вычисленному списку. Есть внутренняя проверка, но эта проверка только для того, чтобы увидеть, достиг ли цикл смещения последнего элемента в списке. Это дешевая операция в базовом C-коде. Чтобы было ясно, keys и values не вызываются на каждой итерации. Список повторяющихся вещей вычисляется только один раз в начале цикла.

Кроме того, $key и $value являются псевдонимами фактического ключа или фактического значения, а не копий. Таким образом, копия для каждой итерации не создается.

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

foreach my $line (<$file_handle>) {...}

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

open my $fh, '<', '/dev/urandom';
say ord while <$fh>;

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

Теперь измените вторую строку следующим образом:

say ord for <$fh>;

Похоже, что он зависает, пока он потребляет всю системную память, пытаясь извлечь все содержимое / dev / urandom (и бесконечного потока). Он должен сделать это, прежде чем сможет начать итерацию, потому что именно так работает цикл foreach на основе диапазона в Perl и некоторых других языках.

Таким образом, цикл foreach, основанный на диапазоне, является недорогим с точки зрения вычислительных затрат, но в некоторых случаях потенциально дорогостоящим в плане использования памяти.

Говоря с вашим последним примером:

my @array = keys %hash;
foreach my $keys (@array) { ... } 

Это не имеет заметного значения, и на самом деле может быть медленнее или потреблять больше памяти. Когда я сравниваю два подхода с хешем 100000 элементов, разница между ними составляет всего 2%, или в пределах погрешности:

         Rate   copy direct
copy   35.9/s     --    -2%
direct 36.7/s     2%     --

Вот код:

use Benchmark qw(cmpthese);

my %hash;
@hash{1..100000} = (1..100000);


sub copy {
    my @array = keys %hash;
    my $b = 0;
    $b += $_ foreach @array;
    return $b;
}

sub direct {
    my $b = 0;
    $b += $_ foreach keys %hash;
    return $b;
}

cmpthese(-5, {
    copy => \&copy,
    direct => \&direct,
});
3 голосов
/ 01 июля 2019

Условие рассчитывается только первоначально (до запуска цикла), поэтому я не думаю, что это увеличит скорость для предварительного расчета массива перед циклом .. Пример:

for my $key (get_keys()) {
    say $key;
}

sub get_keys {
    say "Calculating keys..";
    return qw(a b c d);
}

Выход :

Calculating keys..
a
b
c
d
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...