Скорость индексации массивов Perl по смещению - PullRequest
11 голосов
/ 09 июня 2011

Согласно этому вопросу и этому ответу списки реализованы в виде массивов:

Perl реализует списки с массивом и смещением первого / последнего элемента,Массив выделяется больше, чем необходимо, смещения, изначально указывающие на середину массива, так что есть место для роста в обоих направлениях (unshifts и push / вставки), прежде чем потребуется перераспределение базового массива.Следствием этой реализации является то, что все операторы примитивного списка perl (вставка, выборка, определение размера массива, push, pop, shift, unshift и т. Д.) Выполняются за O (1) раз.

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

Индексирование в массивы не использует сильные стороны Perl.Если вы используете операторы pop, push и аналогичные, которые не используют индексацию, ваш код, как правило, будет работать быстрее, чем если бы вы использовали много индексов, а также избегал ошибок «off-by-one», часто называемых ошибками fencepost.Иногда начинающий программист на Perl (желая посмотреть, как скорость Perl сравнивается с C), скажем, скажем, алгоритм сортировки, оптимизированный для C (со многими операциями индекса массива), переписывает его прямо в Perl (опять же, со многими операциями индекса) иИнтересно, почему это так медленно?Ответ заключается в том, что использование скрипки Страдивари для стучания гвоздей не должно рассматриваться как надежная методика построения.

Как это может быть правдой, если список действительно представляет собой массив под капотом?Я знаю, что просто не в силах сравнить скорость Perl с C, но разве индексирование списка по смещению не будет таким же быстрым, как pop, push или что-то еще?Кажется, они противоречат друг другу.

1 Ответ

20 голосов
/ 09 июня 2011

Это связано с реализацией Perl в виде серии кодов операций.push, pop, shift и unshift - все они сами по себе, поэтому они могут указывать в массив, которым они манипулируют из C, где доступ очень быстрый.Если вы сделаете это из Perl с индексами, вы заставите Perl выполнять дополнительные коды операций, чтобы получить индекс из скаляра, получить слот из массива и вставить что-то в него.

Это можно увидеть с помощью-MO = краткий переключатель, чтобы увидеть, что Perl действительно (в некотором смысле) делает:

$foo[$i] = 1

    BINOP (0x18beae0) sassign
        SVOP (0x18bd850) const  IV (0x18b60b0) 1
        BINOP (0x18beb60) aelem
            UNOP (0x18bedb0) rv2av
                SVOP (0x18bef30) gv  GV (0x18b60c8) *foo
            UNOP (0x18beba0) null [15]
                SVOP (0x18bec70) gvsv  GV (0x18b60f8) *i

push @foo, 1

    LISTOP (0x18bd7b0) push [2]
        OP (0x18aff70) pushmark
        UNOP (0x18beb20) rv2av [1]
            SVOP (0x18bd8f0) gv  GV (0x18b60c8) *foo
        SVOP (0x18bed10) const  IV (0x18b61b8) 1

Вы видите, что Perl должен выполнять меньше шагов, поэтому можно ожидать, что он будет быстрее.

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

...