Почему substr-lvalue быстрее, чем substr с четырьмя аргументами?

Из этот вопрос мы сравним эти два варианта,

substr( $foo, 0, 0 ) = "Hello ";
substr( $foo, 0, 0, "Hello " );

В нем мы обнаруживаем, что substr-lvalue быстрее. На что Икегами сказал:

Как 4-arg substr медленнее, чем lvalue substr (который должен создавать волшебный скаляр и требует дополнительных операций)??? – икегами

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

Почему substr-lvalue быстрее, чем substr с четырьмя аргументами в приведенном выше примере использования?


person Evan Carroll    schedule 08.05.2020    source источник
comment
pp_substr и Perl_magic_setsubstr. Последний вызывается после присвоения магическому скаляру.   -  person ikegami    schedule 08.05.2020


Ответы (2)


Это был просто плохой результат теста.

Когда я воспроизвел ваши результаты, я использовал Perl на Unbuntu в Windows Susbsytem для Linux. Скажем так, производительность чувствительна к внешним факторам в этой системе.

Даже при использовании нативной сборки для Windows (Strawberry Perl) на одном компьютере я получаю дикие отличия в результатах:

                   Rate        substr substr_valute   multiconcat
                  Rate substr_valute        substr   multiconcat
substr_valute 6997958/s            --           -0%          -27%
substr        7007667/s            0%            --          -26%
multiconcat   9533733/s           36%           36%            --

                   Rate        substr substr_valute   multiconcat
substr        6795650/s            --           -0%          -10%
substr_valute 6805545/s            0%            --          -10%
multiconcat   7526593/s           11%           11%            --

                    Rate        substr substr_valute   multiconcat
substr         7513339/s            --          -22%          -28%
substr_valute  9693997/s           29%            --           -6%
multiconcat   10367639/s           38%            7%            --

                    Rate        substr   multiconcat substr_valute
substr         8791152/s            --          -13%          -14%
multiconcat   10139954/s           15%            --           -1%
substr_valute 10240638/s           16%            1%            --

Время просто такое маленькое, а машина слишком занята, чтобы получить точные показания.

(Где-то здесь нужно упомянуть о микрооптимизациях...)

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

                   Rate        substr substr_valute   multiconcat
substr        4293130/s            --           -3%          -13%
substr_valute 4407446/s            3%            --          -11%
multiconcat   4938717/s           15%           12%            --

                   Rate substr_valute        substr   multiconcat
substr_valute 4289732/s            --           -2%          -16%
substr        4356113/s            2%            --          -15%
multiconcat   5096889/s           19%           17%            --

(Я использовал -3 вместо 100_000_000.)

Все различия составляют 3% или менее, что не является существенным. Насколько я могу судить, один не медленнее другого.

На самом деле никакой разницы ожидать не стоит. Как указал Дэйв Митчелл, substr( $foo, 0, 0 ) = "Hello "; оптимизировано до практически эквивалентного substr( $foo, 0, 0, "Hello " ); начиная с 5.16 (с улучшением в 5.20).

$ perl -MO=Concise,-exec -e'substr( $foo, 0, 0, "Hello " );'
1  <0> enter
2  <;> nextstate(main 1 -e:1) v:{
3  <#> gvsv[*foo] s
4  <$> const[IV 0] s
5  <$> const[IV 0] s
6  <$> const[PV "Hello "] s
7  <@> substr[t2] vK/4
8  <@> leave[1 ref] vKP/REFC
-e syntax OK

$ perl -MO=Concise,-exec -e'substr( $foo, 0, 0 ) = "Hello ";'
1  <0> enter
2  <;> nextstate(main 1 -e:1) v:{
3  <$> const[PV "Hello "] s
4  <#> gvsv[*foo] s
5  <$> const[IV 0] s
6  <$> const[IV 0] s
7  <@> substr[t2] vKS/REPL1ST,3
8  <@> leave[1 ref] vKP/REFC
-e syntax OK

(Единственное отличие состоит в порядке передачи операндов, который сигнализируется с помощью флага REPL1ST.)

person ikegami    schedule 08.05.2020
comment
Ага, у меня даже с -3 я получаю гигантскую разницу на дебиане с Бастером. - person Evan Carroll; 08.05.2020
comment
-3 против 100_000_000 ничего не изменит, кроме времени выполнения (3 секунды против 100_000_000 тестов) - person ikegami; 08.05.2020
comment
Для меня substr_lvalue примерно то же время, что и multiconcat, и всегда быстрее, чем substr на процессоре Intel(R) Core(TM) i7-3520M с тактовой частотой 2,90 ГГц. - person Evan Carroll; 08.05.2020
comment
Мы говорим о временах около 100 нс на интеракцию, включая относительно дорогой дополнительный вызов. Неудивительно, что он подвержен колебаниям. При тестировании подобных вещей я использую b_multiconcat => 'use strict; use warnings; for (1..1000) { my $foo = "world!"; $foo = "Hello $foo"; }' - person ikegami; 08.05.2020
comment
@ikegami Просто любопытно узнать, какую команду вы использовали для получения этих данных результатов? - person vkk05; 08.05.2020
comment
@vinodk89 Смотрите ссылку в вопросе - person ikegami; 08.05.2020
comment
Расширил мой ответ на основе комментария-ответа Дэйва Митчелла. - person ikegami; 08.05.2020

Начиная с версии 5.16.0, вариант lvalue+assign был оптимизирован в вариант с 4 аргументами (хотя операция назначения NOOP с нулевым значением все еще находилась в пути выполнения до версии 5.20.0, что немного замедляло ее).

person Dave Mitchell    schedule 08.05.2020