Является ли сравнение строк или поиск хэша быстрее в Perl?


У меня есть некоторый код, который в одной части будет выполняться много, и я удивляюсь, какой способ является более эффективной реализацией. Я буду использовать цикл for для имитации части, которая выполняется много:

Вариант А:

my %sections = (
    'somestring1' => 1,
    'somestring2' => 1,
    'somestring3' => 1,
    'somestring4' => 1
);

for (0..10000)
{
    # $element is chosen at random
    $namespace = $element if $sections{$element};
}

Вариант B:

for (0..10000)
{
    # $element is chosen at random
    $namespace = $element if ($element eq'somestring1' || 
                            $element eq'somestring2' ||
                            $element eq'somestring3' ||
                            $element eq'somestring4');
}

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

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

5   2   2009-09-15 00:37:28

5 ответов:

Используйте функцию cmpthese из модуля Benchmark

use strict;
use warnings;
use Benchmark qw'cmpthese';

my %sections = (
    somestring1 => 1,
    somestring2 => 1,
    somestring3 => 1,
    somestring4 => 1
);

my @elements = map { 'somestring' . int(1 + rand(10)) } 1 .. 100;

my $namespace;

cmpthese(100000, {
    hash_value => sub {
        foreach my $element (@elements) {
            $namespace = $element if $sections{$element};
        }
    },
    hash_exists => sub {
        foreach my $element (@elements) {
            $namespace = $element if exists $sections{$element};
        }
    },
    string_cmp => sub {
        foreach my $element (@elements) {
            $namespace = $element if (
                $element eq'somestring1' ||
                $element eq'somestring2' ||
                $element eq'somestring3' ||
                $element eq'somestring4');
        }
    },
});

Мои результаты (запуск Perl 5.10 на WinXP):

               Rate  string_cmp  hash_value hash_exists
string_cmp  18932/s          --        -44%        -50%
hash_value  33512/s         77%          --        -12%
hash_exists 38095/s        101%         14%          --
Таким образом, поиск хэша на 77% быстрее, чем каскадные сравнения строк, а проверка наличия хэша вместо значения (как предположил Адам Беллер) - еще на 14% быстрее.

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

for (0..10000)
{
    # $element is chosen at random
    $namespace = $element if exists $sections{$element};
}
Простая проверка наличия хэш-ключа быстрее, чем получение его значения для сравнения, поэтому используйте exists.

Механизм поиска хэша значительно быстрее.

Вероятно, настало время ознакомиться с инструментами бенчмаркинга, такими как CPAN module Benchmark.

Бенчмарк Майкла кармана хорош, но результаты уже достаточно устарели, поэтому люди, не работающие на своей собственной машине, могут получить неверное представление. Таким образом, тот же самый бенчмарк (просто в 10 раз больше случаев, чтобы дать более последовательные результаты) на Mac Pro w / Mac OS X и Perl 5.24.1:

               Rate  string_cmp hash_exists  hash_value
string_cmp  54142/s          --        -28%        -32%
hash_exists 74850/s         38%          --         -7%
hash_value  80192/s         48%          7%          --

Однако на AWS с CentOS 7 / Perl 5.24.0 мы получаем:

string_cmp  70373/s          --        -24%        -25%
hash_value  92851/s         32%          --         -1%
hash_exists 93545/s         33%          1%          --

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

Одна вещь, которая мне не нравится в бенчмарке, - это то, что он довольно произвольно выбирает для сравнения 4 равенства с хэш-проверкой. Не смущайтесь, если у нас есть только один элемент в хэше, поэтому мы сравниваем с одним равенством, которое мы получаем (на моем Mac Pro / Perl 5.24.1):

hash_value  119474/s          --         -1%        -14%        -34%
hash_exists 121065/s          1%          --        -12%        -33%
grep        138122/s         16%         14%          --        -23%
string_cmp  180180/s         51%         49%         30%          --

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

string_cmp  104167/s          --        -15%        -17%
hash_value  121951/s         17%          --         -2%
hash_exists 125000/s         20%          2%          --

Однако это происходит с хэшем, предварительно построенным вне цикла, как в исходном примере. Что, если мы создадим хэш на каждом тестовом цикле? Т. е. у вас есть пара значений, которые вы хотите проверить на наличие в массиве, стоит ли накладные расходы на создание хэша с ними? Я не буду утомлять вас дополнительными результатами, так как вы можете обнаружить, что они отличаются на вашей машине, но ответ заключается в том, что для 2 значений "это зависит" (так что, возможно, Вам не стоит беспокоиться), но для 3 или более вы должны это сделать.