-
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
Специализация без шаблона #251
Comments
На курсовую работу предлагается реализовать программу-минимум, причём в базовом варианте. Заметим, это не препятствует реализации синтаксиса |
Задача не была выбрана в качестве курсовой. |
Задача не была выбрана в качестве ВКР. |
Другой подход к специализации без шаблонаЕсли шаблон не задан, то его можно выводить — вычислять входной формат как ГСО образцов, и параметры, сужающиеся в переменные того же типа или (в дальнейшем) L-выражения, объявлять статическими. Дальнейшая специализация идёт обычным образом по шаблону. Так, в частности описано в #252. Но интереснее другое: можно формировать шаблон индивидуально для каждого вызова — вычислять ГСО от образцов вызываемой функции и фактического аргумента вызова. В этом случае можно оптимизировать вызовы, которые иначе бы не оптимизировались. Например, вызов В этом случае частью сигнатуры будет ещё и актуальный шаблон, по которому строился вызов. Недостаток по сравнению с динамическим обобщением — снижается потенциальная глубина оптимизации, ведь иногда возможны решения и для не-L-образцов. |
Специализировать все функции?А почему бы и нет?
|
Имена функций с меткой (Spec …) в дереве могут иметь суффиксы. На данный момент это совершенно нейтральное изменение, никакими входными данными его выявить невозможно, поэтому с полным основанием оно является рефакторингом. Но это исправление нужно сразу нескольким задачам: • Вложенные функции (включая присваивания и блоки) неявно преобразуются в обычные глобальные функции с суффиксами. Задача #160 требует их специализировать. • В задаче #252 предлагается автоматически размечать функции, пригодные для оптимизации (включая специализацию). Размечатель будет работать как с входным деревом до оптимизации, так и с оптимизированным деревом после оптимизаций. Пригодными для оптимизации могут быть и функции с суффиксами. • При глобальной оптимизации (#255) синтаксические деревья разных единиц трансляции объединяются в одно, локальные функции в них переименовываются — получают суффиксы. • В задаче #251 предлагается рассмотреть специализацию всех функций программы, включая функции с суффиксами. • На это нет отдельной заявки (это часть #160), но имеет смысл разрешить одновременную прогонку и специализацию функций, пометку одной функции метками $DRIVE и $SPEC одновременно. В этом случае придётся специализировать и функции Func*n — остатки прогоняемых функций. Ну и кроме того, это красиво: поддержка не частного случая, а общего.
Они будут полезны при отладке или написании новых оптимизаций, например, В стабильной версии этот флаг всегда игнорируется. В рабочей может включать новый режим. Поэтому можно, например, держать в RLMAKE_FLAGS=-Ox, вызывать попеременно makeself.bat и makeself-s.bat и не иметь проблем со вторым вызовом. После завершения разработки новой функции её можно или сделать неотключаемым поведением по умолчанию, или назначить новый флаг, или связать новый режим со старым флагом, старому режиму назначить новый флаг. Пример для последнего: при реализации #251 флаг S может включать, например, специализацию всех вызовов, флаг s — текущий режим (оптимизация помеченных вызовов).
Соображение про динамическое обобщениеДопустим, алгоритм обобщённого сопоставления обнаружил, что сопоставление не определено и нужно обобщить некий участок левой части. Какое уравнение не решилось, знает сам алгоритм. Как он должен сообщить об этом наружу? Предлагается между лексемами выражения (т.е. между символами, переменными, по обе стороны от каждой скобки) вставлять координаты — последовательные номера. Выражение
Теперь, когда сопоставление невозможно, алгоритм будет возвращать не просто Обобщение, приводящее к решению уравнения, может быть неоднозначным. Поэтому остаётся открытым вопрос: какое обобщение выбрать? При наличии повторных t-. e-переменных мы получаем уравнение вида Динамическое обобщение потребуется не только для настоящей задачи, оно также будет крайне полезно для дефорестации (#165). |
Оба этих соображения актуальны и для дефорестации (#165). |
Соображения про динамическое обобщениеВ этом комментарии я формализую то, что выкристаллизовывалось в предыдущих 7 комментариях, а также опишу многообещающий подход к решению задачи динамического обобщения (последний абзац комментария). Постановка задачи на расширенную специализацию функцийИсходные данныеУ нас есть специализируемая функция
Здесь Для простоты будем считать, что Функции Идеальный случай специализацииМы хотим построить специализированный экземпляр В актуальной реализации вся статически известная информация о вызове — это только выражение А что же тогда будет статически не известным? Значения переменных из Таким образом, вызов будет иметь вид Рассмотрим уравнение
где Тогда вид функции
Идеал достижим не всегдаК сожалению, идеал достижим не всегда. Не для любого уравнения вида
Задача динамического обобщенияЕсли в функции Под обобщением выражения Задачей динамического обобщения мы будем подразумевать поиск такого обобщения При использовании динамического обобщения вызов функции
Замыкания и абсурдные функцииОбъекты замыканий в образцах синтаксически и семантически недопустимы. В текущей реализации, если при специализации строится экземпляр, в теле которого оказываются образцы, содержащие замыкания, то данная специализация отбраковывается, а вызов остаётся неспециализирован. Замыкания в образцах могли возникнуть из-за того, что подстановки для статических переменных содержали замыкание и сами статические переменные находились или в динамических участках образца, или в образцах условий. В рамках расширенной специализации никаких динамических фрагментов образца нет, образец экземпляра фактически представляет собой набор сужений, приведённый к формату. Алгоритм обобщённого сопоставления гарантирует, что сужения никогда не будут содержать объектов замыканий, т.к. он используется и при прогонке. Таким образом, нужно контролировать переменные из Список таких «беззамыкательных» переменных для каждого предложения можно подготовить заранее (хотя это преждевременная оптимизация). Далее, можно либо отдельно проверять присваивания таким переменным, или подсказать алгоритму обобщённого сопоставления, что в этих переменных замыкания запрещены. Подсказать можно, сделав их повторными в правой части уравнения. Назовём список «беззамыкательных» переменных
разрешимо, если переменным из Поэтому на первом этапе проще проверять присваивания. ВыводыТаким образом, любой вызов функции Сигнатурой экземпляра Иногда специализация будет давать тривиальный результат — новая функция будет совпадать с исходной с точностью для формата. Такие вызовы можно распознавать по сигнатуре и не специализировать их. Тривиальной сигнатурой будет сигнатура, в которой:
Можно в тривиальной сигнатуре также запретить абстрактные скобки, в этом случае специализация будет давать оптимизацию на низком уровне — узел для имени тега не будет ни распределяться, ни сопоставляться. Соображение по выбору обобщенияНам нужно найти обобщение Предлагается последовательно сопоставлять Предложенный алгоритм может давать неоптимальный результат, т.к. обобщение неоднозначное. Навскидку привести пример мне здесь сложно, но это аналогично тому, как если бы мы искали обобщение образцов, обобщая их попарно. Обобщили первый со вторым, третий с обобщением первых двух и т.д. Результат может не быть даже ЛСО. Но алгоритма, способного дать минимальное обобщение, на данный момент я не знаю. Но зато задачу динамического обобщения аргумента для нескольких образцов можно свести к задаче динамического обобщения для аргумента и одного образца. Динамическое обобщение в расширенном сопоставлении с образцомФункция обобщённого сопоставления решает уравнение вида Уравнение Задачей динамического обобщения при сопоставлении с образцом является поиск обобщения В предыдущем комментарии предлагается интерфейс для функции обобщённого сопоставления, которая может выполнять динамическое обобщение. Она должна возвращать координаты участка выражения В процессе решения уравнения формируется система уравнений вида
где При планируемой расширенной поддержке повторных переменных (#249) также потребуется уравнения вида
где Далее уравнения первого типа (выражение с образцом) мы будем записывать через двоеточие, второго типа (выражение с выражением, повторные переменные) через знак равенства. Причин для динамического обобщения может быть две: имеем уравнение запрещённого вида или имеем уравнение, на котором решатель зацикливается.
С запрещёнными уравнениями всё просто. Видим «неправильное» уравнение, прерываем процесс решения с возвратом признака обобщения. Но вот что делать с зацикливающими уравнениями? По внешнему виду они не всегда различимы. Возможный подход к решению зацикливающих уравнений заключается в обнаружении зацикливания при помощи отношения Хигмана-Крускала. При решении системы уравнений мы храним историю решения. Если следующая система опасно похожа на одну из предыдущих, то процесс решения прерываем, а участки выражения, соответствующие левым частям уравнений, обобщаем. |
Смысл в том, что в рамках задач #251 и #322 потребуется разработать функцию обобщённого сопоставления с другим интерфейсом — Solve-Spec. Эта функция будет возвращать либо просто решение (когда решение существует), либо динамическое обобщение левой части аргумента и результат для него. Функции Solve-Drive и Solve-Spec будут обёртками над некоторым внутренним кодом в GenericMatch.ref.
В пред-предыдущем комментарии основная идея описана. Переформулирую её в псевдокоде. Сначала несколько цитат из предыдущего комментария.
Алгоритм в псевдокодеВходные данные: вызов
Как-то так. Если есть вопросы, пишите в комментариях. Изменения структур данныхПотребуется изменить следующие структуры данных: - t.Signature ::= ((e.InstanceName) t.StaticVarVals*)
- t.StaticVarVals ::= (e.Expression)
+ t.Signature ::= ((e.InstanceName) e.Expression)
e.InstanceName ::= e.Name
e.Histories ::= ((e.InstanceName) e.History)*
- e.History ::= ((e.FuncName) t.StaticVarVals*)*
+ e.History ::= ((e.FuncName) e.Expression)* Переписать потребуется только функции
|
Некоторые важные замечания
|
Специализация без шаблона была необходимым условием для #314. |
Автотест на нюанс с обобщением в специализации (#251)
Ранее специализация замыкания {{ &F CONTENT }} осуществлялась как специализация фиктивного вызова <F CONTENT e.@>. Оптимизатор строил новый вызов <F@1 CONTENT′ e.@>, из которого восстанавливалось замыкание {{ &F@1 CONTENT′ }}. Добавлять и удалять фиктивную переменную e.@ было безопасно, т.к. она была в позиции динамического параметра, позиция параметра не менялась (оставалась последней) и во внутрь функции она не протекала. Теперь же при специализации это имя может протечь внутрь экземпляра, внутри экземпляра может оказаться другое замыкание, в контексте которого будет переменная e.@. Добавление e.@ в конец вызовет конфликт имён. ---- В коде есть некоторый костыль, который проистекает из отсутствия поддержки подавления предупреждений. Исходники намеренно собираются с -Werror, а в данном коде было бы разумно явно добавить «подозрительную» (#290) повторную переменную. Пришлось достигать эту проверку более громоздким куском исходного кода.
Ранее древенсая оптимизация в lexgen’е подавлялась при помощи объявления функций как $SPEC State (e.acc) e.text; До реализации #251 «Специализация без шаблона» эта строчка подавляла специализацию функции-состояния в режиме -OA+. Сейчас эта строчка наоборот, форсирует специализацию этой функции даже в режиме -OA-. Избыточная прогонка подавлена при помощи организации автомата так, чтобы авторазметка (#251) рассматривала некоторые «опасные» функции как базисные, см. 0cda99e2d. В текущей версии 3.3 реализована поддержка функции обобщения gen_e__ (#331), которая предназначена для подавления специализации. Теперь все аккумуляторы принудительно обобщаются при помощи gen_e__. Прогонка подавляется за счёт того, что в актуальной версии не реализована прогонка функций с активным аргументом (#230). Потеря быстродействия не страшна, т.к. Простой Рефал уже deprecated (#318, DSL, который растворяется во время компиляции (#50). Потеря быстродей
UPD 24.10.2020: название заявки обновлено, т.к. именно это требуется в комментариях.
Мотивация
Сейчас компилятор требует, чтобы в образцах предложений специализируемой функции статические параметры соответствовали переменным того же типа. Однако, это довольно жёсткое ограничение: для некоторых содержательных функций приходится писать довольно громоздкий и корявый код.
Например, в своём недавнем письме в рассылку [email protected] я разбирал пример с интерпретатором диалекта Форта, где пришлось написать так:
Чтобы функцию можно было специализировать по
e.CODE
потребовалось написать одно предложение с блоком. Более естественный вариант выглядел бы так:Но нельзя,
e.CODE
здесь уточняется не в e-переменную, а в выражение.Почему так сделано? Потому что функция, помеченная
$SPEC
, по сути является шаблоном, по которому строятся экземпляры, адаптированные к каждому конкретному вызову. И строятся они буквально шаблонной заменой значений статических параметров на их актуальные значения. А для шаблонной замены параметрам неизбежно должны соответствовать переменные того же типа в предложениях — на место этих переменных подставляется фактический аргумент.Однако разумно разрешить статическим параметрам уточняться не в переменные, а в произвольные выражения того же типа. Как минимум, в L-выражения. В этом случае специализация получает возможности прогонки. При создании экземпляра функции для каждого предложения будет разрешаться система уравнений для статических параметров, предложения будут размножаться в соответствии с решениями системы.
Почему так не было сделано? Подход с возможностями прогонки требует наличия функции обобщённого сопоставления с образцом, которая может решить эту систему уравнений. Но на момент начала работ по обоим оптимизациям (прогонка #122, специализация #126) этой функциональности не было, был её ограниченный вариант, допускающий лишь успешные сопоставления. Прогонку делал Кирилл Ситников @InfiniteDisorder, специализацию — Дарья Сухомлинова @StrixSeloputo. Поскольку функция решения уравнений есть содержательная часть работы Кирилла, было решено ограничить Дарью базовым вариантом специализации.
Программа минимум: статические параметры не обязаны сужаться только в переменные
Собственно, нужно добавить элементы прогонки в специализацию, как описано в предыдущем абзаце. Сейчас в компиляторе есть и функция обобщённого сопоставления, и базовый вариант специализации.
Если статические параметры сужаются в L-выражения (которые в совокупности не содержат повторных t- и e-переменных), то специализация с элементами прогонки всегда будет успешной. Иначе возможен вариант
Undefined
, что в этом случае делать — не очевидно.Undefined
, генерируется как при прогонке остаточная функцияFunc*N
, которая вызывается в неуспешном предложении. Побочный эффект: оптимизация будет увеличивать число шагов.Что значит: сопоставление не удалось разрешить? Это значит, что при решении системы получилось уравнение вида
E : Le
, гдеE
— фрагмент аргумента, аLe
— фрагмент образца и с этим уравнением алгоритм решения справиться не может. (Частный случайE1 : E2
, возникающий при наличии двух присваиванийE1 ← var
иE2 ← var
можно трактовать как уравнение〈E1, E2〉 : 〈var, var〉
, не будем его подробно рассматривать.)Уравнение вида
var : Le
решить в каком-то смысле можно при любомLe
(при соответствии типа переменной, конечно) — его решением можно назвать сужениеvar → Le
(но есть тонкости с переменными). Решение для системы(var1) … (varN) : (Le1) … (LeN)
видаvar1 → Le1
, …,varN → LeN
можно подставить в L-образец, если порядок переменныхvar1
…varN
тот же, что и в системе уравнений. В том смысле, что при подстановке не должен меняться порядок следования открытых e-переменных (см. #249).Если решатель нашёл неразрешённое уравнение
E : Le
(или несколько таких уравнений), то в исходном аргументе можно заменитьE
на новую переменную и заново выполнить прогонку. Т.е., в каком-то смысле мы часть аргумента делаем динамической.Базовый вариант должен быть реализован в первую очередь. Любая пометка оптимизации (
$DRIVE
,$INLINE
,$SPEC
) это всего лишь совет компилятору оптимизировать функцию. Если оптимизация возможна, то она выполняется. Если нет, остаётся как есть. Такое решение в духе Си++, где ключевые словаinline
иregister
тоже являются исключительно советами. Если программист сам написал предложение, где статические параметры в совокупности отображаются в не-L-кортеж образцов, значит он готов к тому, что функция иногда специализироваться не сможет.Программа максимум: специализация без шаблона
Здесь предлагается специализируемые функции просто помечать ключевым словом
$SPEC
, например в форме$SPEC Map { … }
.Если реализована программа-минимум, то описание
должно считаться эквивалентным
Вообще, зачем нужен был шаблон в исходной постановке задачи? Изначально для выделения статических (шаблонных) параметров одновременно с желанием сохранить привычный синтаксис функции. Альтернативной могло быть использование особого синтаксиса для шаблонных параметров. Например, такого:
А так (а) сохраняется привычный вид специализируемой функции, (б) при невозможности специализации вызов функции выполняется обычным образом.
Что даёт шаблон, если мы разрешим прогонку статических параметров? Во-первых, шаблон автоматически обобщает некоторые участки аргумента как незначимые. Во-вторых, он разрешает динамическим частям образцов иметь любой вид без ограничений.
Например, в функции
есть открытые и повторные e-переменные и они ни сколько не мешают оптимизации. При специализации этой же функции без шаблона нужна нетривиальная поддержка образцов общего вида при прогонке и, по-видимому, механизм динамического обобщения аргумента. Например, в следующей гипотетической программе
возникнет нетривиальное уравнение
e.X e.Y e.Z : e.Text-B 'Foo' e.Text-E
, которое решить невозможно и придётся динамически обобщитьe.X e.Y e.Z
до одной переменной, скажем,e.0
.Т.е. специализацию без шаблона сделать можно, но она потребует сложный нетривиальный (и, по-видимому, медленный) анализ на стадии компиляции. Она может быть дополнением специализации с шаблоном, но не заменой ему.
The text was updated successfully, but these errors were encountered: