Analysis of the Algorithms of the Constituent Parts of the Compiler and its Optimization
- Authors: Kharin I.A.1, Raskatova M.V.1
-
Affiliations:
- National Research University “MEI”
- Issue: Vol 10, No 2 (2023)
- Pages: 26-35
- Section: MATHEMATICAL AND SOFTWARE OF COMPUTЕRS, COMPLEXES AND COMPUTER NETWORKS
- URL: https://journals.eco-vector.com/2313-223X/article/view/568056
- DOI: https://doi.org/10.33693/2313-223X-2023-10-2-26-35
- EDN: https://elibrary.ru/BDGKMA
- ID: 568056
Cite item
Full Text
Abstract
Program optimization arose as a response to the emergence of high-level programming languages, and includes special techniques and methods used in building compilers to produce sufficiently efficient object code. A combination of these techniques constituted in the past and are now an integral part of so-called optimizing compilers, the purpose of which is to create object code, saving computer resources such as processor time and memory. For modern supercomputers, the requirement to make proper use of hardware features is also added. In this context, issues related to compiler optimization deserve special attention, which may involve adapting the compiler to reduce runtime or object size, or both. In view of the above, the aim of the paper is to analyze the algorithms of the compiler constituents and outline ways to optimize it. The general technology of the compiler is briefly characterized. Particular attention is paid to the main functions of the algorithms, which are implemented at different stages of the compiler’s work. The possibilities of using machine learning to optimize compilers are also considered.
Keywords
Full Text
ВВЕДЕНИЕ
Языки программирования высокого уровня предлагают множество абстрактных конструкций написания кода, таких как функции, условные операторы и циклы, которые открывают широкие возможности для системной архитектуры, увеличения скорости работы, сокращения времени настройки [Штейнберг, 2021]. Однако одним из недостатков написания кода на языке программирования высокого уровня является потенциально существенное снижение продуктивности. В данном контексте особую значимость и важность приобретают компиляторы, которые позволяют автоматически оптимизировать код для повышения его производительности. Они могут преобразовывать циклы, условные операторы и рекурсивные функции, удалять целые блоки кода и использовать преимущества целевой архитектуры набора команд (ISA), чтобы сделать код быстрым и компактным [Manish, Zoran, 2022].
Оптимизирующие компиляторы являются основой современного программного обеспечения: они позволяют программисту писать код на понятном ему языке и при этом преобразовывать его в форму, подходящую для эффективной работы базового оборудования. Архитектура этих средств влияет на скорость и безошибочность их работы. В данном случае скорость является критически важной, поскольку в процессе разработки программных продуктов компилирование выполняется очень часто и напрямую влияет на их стоимость.
Компиляторы программ могут применяться на любом этапе, например, как элемент более крупного компилятора (например, Scala Optimizer), либо как отдельная самостоятельная программа, которая запускается перед выполнением (например, Proguard) или как часть среды выполнения, которая оптимизирует программу во время ее выполнения (например, JIT-компилятор JVM) [Баглий, Кривошеев, Штейнберг, 2022]. Ограничения, накладываемые на компиляторы, в каждом случае, различны, но их выходная цель одинакова: взять входную программу и преобразовать ее в выходную, которая делает то же самое, но быстрее.
На сегодняшний день оптимизация компиляторов для современных архитектур достигла высокого уровня сложности. Используемые в настоящее время компиляторы развились до такой степени, что предоставляют пользователю большое количество опций. Например, компиляторы GCC включают 38 опций, сгруппированных в три уровня оптимизации, от O1 до O3 [Болотнов, Нурисламова, 2019]. С другой стороны, оптимизации компилятора могут взаимодействовать самым непредсказуемым образом. Несмотря на то, что компиляторы позволяют достигнуть значительных улучшений во многих программах, потенциал снижения производительности в определенных моделях программ известен как разработчикам компиляторов, так и многим пользователям.
Современное состояние дел таково, что на практике эта проблема решается с помощью опций компилятора. Наличие опций компилятора отражает неспособность современных оптимизаторов сделать наилучший выбор во время компиляции. Недоступность входных данных программы и недостаточное знание целевой архитектуры могут сильно ограничивают точность моделей производительности во время компиляции.
С учетом вышеизложенного, определение наилучшей комбинации оптимизаций компилятора для конкретной программы или ее раздела является важной научно-практической задачей. Кроме того, усовершенствования требует алгоритм оркестрации (процесс объединения, сортировки и представления результатов, полученных из различных систем и алгоритмов машинного обучения), который позволит подобрать наилучшую комбинацию оптимизации для компилятора.
Таким образом, комплекс обозначенных задач предопределяет необходимость интенсификации научных исследований в данной предметной плоскости, что и обуславливает выбор темы статьи.
Анализу существующих средств создания и методов разработки компиляторов посвятили свои работы А.И. Стрелец, Е.А. Черникова, Л.В. Малков, А.И. Дождев, Дж. Джанг (J. Zhang), З. Чжу (Z. Zhu), Х. Ванг (H. Wang), Х. Джианг (H. Jiang), Ч. Панг (Ch. Pang), Ф. Вердуго (F. Verdugo), С. Бадиа (S. Badia).
Обзор существующих методов оптимизации компиляторов, а также средств динамического анализа программ представлен в трудах Н.И. Вьюковой, В.А. Галатенко, С.В. Самборского, А.Н. Андрианова, Т.П. Баранова, С.Н. Агатоса (S.N. Agathos), В.В. Димакополуса (V.V. Dimakopoulos), И.К. Касмеридиса (I.K. Kasmeridis).
Преимущества использования динамической компиляции в интерпретируемых языках программирования детально изучаются Р.В. Баевым, Л.В. Скворцовым, Е.А. Кудряшовым, Р.А. Бучацким, Р.А. Жуйковым, М. Баттаглией (M. Battaglia), А. Калахорано-Ди Парте (A. Calahorrano-Di Patre), Э.Ф. Финдерсом (A.F. Flinders).
Однако, несмотря на имеющиеся труды и наработки, а также высокий интерес со стороны ученых и практиков к рассматриваемым вопросам, ряд дискуссионных аспектов остается невыясненным. В частности, особого внимания заслуживают проблемы усовершенствования методов динамической компиляции в сочетании с рядом оптимизации разного уровня и параллелизмом обработки данных. Также в уточнении нуждаются методы оптимизации компиляторов и их наиболее эффективная комбинация.
Итак, принимая во внимание изложенное выше, цель статьи можно сформулировать следующим образом – провести анализ алгоритмов составляющих частей компилятора и обозначить перспективные методы его оптимизации.
Методы решения рассмотренной задачи – анализ, синтез, сравнение, группировка, обобщение, систематизация.
АЛГОРИТМЫ ФАЗЫ АНАЛИЗА КОМПИЛЯТОРА
Компилятор работает на разных этапах, каждый этап преобразует исходную программу из одного представления в другое. Каждая фаза берет входные данные с предыдущей стадии и передает свои выходные данные на следующую фазу компилятора [Вьюкова, Галатенко, Самборский, 2020]. Укрупненно выделяют две фазы – анализа и синтеза (рис. 1).
Рис. 1. Фазы работы компилятора [Aschwanden, 2021]
Fig. 1. Compiler phases [Aschwanden, 2021]
Известная как внешний интерфейс компилятора, фаза анализа, читает исходную программу, разделяет ее на основные части и затем проверяет на наличие лексических, грамматических и синтаксических ошибок.
Известная как серверная часть компилятора, фаза синтеза генерирует целевую программу с помощью промежуточного представления исходного кода и таблицы символов.
Фаза анализа включает в себя следующие алгоритмы: алгоритм лексического анализа, синтаксического анализа или разбора, а также семантического анализа.
Лексический анализ или лексический анализатор является начальным этапом компилятора. Эта фаза сканирует исходный код и преобразует входную программу в серию токенов. Сканирование происходит по одному символу за раз. Как только лексический анализатор определяет конец лексемы, он преобразует лексему в токен. Таким образом, входной код трансформируется в последовательность токенов. Токен – это осмысленная группа символов из исходного текста, которую компилятор распознает [Советов, 2019]. Затем лексический анализатор передает эти лексемы на следующий этап компилятора. Сканирование удаляет из входного потока нетоковые структуры, такие как комментарии, ненужные белые пробелы и т.д. Программа, реализующая лексический анализ, называется лексер, лексический анализатор или сканер.
На рис. 2 представлен пример работы лексера.
Синтаксический анализ – вторая фаза в компиляторе, получает на вход поток токенов, в соответствии с которым на выходе формируется дерево разбора. Дерево разбора генерируется с помощью заранее определенных правил грамматики языка, на который ориентирован компилятор. Этап синтаксического анализа также известен как иерархический анализ, или синтаксический разбор [Малявко, 2019]. Пример синтаксического анализа представлена на рис. 3.
Рис. 2. Пример работы лексического анализатора
Fig. 2. An example of how a lexical analyzer works
Рис. 3. Пример синтаксического анализа
Fig. 3. Parsing example
Семантический анализ. На этом этапе процесса компиляции семантический анализатор проверяет, соответствует ли дерево разбора, которое он получил на вход, правилам языка, на который нацелен компилятор. Семантический анализатор также записывает все идентификаторы, их типы, выражения и т.д. Фаза семантического анализа генерирует на выходе аннотированный синтаксис дерева. Семантика языка делает его конструкции, такие как лексемы и синтаксические структуры, осмысленными. Прохождение данного этапа позволяет интерпретировать символы, их типы и отношения между ними [Стрелец, Черникова, Малков, Дождев, 2019].
АЛГОРИТМ ГЕНЕРАЦИИ ЦЕЛЕВОГО КОДА
Генерацию кода можно рассматривать как заключительную фазу компиляции. Код, сгенерированный компилятором, представляет собой объектный код какого-либо языка программирования более низкого уровня, например, языка ассемблера [Третьяк, 2021]. Исходный код, написанный на языке более высокого уровня, преобразуется в язык более низкого уровня, в результате чего получается объектный код, который должен обладать следующими минимальными свойствами:
1) он должен нести точный смысл исходного кода;
2) он должен быть эффективным с точки зрения использования процессора и управления памятью.
Алгоритм генерации исходного кода включает в себя следующие этапы:
- вход: оптимизированное промежуточное представление;
- выход: целевой код;
- выполняемое задание: методы распределения и оптимизации регистров;
- метод: стратегии распределения и оптимизации регистров.
Каждая строка оптимизированного кода может соответствовать одной или нескольким строкам машинного (или ассемблерного) кода, следовательно, существует отображение 1:N, связанное с ними (рис. 4).
Рис. 4. Сопоставление оптимизированного и исходного кода [Wang, 2018]
Fig. 4. Matching optimized and source code [Wang, 2018]
ОБЩАЯ ОПТИМИЗАЦИЯ КОМПИЛЯТОРА
В настоящее время существует множество методов оптимизации компилятора, начиная от простых преобразований, таких как свертывание констант, и заканчивая экстремальными преобразованиями, такими как планирование инструкций. Следует акцентировать внимание на том, что в настоящее время не существует универсального решения, работающего хорошо во всех случаях, поэтому инженеры используют компромиссные решения для оптимизации только ключевых параметров [Tang, Zhide, 2021].
В рамках проводимого исследования рассмотрим более подробно несколько методов оптимизации компилятора, которые получили широкое распространение.
Встраивание функций. Встраивание функций – это процесс устранения накладных расходов, связанных с переходом к функции и возвратом из нее, путем встраивания кода функции везде, где она вызывается. Это классический пример компромисса между пространством и временем. Код может работать более эффективно, если ему не нужно прыгать по кругу, чтобы добраться до определения функции [Chen, 2022].
Оптимизация по профилю. При оптимизации на основе профиля создается профиль программы путем передачи информации о поведении программы обратно компилятору для будущих компиляций. Компилятор может делать такие вещи, как замена пункта if else, если первая часть выполняется чаще, либо же распределение регистров на основе количества раз использования переменной при выполнении программы. Он также может делать то, что называется «разворачиванием цикла», которое эффективно «разворачивает» цикл, копируя его для каждой итерации в тех случаях, когда циклы выполняются наиболее часто [Sampson, Adit, 2022].
Третий метод заключается в том, чтобы сделать все рекурсивные функции хвостовыми рекурсивными. При такой оптимизации код изменяется таким образом, что переменные и значения не нужно хранить между рекурсивными вызовами [Huang; Xie, 2021].
Метод устранения избыточных конструкций. На уровне исходного кода пользователь может благодаря этому методу сделать следующее (рис. 5).
Рис. 5. Пример устранения лишних инструкций
Fig. 5. Example of eliminating unnecessary instructions
Компиляция на основе машинного обучения. Получив программу, разработчики решают задачу о том, какую эвристику или оптимизацию применить, чтобы сделать код лучше. Улучшение часто означает более быстрое выполнение, но также может означать уменьшение объема кода или снижение энергопотребления (затраты энергии, необходимые для выполнения кода). Более эффективный код всегда менее энергозатратен. Машинное обучение может быть использовано для построения модели, используемой в компиляторе, которая принимает такие решения для любой конкретной программы. Данный метод оптимизации компилятора представляется как один из наиболее прогрессивных, поэтому рассмотрим его более подробно.
МАШИННОЕ ОБУЧЕНИЕ В ОПТИМИЗАЦИИ КОМПИЛЯТОРОВ
Машинное обучение предсказывает результат для новой точки данных на основе предыдущей информации. В самом простом виде обозначенную операцию можно считать интерполяцией. Эта способность предсказывать на основе уже имеющихся сведений может быть использована для поиска точки данных с наилучшим результатом, что в свою очередь тесно связано с областью оптимизации. Именно на этом пересечении взгляда на улучшение кода, как на проблему оптимизации, и машинного обучения, как на предсказание оптимума, заложен значительный потенциал компиляции машинного обучения.
Рис. 6. Общий взгляд на машинное обучение в компиляторах:
а – разработка функции; b – обучение модели; с – развертывание
Fig. 6. A general view of supervised machine learning in compilers
а – feature engineering; b – learning model; с – deployment
Рисунок 6 дает интуитивное представление о том, каким образом машинное обучение может быть применено к оптимизации компиляторов.
Использование машинного обучения при оптимизации компиляторов включает в себя 3 взаимосвязанных фазы: разработку функций, обучение модели и развертывание (см. рис. 6). Рассмотрим их более подробно.
Разработка функций
Машинное обучение опирается на набор количественно измеримых свойств или признаков для характеристики программ (рис. 6, a). Существует множество различных характеристик, которые могут быть использованы на практике. К ним относятся статические структуры данных, извлеченные из исходного кода программы или промежуточного представления компилятора (например, количество инструкций или ветвлений), информация динамического профилирования (например, значения счетчиков производительности), полученная во время выполнения программы, или комбинация того и другого.
Стандартные алгоритмы машинного обучения обычно работают с входными данными фиксированной длины, поэтому выбранные свойства будут обобщены в вектор признаков фиксированной длины. Каждый элемент вектора может быть целым, вещественным или булевым значением. Процесс выбора и настройки признаков называется инженерией признаков. Этот процесс может потребовать многократных итераций, чтобы найти набор высококачественных признаков для построения точной модели машинного обучения.
Обучение модели
Вторым шагом является использование обучающих данных для построения модели с помощью алгоритма обучения. Этот процесс показан на рис. 6, b. Разработчик компилятора может выбрать обучающие программы, которые типичны для прикладной области. Для каждой обучающей программы рассчитываются значения признаков, компилируется программа с различными вариантами оптимизации, осуществляется запуск и хронометраж скомпилированных двоичных файлов, чтобы определить наилучший вариант. В результате этого процесса для каждой обучающей программы создается экземпляр, состоящий из значений характеристик и оптимального варианта компилятора для программы.
Затем эти примеры помещаются в алгоритм машинного обучения для автоматического построения модели. Задача алгоритма обучения – найти на основе обучающих примеров корреляцию между значениями характеристик и оптимальным решением по оптимизации. Полученная модель может быть использована с целью предсказания наилучшего варианта оптимизации для нового набора характеристик. Поскольку производительность обучаемой модели сильно зависит от того, насколько правильно выбраны признаки и обучающие программы, процессы разработки признаков и создания обучающих данных часто приходится повторять несколько раз [Wang, 2018].
Критически большое значение для получения высокого результата в процессе использования машинного обучения для оптимизации компилятора имеет выбор наиболее эффективной и адаптивной модели машинного обучения. В табл. 1 представлено описание наиболее распространенных моделей.
Итак, существует две основных группы методов машинного обучения, которые могут использоваться для оптимизации компиляторов: контролируемое и неконтролируемое обучение.
При использовании контролируемого машинного обучения, прогностическая модель обучается на эмпирических данных о производительности (помеченные выходы) и важных количественных свойствах (признаки) репрезентативных программ. Модель изучает корреляцию между значениями этих характеристик и оптимизационным решением, которое обеспечивает оптимальную (или почти оптимальную) производительность. Полученные корреляции используются для предсказания лучших решений для оптимизации новых программ. В зависимости от характера выходных данных, прогностическая модель, может быть, либо регрессионной для непрерывных выходов, либо моделью классификации для дискретных выходов.
При неконтролируемом обучении, входными данными для алгоритма является только набор исходных значений – выходных данных нет. Одной из форм неконтролируемого обучения является кластеризация, которая группирует входные данные на несколько подмножеств. Например, можно использовать кластеризацию для выбора точек выполнения программы. Для этого сначала набор информации о времени выполнения программы делится на группы (или кластеры), так что точки внутри каждого кластера похожи друг на друга с точки зрения структуры программы (циклы, использование памяти и т.д.); затем выбирается несколько точек из каждого кластера, чтобы представить все точки моделирования в этой группе без потери информации.
Таблица 1. Методы машинного обучения для оптимизации компилятора [Machine learning methods for compiler optimization]
Подход [Approach] | Метод [Method] | Области применения [Application] | Модели [Domains Models] |
Контролируемое обучение [Supervised learning] | Классификация [Classification] | Используется для моделирования непрерывных величин, например, для оценки времени выполнения, ускорения, энергопотребления, задержки и т.д. [Useful for modelling continuous values, such as estimating execution time, speedup, power consumption, latency etc.] | Линейная/нелинейная регрессия, искусственные нейронные сети (ИНС), опорные векторы (SVM) [Linear/non-linear regression, artificial neural networks (ANNs), support vector machines (SVMs)] |
Регрессия [Regression] | Полезно для прогнозирования дискретных значений, например, при выборе флага компилятора, #threads, коэффициентов разворачивания цикла, алгоритмических реализаций [Useful for predicting discrete values, such as choosing compiler flags, #threads, loop unroll factors, algorithmic implementations etc.] | K-ближайший сосед (KNN), дерево решений, логическая регрессия, SVM, ядерно-канонический корреляционный анализ, байесовский анализ [K-nearest neighbour (KNN), decision trees, random forests, logical regression, SVM, Kernel Canonical Correlation Analysis, Bayesian] | |
Неконтролируемое обучение [Unsupervised learning] | Кластеризация [Clustering] | Анализ данных, например группировка трассировок профилирования в кластеры аналогичного поведения [Data analysis, such as grouping profiling traces into clusters of similar behavior] | K-средние, быстрая кластеризация Ньюмана [K-means, Fast Newman clustering] |
Разработка функций [Feature engineering] | Уменьшение размеров функций, поиск полезных представлений функций [Feature dimension reduction, finding useful feature representations] | Анализ главных компонент (PCA), автоэнкодеры [Principal component analysis (PCA), autoencoders] | |
Онлайн-обучение [Online learning] | Поиск и самообучение [Search and self-learning] | Используется для исследования большого пространства оптимизации, адаптации во время выполнения, динамического планирования задач, где оптимальный результат достигается с помощью ряда действий [Useful for exploring a large optimisation space, runtime adaption, dynamic task scheduling where the optimal outcome is achieved through a series of actions] | Генетический алгоритм (GA), генетическое программирование (GP), обучение с подкреплением (RL) [Genetic algorithm (GA), genetic programming (GP), reinforcement learning (RL)] |
Существуют также методы, которые находятся на границе контролируемого и неконтролируемого обучения. Это методы онлайн обучения, которые уточняют знания, полученные в ходе автономного обучения или предыдущих запусков, используя эмпирические наблюдения, сформированные во время развертывания. Примером метода из этой группы являются эволюционные алгоритмы или эволюционные вычисления, такие как генетические алгоритмы, генетическое программирование и стохастический поиск. Они используются для нахождения хорошего оптимизационного решения из большого пространства поиска.
Развертывание
На последнем этапе изученная модель вставляется в компилятор для предсказания лучших решений по оптимизации новых программ. Это показано на рис. 6, c. Чтобы сделать предсказание, компилятор сначала извлекает характеристики входной программы, а затем передает извлеченные значения характеристик в обученную модель.
В качестве примера, иллюстрирующего описанные выше шаги использования машинного обучения в компиляторах, можно привести уплотнение потоков для программ на GPU (графический процессор). Эта техника преобразования кода работает путем передачи нескольких рабочих объектов (или рабочих элементов) одному потоку. Она похожа на разворачивание цикла, но применяется к параллельным рабочим элементам, а не к последовательным итерациям цикла.
На рис. 7, a показано простое ядро OpenCL, в котором поток одновременно работает над рабочим элементом одномерного входного массива in. Рабочий элемент, над которым производится операция, задается значением, возвращаемым API OpenCL get_global_id(). На рис. 7, b показан преобразованный код после применения коэффициента уплотнения потоков, равного двум, где каждый поток обрабатывает два элемента входного массива.
Рис. 7. Уплотнение потока OpenCL:
a – оригинальное ядро OpenCL; b – преобразование кода с коэффициентом уплотнения 2
Fig. 7. Compacting an OpenCL thread:
a – the original OpenCL core; b – code conversion with compaction factor 2
Итак, оригинальный код OpenCL показан на рис. 7, a, где каждый поток берет квадрат одного элемента входного массива. При уплотнении в два раза (рис. 7, b) каждый поток теперь обрабатывает два элемента входного массива.
Преимущество подхода, основанного на машинном обучении, заключается в том, что весь процесс построения модели можно легко повторить, когда компилятору потребуется новая аппаратная архитектура, операционная система или область применения. Построенная модель полностью основана на экспериментальных результатах и, следовательно, является доказательной.
На следующем этапе исследования представляется целесообразным сравнить производительность компилятора, основанного на машинном обучении и обычных компиляторов. Для этого будем использовать CompilerGym с подкреплением искусственного интеллекта и обычные Autophase и OpenTuner. Оценку будем проводить на примере вычислительной эффективности среды фазового упорядочивания.
На рис. 8 показан цикл взаимодействия для среды CompilerGym. CompilerGym реализован на смеси Python и C++. Ядро среды выполнения состоит из 12 тысяч строк кода. Интеграция компиляторов включает 6 тысяч строк кода для LLVM, 3 тысячи для GCC и 0,5 тысячи для loop_tool. CompilerGym имеет открытый исходный код и доступен по разрешительной лицензии.
Рис. 8. Цикл взаимодействия CompilerGym
Fig. 8. Cycle of CompilerGym interaction
Как свидетельствует рис. 8 среда CompilerGym представляет собой пространство наблюдений, результатов и действий. Цель пользователя – выбрать действие, которое приведет к наибольшему суммарному результату. Это может быть сделано с помощью ручной эвристики, поиска, контролируемого машинного обучения или обучения с подкреплением.
В ходе вычислений будем измерять вычислительную эффективность каждой из выбранных сред путем измерения времени выполнения операций во время 1M (1 млн) случайных траекторий. В табл. 2 представлены результаты сравнения.
Анализ данных табл. 2 позволяет сделать вывод, что CompilerGym работает в 27 раз быстрее, чем другие компиляторы. Такая высокая пропускная способность позволяет обучать более крупные модели на больших наборах данных.
Итак, полученные данные свидетельствует о том, что CompilerGym достигает гораздо большей пропускной способности, чем Autophase, предлагая при этом тот же интерфейс, пространство наблюдения и сигнал результатов. Это достигается благодаря клиент-серверной архитектуре CompilerGym. После первоначального считывания и разбора файла битового кода с диска сервер Compiler-Gym последовательно применяет индивидуальный проход оптимизации на каждом шаге. В отличие от этого, Autophase и OpenTuner должны на каждом шаге считывать и разбирать IR (промежуточную репрезентативность), применять всю последовательность проходов, а затем сортировать результат. OpenTuner, который был разработан для тех случаев, когда время поиска доминирует над временем компиляции, имеет самую высокую стоимость инициализации среды, так как требует нескольких дисковых операций и создания базы данных. Сервер CompilerGym поддерживает кэш разобранных неоптимизированных битовых кодов, что позволяет амортизировать затраты на инициализацию среды Ϭ(1).
Таблица 2. Вычислительные затраты на операции различных компиляторов [Computational costs for operations of various compilers]
Запуск службы, мс [Service Startup, ms] | Инициализация среды, мс [Environment Initialization, ms] | Шаг среды, мс [Environment Step, ms] | |||||||||||
Cost | p50 | p99 | μ | Cost | p50 | p99 | μ | Cost | p50 | p99 | μ | ||
Autophase | – | – | – | – | Ϭ(n) | 22,4 | 388,4 | 53,3 | Ϭ(nm) | 71,0 | 2489,8 | 205,9 | |
OpenTuner | – | – | – | – | Ϭ(n) | 269,6 | 8515,3 | 777,5 | Ϭ(nm) | 50,7 | 1491,1 | 131,2 | |
CompilerGym | Ϭ(1) | 119,7 | 131,8 | 120,8 | Ϭ(1)† | 2,2 | 198,6 | 21,3 | Ϭ(n) | 1,0 | 108,6 | 7,5 | |
Примечание. Cost – средние временные сложности по отношению к n – размеру компилируемой программы и m – количеству действий в эпизоде; † – амортизированная стоимость, достигаемая за счет кэширования начальных состояний среды; p50 и p99 – 50-й и 99-й процентили времени ожидания, соответственно; μ – среднее арифметическое время ожидания.
ВЫВОДЫ
В процессе исследования проведен анализ общей технология работы компилятора. Отдельное внимание уделено рассмотрению основных функции алгоритмов, входящих в состав разных этапов работы компилятора, обозначены общие характеристики этих алгоритмов. Также описаны некоторые методы оптимизации компиляторов, поскольку оптимизация компилятора влияет на основные критериев его оценки, такие как как эффективность, скорость работы и обнаружение ошибок при сборке.
Кроме того, отдельное внимание уделено методу оптимизации компиляторов с использованием машинного обучения. В данном контексте отдельно рассмотрена взаимосвязь между машинным обучением и оптимизацией компилятора. Также представлена методология, описывающая как машинное обучение можно применить к компиляторам, в контексте трех фаз: разработка функций, обучение модели и развертывание. В качестве практического примера действенности машинного обучения для оптимизации компиляторов приведено уплотнение потоков для программ на GPU и сравнение трех сред Autophase, CompilerGym, OpenTuner на предмет вычислительной эффективности каждой среды путем измерения времени выполнения операций во время 1М случайных траекторий.
About the authors
Ilya A. Kharin
National Research University “MEI”
Author for correspondence.
Email: xarin.ilya@bk.ru
postgraduate student at the Department of Computing Machines, Systems and Networks of the National Research University “MEI”
Russian Federation, MoscowMarina V. Raskatova
National Research University “MEI”
Email: marvp@yandex.ru
Candidate of Engineering; associate professor at the Department of Computing Machines, Complexes and Systems of the National Research University “MEI”
Russian Federation, MoscowReferences
- Aschwanden P. CcNav: Understanding compiler optimizations in binary code. IEEE Transactions on Visualization and Computer Graphics. 2021. Vol. 27. No. 2. Pp. 667–677.
- Chen Ge. CRAC: An automatic assistant compiler of checkpoint/restart for OpenCL program. Concurrency and Computation: Practice and Experience. 2022. Vol. 34. No. 8. Pp. 14–22.
- Huang Ya., Xie B. Fine-grained compiler identification with sequence-oriented neural modeling. IEEE Access: Practical Innovations, Open Solutions. 2021. Vol. 9. Pp. 49160–49175.
- Sampson A., Adit N. Performance left on the table: An eva-luation of compiler autovectorization for RISC-V. IEEE Micro. 2022. Vol. 42. No. 5. Pp. 41–48.
- Tang Yi., Zhou Zh.. Detecting compiler warning defects via diversity-guided program mutation. IEEE Transactions on Software Engineering. 2021. Vol. 48. No. 11. Pp. 4411–4432.
- Tewary M., Salcic Z. Compiler-assisted energy reduction of java real-time programs. Microprocessors and Microsys-tems. 2022. Vol. 89. No. 3. Pp. 78–83.
- Wang Zh. Machine learning in compiler optimization. Proceedings of the IEEE. 2018. Vol. 106. No. 11. Pp. 1879–1901.
- Baglii A.P., Krivosheev N.M., Steinberg B.Y. Automation of program paralleling with data transfer optimization. Scientific Service on the Internet. 2022. No. 24. Pp. 81–92. (In Rus.)
- Bolotnov A.M., Nurislamova E.A. The influence of GCC compiler optimization on program code efficiency in C++. Modern Science-Intensive Technologies. 2019. No. 12-2. Pp. 266–270. (In Rus.)
- Vyukova N.I., Galatenko V.A., Samborsky S.V. Means of dynamic program analysis in GCC and CLANG compilers. Programming. 2020. No. 4. Pp. 46–64. (In Rus.)
- Malyavko A.A. Error handling in the parser of EL compiler. Scientific Vestnik of Novosibirsk State Technical University. 2019. No. 2 (75). Pp. 37–48. (In Rus.)
- Sovetov P.N. Iterative approach using a compiler to synthesize and model a problem-oriented instruction set. International Journal of Open Information Technologies. 2019. Vol. 7. No. 10. Pp. 14–21. (In Rus.)
- Strelets A.I., Chernikova E.A., Malkov L.V., Dozhdev A.I. Structure of the compiler of a one-time program. International Journal of Humanities and Natural Sciences. 2019. No. 1-1. Pp. 146–147. (In Rus.)
- Tretiak A.V. The importance of indentation in the development of lexical analyzer compilers. Molodezh. Science. Innovations. 2021. Vol. 1. Pp. 306–309. (In Rus.)
- Steinberg B.J. Transformations of programs – fundamental basis for creating optimizing parallelizing compilers. Software Systems: Theory and Applications. 2021. Vol. 12. No. 1 (48). Pp. 21–113. (In Rus.)
Supplementary files








