Производительность локальной переменной и доступа к массиву

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

sub foo {
  my $value = $array[17];

  do_something_with($value);
  do_something_else_with($value);
}

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

sub foo {
  do_something_with($array[17]);
  do_something_else_with($array[17]);
}

Для наглядности первое понятнее. Я предположил, что производительность будет по крайней мере равной (или лучше) и для первого случая - для поиска в массиве требуется умножение-и-сложение, в конце концов.


Представьте мое удивление, когда эта тестовая программа показала обратное. На моей машине повторный поиск в массиве на самом деле быстрее, чем сохранение результата, пока я не увеличу ITERATIONS до 7; другими словами, для меня создание локальной переменной имеет смысл только в том случае, если она используется по крайней мере 7 раз!

use Benchmark qw(:all);

use constant { ITERATIONS => 4, TIME => -5 };

# sample array
my @array = (1 .. 100);

cmpthese(TIME, {
  # local variable version
  'local_variable' => sub {
    my $index = int(rand(scalar @array));
    my $val = $array[$index];
    my $ret = '';

    for (my $i = 0; $i < ITERATIONS; $i ++) {
       $ret .= $val;
     }

    return $ret;
  },

  # multiple array access version
  'multi_access' => sub {
    my $index = int(rand(scalar @array));
    my $ret = '';

    for (my $i = 0; $i < ITERATIONS; $i ++) {
      $ret .= $array[$index];
    }

    return $ret;
  }
});

Результат:

                   Rate local_variable   multi_access
local_variable 245647/s             --            -5%
multi_access   257907/s             5%             --

Это не ОГРОМНОЕ различие, но оно поднимает мой вопрос: почему медленнее создать локальную переменную и кэшировать поиск в массиве, чем снова выполнять поиск? Читая другие SO сообщения, я видел, что другие языки / компиляторы имеют ожидаемый результат, а иногда даже превращают их в один и тот же код. Что делает Perl?

2 ответа

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

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

my $index = int(rand(scalar @array));
my $val = 0; # <- this is new
$val = $array[$index];
my $ret = '';

... код подвергается дополнительному штрафу в 5% скорости по сравнению с версией с одним назначением - даже если он не делает ничего, кроме фиктивного присвоения переменной.

Я также проверил, не вызвал ли область действия установку / демонтаж $var чтобы снизить производительность, переключив его на глобальный вместо локального. Разница незначительна (см. Комментарии к @zdim выше), указывая на конструкцию / разрушение как на узкое место в производительности.


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

Оказывается, это не относится к Perl (хотя я не знаю точно, почему, это нормально). Скалярное присваивание является относительно "медленной" операцией... Что бы ни делали внутренние компоненты Perl, чтобы получить n-й элемент объекта Array, по сравнению с ним, на самом деле, это довольно быстро. "Умножение и сложение", о котором я упоминал в первоначальном посте, все еще гораздо менее трудоемко, чем код для скалярного назначения.

Вот почему требуется так много поисков, чтобы соответствовать производительности кэширования результата: простое назначение переменной "cache" происходит в ~7 раз медленнее (для моей настройки).

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

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

Другие вопросы по тегам