Что происходит, когда декларатор (my/state) находится в блоке for?

Следующие блоки запускают цикл, присваивая тему переменной $var:

  • Первый my $var; находится вне цикла
  • Второй my $var; находится внутри цикла
  • Наконец, state $var; находится внутри цикла
my $limit=10_000_000;
{
    my $var;
    for ^$limit { $var =$_; }
    say now  - ENTER now;
}
{
    for ^$limit { my $var; $var=$_; }
    say now  - ENTER now;
}
{
    for ^$limit { state $var; $var=$_; }
    say now  - ENTER now;
}

Примерная длительность вывода (в секундах) каждого блока следующая:

0.5938845                                                                                                                                 
1.8251226                                                                                                                                 
2.60700803  

Переменные движения https://docs.perl6.org/syntax/состояниеstate имеют ту же лексическую область видимости, что и my. Функционально блок кода 1 и блок 3 обеспечивают одно и то же постоянное хранилище для нескольких вызовов соответствующего блока цикла.

Почему state (и внутренняя my) версия занимает так много времени? Что еще он делает?

Редактировать: Подобно комментарию @HåkonHægland, если я вырежу и вставлю приведенный выше код, чтобы запустить каждый блок три раза, время значительно изменится для my $var вне цикла (первый случай):

0.600303                                                                                                                                  
1.7917011                                                                                                                                 
2.6640811                                                                                                                                 

1.67793597                                                                                                                                
1.79197091                                                                                                                                
2.6816156                                                                                                                                 

1.795679                                                                                                                                  
1.81233942                                                                                                                                
2.77486777

Нет времени для каждого блока

drclaw 08.04.2019 08:04

Я получаю сильно различающиеся результаты в зависимости от порядка блоков, см.: tio.run/##K0gtyjH7/…

Håkon Hægland 08.04.2019 09:40

Согласно Хокону, одной из проблем может быть неиспользование чего-то вроде крун. Aiui: первый показанный блок назначает новый Scalar вне цикла и, таким образом, экономит затраты на создание нового Scalar при каждом проходе через цикл, который предположительно связан со вторым показанным блоком. И оба этих блока можно оптимизировать до простого цикла, тогда как третий требует создания замыкания, что дороже. Я с нетерпением жду авторитетного ответа от смелого разработчика, такого как timotimo или jnthn...

raiph 08.04.2019 10:00

@HåkonHægland, это действительно странная разница во времени. Я тоже добавлю это в вопрос.

drclaw 08.04.2019 23:50
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
9
4
147
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Краткая версия: в мире без какой-либо оптимизации времени выполнения (специализация типов, JIT и т. д.) тайминги соответствовали бы вашим ожиданиям. На время здесь влияет то, насколько хорошо оптимизатор обрабатывает каждый пример.

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

13.92366942
16.235372
14.4329288

Они имеют какой-то интуитивный смысл:

  • В первом случае у нас есть простая лексическая переменная, объявленная во внешней области видимости блока.
  • Во втором случае мы должны выделить, а затем собрать мусор, дополнительное выделение Scalar каждый раз в цикле, что составляет дополнительное время.
  • В третьем случае мы используем переменную state. Переменная state хранится в объекте кода замыкания, а затем копируется в кадр вызова во время входа. Это дешевле, чем каждый раз выделять новый Scalar, но все же немного больше работы, чем вообще не выполнять эту операцию.

Далее запустим 3 программы с включенным оптимизатором, каждый пример в своей изолированной программе.

  • Первый выходит в 0.86298831, в 16 быстрее. Вперёд оптимизатор! Он имеет встроенное тело цикла.
  • Второй выходит в 1.2288566, в 13 раз быстрее. Тоже не слишком хлипкий. Он снова встроил тело цикла. (Этот случай также станет намного дешевле в будущем, как только анализатор побегов станет достаточно умным, чтобы исключить выделение Scalar.)
  • Третий выходит в 2.0695035, в 7 раз быстрее. Это сравнительно не впечатляет (даже если все же значительное улучшение), и основная причина в том, что он не встраивает тело цикла. Почему? Потому что он еще не умеет встраивать код, использующий переменные состояния. (Как это увидеть: запустить с MVM_SPESH_INLINE_LOG=1 в среде, и среди вывода: Can NOT inline (1) with bytecode size 78 into (3): cannot inline code that declares a state variable.)

Короче говоря, доминирующим фактором здесь является встраивание тела цикла с переменными состояния, что в настоящее время невозможно.

Не сразу понятно, почему оптимизатор работает хуже в случае с внешним объявлением $var, когда это не первый цикл в программе; это больше похоже на ошибку, чем на разумный случай «эта функция еще недостаточно оптимизирована». В своей небольшой защите ему все же удается добиться значительного улучшения, даже если оно не такое большое, как хотелось бы!

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

Похожие вопросы

Возвращаемый адрес локальной переменной - можно игнорировать предупреждение без ущерба, но как правильно?
Как получить значение переменной в лямбда-выражении из-за пределов функции в Котлине
Я определяю некоторые переменные и использую их для получения широты и долготы, которые работают, но после того, как я передаю переменную другой функции, она не работает
Как глобальное ключевое слово PHP работает внутри функции с использованием области видимости?
Как определить полиморфную ассоциацию по атрибуту
Плюсы и минусы определения функции внутри компонента React или в этом файле
Наши переменные динамические?
Неопределенная ошибка переменной, хотя переменная присутствует в уже включенном файле
Две функции в одном классе не могут получить доступ друг к другу
Как изменить и получить объекты из другого класса, не объявляя его?