Скорость индексирования массивов Perl с помощью смещения

В соответствии с этим вопросом и этот ответ списки реализуются как массивы:

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

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

Индексирование в массивы не используется Сильные стороны Perls. Если вы используете поп, push и аналогичных операторов, которые избегают используя индексирование, ваш код будет как правило, быстрее, чем если вы используете многие индексы, а также избегая ошибки "по очереди", часто называемые ошибки "fencepost". Иногда, начиная программист Perl (желая посмотрите, как скорость Perls сравнивается с Cs) возьмет, скажем, алгоритм сортировки оптимизированный для C (с большим индексом массива операции), переписать его прямо в Perl (опять же, с многие операции с индексами) и удивляться, почему его так медленно. Ответ заключается в том, что использование скрипка Страдивари для ног не следует рассматривать как звук строительной техники.

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

Ответ 1

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

Вы можете увидеть это, используя ключ -MO = Terse, чтобы увидеть, что 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 должен выполнять меньше шагов, поэтому можно ожидать, что они будут быстрее.

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