Skip to content
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

Оптимизация результатных выражений #16

Closed
Mazdaywik opened this issue Jan 23, 2016 · 2 comments
Closed
Labels
Milestone

Comments

@Mazdaywik
Copy link
Member

Текущая реализация компилятора использует классическое списковое представление данных. Это значит, что поле зрения (которое по определению является активным выражением) представляет собой единый двусвязный список, отдельным элементам которого соответствуют атомы и три вида скобок (скобки имеют дополнительные связи между собой). Также имеется и список свободных узлов, из которого берутся новые узлы для построения новых значений и куда помещаются использованные узлы. При недостатке узлов этот список автоматически пополняется новыми узлами, полученными путём выделения динамической памяти функцией malloc().

Выполнение функции условно можно разбить на два этапа: анализ аргумента и построение результата. Анализ осуществляется путём последовательного перебора левых частей предложений на предмет соответствия аргумента одному из образцов (анализ в настоящей задаче не рассматривается). Построение результата — формирование возвращаемого значения функции — представляет собой создание нового фрагмента двусвязного списка, который будет замещать список-вызов функции в поле зрения.

В текущей реализации результат формируется из переменных, присутствующих в образце и новых узлов (литеральных значений атомов и скобок), формируемых в списке свободных узлов. При этом, ожидаемо, что если в правой части у некоторой переменной вхождений больше, чем в левой, недостающие экземпляры переменной формируются в списке свободных узлов. Затем, оставшаяся часть вызова функции (скобки вызова функции + имя функции + остаток аргумента, из которого изъяны переменные для результатного выражения) переносится в список свободных узлов.

В данном случае есть две причины потери производительности:

  1. Размещаемые элементы переносятся из списка свободных узлов в поле зрения по одному за раз. То есть, для каждого нового узла перепровязываются ссылки «предыдущий»/«следующий» при изъятии из списка свободных узлов и вставке в поле зрения.
  2. Для построения результата не всегда требуется строить новые узлы в списке свободных узлов. Часто можно заново использовать фрагменты из вызова функции, а в отдельных частных случаях создание нового результатного выражения требует лишь небольшой правки вызова (даже без распределения новых узлов в списке свободных).

Примечание. Данное исследование уже проводилось в 2010 году (коммит 6af96b3, не в master’е). В ходе исследования удалось выяснить, что уйма времени тратится на перевязывание отдельных узлов, если переносить созданные элементы целыми фрагментами, то производительность резко повышается. Также исследование показало, что если сохранять ссылки на отдельные узлы аргумента и вместо нового создания в списке свободных узлов использовать такие же из аргумента, то статистически значимого прироста производительности это не даёт. Алгоритм, который позволяет минимальным числом правок преобразовать вызов функции в результат, не разрабатывался.

Минимизация числа переносов уже была реализована в Модульном Рефале, прирост производительности составил порядка 13 %. В основной ветке Простого Рефала такой оптимизации на данный момент нет, код из исследования 2010 года не применялся в основной ветке, поскольку написан относительно грязно и содержит кучу артефактов исследования.

Программа минимум — это минимизация количества переносов, должна быть реализована по-любому. Главное, что требуется реализовать — алгоритм, который преобразует вызов функции в её результат путём минимального числа модификаций.

Романенко в своей диссертации от 1978 года предлагает
алгоритм оптимизации построения результатных выражений, однако описывает его лишь в общих чертах.

Диссертация Романенко:

@Mazdaywik
Copy link
Member Author

Соображение

Оптимизация построения результатных выражений предполагает повторное использование фрагментов вызова функции для построения результата. Следовательно, (а) при сопоставлении образцов должны запоминаться позиции отдельных его фрагментов (хотя бы тех, которые будут использоваться в результате), (б) при генерации команд построения результата должна быть известна информация о том, какие части результата заимствуются из образца, какие создаются заново.

Сейчас

Сейчас (в состоянии без оптимизации) фрагментами, которые могут (и должны быть!) заимствованы из вызова функции — это переменные. Часть переменных в результатном выражении берутся из аргумента, часть должны быть скопированы. Информация о том, какие переменные должны быть скопированы (и какие не должны были сопоставляться) определяется уже после (!) анализа образцового и результатного выражений. Операции сопоставления с образцом фильтруются на предмет избыточных сопоставлений с закрытыми переменными (ранее они приводили к предупреждениям компилятора C++). Операции построения результата дополняются командами копирования.

Что надо сделать

Чтобы реализовать оптимизацию, нужно изменить подход. Возможны два варианта.

Либо функция сопоставления с образцом (GenPattern в HightLevelRASL.sref) должна на выходе возвращать (помимо последовательности команд) позиции всех элементов вызова функции (= < + имя функции + аргумент + >), при этом команды сопоставления должны будут сохранять позиции всех элеменов. После чего отдельная новая функция будет анализировать помеченный образец и результат, выдавая на выходе помеченный результат, на основе которого функция GetResult (там же) будет формировать команды построения результата, зная какие фрагменты результата уже в готовом (или полуготовом) виде присутствуют в образце.

Либо сначала отдельной функцией обходится и образец, и результат, при этом в том и другом помечаются, соответственно, сохраняемые и переиспользуемые фрагменты, а затем функции GenPattern и GenResult будут формировать, соответственно, команды сопоставления и команды построения. GenPattern сможет генерировать команды, сохраняющие позиции, только для тех фрагментов, которые будут переиспользоваться (следствие: могут даже не сохраняться позиции некоторых переменных).

Оба варианта приемлемы. Но надо помнить: задачей курсового является, прежде всего, написание той новой функции, которая будет выбирать для результата переиспользуемые фрагменты образца.

Mazdaywik added a commit that referenced this issue Mar 17, 2016
Данная модификация нужна для реализации обоих оптимизаций (#15, #16).
Mazdaywik added a commit that referenced this issue Mar 24, 2016
Во-первых, код стал более линейным, без кучи вложенных функций.
Во-вторых, переписан с использованием библиотеки GetOps.
В-третьих, появились опции, управляющие процессом компиляции:
* опция -O для включения оптимизации (-OP для образцов, -OR для результатов),
* опция --gen для выбора режима кодогенерации (both, direct или interp).

Пока новые опции никак не обрабатываются.
Mazdaywik added a commit that referenced this issue Apr 17, 2016
Команды сопоставления имели формат:

  (s.Command s.Direction s.Range e.Info)

Функция FreezeRanges ожидала, что номер диапазона будет третьим термом,
и, таким образом, поправляла его значение.

Новые команды сопоставления получили следующий формат:

  (s.Command s.ElemOffset s.Direction s.Range e.Info)

FreezeRange уже не могла поправлять номер диапазона.

Для исправления ошибки добавлен частный случай в функцию FreezeRange.
Mazdaywik added a commit that referenced this issue Apr 17, 2016
Пример кода ранее был ошибочным, теперь ошибка исправлена.

Формат команд сохранения должен быть:
  (s.Command s.Direction s.Range s.ElemOffset e.Info)
Важно: s.Range должен быть третьим термом — там его ожидает увидеть функция
FreezeRanges.
@Mazdaywik
Copy link
Member Author

Mazdaywik commented Jun 30, 2016

Результаты (кратко):

  • Использовался алгоритм жадного строкового замощения.
  • Оптимизация порядка 8…15 % (компонент «Linear result time» может уменьшаться даже двукратно).

Детали — в записке.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants