-
Notifications
You must be signed in to change notification settings - Fork 35
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Оптимизатор зацикливается на комбинаторе неподвижной точки #278
Comments
После этого рефакторинга будет проще научить алгоритм разметки стирать пометки (Drive …) у базисных функций.
Последние два варианта снижают глубину оптимизации — подробности в комментариях к #314, поэтому их реализовывать не будем. Первые три решения — костыли, их тоже реализовывать не будем. Будем реализовывать шестой вариант. Предлагаемое решениеПредлагается модифицировать алгоритм автоматической разметки так, чтобы он не вешал новые метки
|
Перед выполнением прогонки избыточные метки $DRIVE удаляются. В том числе и неявные метки, добавляемые безымянным функциям (что проверяет автотест).
Логика, удаляющая опасные Вот время выполнения функций из
Медленные функции вполне предсказуемы: и захват контекста, и открытые переменные, но я их оптимизировать не буду, поскольку долю они занимают сравнительно небольшую. Если сочту нужным, потом оптимизирую. |
Введение
Оказалось, что разметка безымянных функций неявными метками
$DRIVE
может привести к бесконечной прогонке.Исходной целью древесной оптимизации (прогонки и специализации) была оптимизация вызовов функций
Map
,MapAccum
иReduce
, которые фактически используются для реализации циклов. А безымянная функция в их аргументе является первейшим кандидатом на встраивание — специализацияMap
с последующей прогонкой строит функцию, эквивалентную рекурсивной функции, написанной вручную.Также полезно встраивать/прогонять присваивания и блоки. Присваивание пассивного выражения e-переменной при оптимизации просто имеет нулевую стоимость. Блоки тоже бывает полезно прогонять.
Опасно назначать метки
$DRIVE
и$INLINE
в общем случае рекурсивным функциям, поскольку на них оптимизатор может зациклиться. Безымянные функции не могут быть синтаксически рекурсивными, поэтому на первый взгляд делать их рекурсивными безопасно.Но безымянная функция может принимать ссылку на себя, вызывать полученную s-переменную и так осуществлять рекурсивные вызовы. При прогонке такой функции можно зациклиться.
Пример похожего зацикливания, но для именованной функции, приведён в #252 (comment). Зацикливание безымянных функций можно осуществить, используя тот или иной комбинатор неподвижной точки.
Воспроизведение проблемы
В комментариях к #160 затесался офтопик — пример написания комбинатора неподвижной точки на Рефале: #160 (comment). Возьмём оттуда пример кода и немножко его адаптируем:
Скачать: fixpoint.ref.txt.
Компиляция осуществлялась командой
Цикл внутри
Fact
продолжается до тех пор, пока счётчик итераций не превысит аргумент функции. Для этого вычисляется число, на единицу большее аргумента, и цикл прерывается, при достижении счётчиком итераций этой величины.Встраиваемые функции
FakeMul
иFakeInc
имитируют планируемы интринсики (#260). Можно было бы обойтись без них, используя какую-нибудь унарную систему счисления, но это снизит наглядность примера.Присваивание в функции
Fact
преобразуется во вспомогательную функцию:Функция
Fact=1
принимает число и вычисляет факториал числа, на единицу меньшего. Если посмотреть на результат трансформации этой функции, получим такую красоту:Вычисления остановились на 11, поскольку дальше
FakeMul
иFakeInt
вручную не просчитаны. При реализации интринсиков (#260) вычисления продолжались бы дальше.Вот так это выглядит
Полный лог
Конечно, забавно наблюдать, что компилятор строит нам таблицу факториалов. Но важнее то, что оптимизатор бессмысленно зацикливается и с этим что-то надо делать.
Возможные решения
Ничего не делать
Можно сказать, что эта проблема надумана. На практике таких комбинаторов неподвижной точки не пишут. Даже, если оптимизатор зациклится, компилятор не будет работать вечно — число проходов оптимизации ограничено.
Вариант возможный и приемлемый. Если не найдётся решения лучше, оставим его.
Не назначать метки
$DRIVE
безымянным функциямПростейшее решение, базовый вариант.
Вариант не рассматривается как решение, потому что есть варианты лучше.
Назначать метки
$DRIVE
только блокам и присваиваниямВложенные функции для блоков и присваиваний не могут быть вызваны рекурсивно никаким способом. Единственный экземпляр замыкания в поле зрения находится в позиции вызова.
В отличие от блоков и присваиваний, явные безымянные функции могут получить указатель на себя — вызывающая функция может продублировать s-переменную, передав себя как аргумент. Простейший пример:
Эта строчка кода, очевидно, зациклится.
(Drive …)
в рассахаривателе нужно различать сорта вложенных функций, например, по суффиксам.<Map { … } …>
, а они пролетают.Спасти здесь может Автоматическая разметка оптимизируемых функций #252, обнаружить нерекурсивные вложенные функции и назначить им метку
$DRIVE
.Приемлемое решение, но нужно мерять влияние на производительность.
Специализировать вложенные функции
Это решение близко к #160, оно будет побочным эффектом #160, но это не оно.
При условии реализации #251 любая функция, которую можно прогнать полностью (т.е. без непустого остатка
Func*n
), сможет быть и проспециализирована. Поэтому вызов вложенной функции частично оптимизируется — информация сможет распространиться глубже, где она учтётся оптимизатором. А специализация будет (#253) безопасной.Пересмотреть подход к древесной оптимизации
А именно, при реализации #251 и #252 имеет смысл все оптимизируемые функции сначала специализировать, а потом прогонять «транзитные» экземпляры специализированных функций.
$INLINE
и$DRIVE
любые функции, к зацикливанию это не приведёт. Т.е. это решение более общего случая.$INLINE
станет не нужна, можно вообще использовать одно ключевое слово$OPT
для любых оптимизируемых функций.Последний вариант выглядит интересно, но не как основной, а как один из режимов работы в дополнение к имеющимся.
UPD: Задача #314 посвящена именно этому решению.
The text was updated successfully, but these errors were encountered: