BC/NW 2012; №2 (21):3.2

 

РАЗРАБОТКА ПИРИНГОВОГО МЕТАКОМПИЛЯТОРА ДЛЯ ОРГАНИЗАЦИИ РАСПРЕДЕЛЁННОЙ КРОССПЛАТФОРМЕННОЙ КОМПИЛЯЦИИ ПРИКЛАДНЫХ ПРОГРАММ

 

Нгуен Т.В., Филатов А.В.

 

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

Однако, после появления на свет языков Java и C#, это определение компиляции расширилось. Компиляторы  также стали отвечать за формирование байт-кода для виртуальных машин. То есть, компиляторами языков, таких как Java и C#, стал формироваться исполняемый код некоторой виртуальной микроархитектуры, который позже может быть дополнительно преобразован в код конкретной микроархитектуры непосредственно перед его исполнением или же во время исполнения.

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

Несмотря на вышеизложенное, сам процесс компилирования программ остается прежним и, как правило, это достаточно продолжительная по времени процедура, которая довольно часто повторяется при отладке на стадии разработки приложений. Ранее, процесс компиляции не создавал ощутимых проблем с точки зрения затрачиваемого на него времени.  В частности этому способствовало разбиение кода программ на модули и применение выборочной компиляции (к примеру, при использовании утилиты GNU Make).

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

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

Также нельзя не упомянуть о развитии ПО с открытым исходным кодом. Это не только дало толчок развитию различных технологий совместной разработки - как программных, так и методологических - но также увеличило время перекомпиляции, так как изменения в коде стали охватывать большее количество программных модулей.

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

Доказательством актуальности рассмотренных проблем служит наличие рабочих решений, в том числе коммерческих. Для ускорения компиляции программ в этих решениях используется мощность вычислительных сетей. Эти средства существуют для основных операционных систем - GNU Linux (distcc), Mac OS X (такое средство  встроено в среду разработки XCode), Microsoft Windows (здесь известность обрело решение от фирмы Xoreax).

 Однако, эти средства лишены универсальности - они решают только проблему временных издержек, но не позволяют производить компиляцию для произвольной целевой платформы. Кроме того, вход в подобные вычислительные сети, как правило, является ограниченным. В них применяется классическая централизованная клиент-серверная архитектура - а значит, всегда есть некое управляющее звено, следящее за изменениями в сети. Это накладывает определенные ограничения на возможности масштабирования. Структура подобных систем обычно определена заранее и не подлежит изменению.

Таким образом, возникает задача кросс-платформенной компиляции в распределённой сети. Чтобы её решить, для каждой целевой платформы необходим свой набор инструментов (в частности компиляторов) и библиотек (toolchain). Исходя из того, что в подавляющем большинстве случаев у разработчика установлен компилятор только для его текущей операционной системы, то, для того, чтобы добиться универсальности сети компиляторов, она должна быть гетерогенна. В частности необходима неоднородность операционных систем входящих в ней узлов.

Для того чтобы решить задачу кросс-платформенной компиляции и при этом избежать проблем масштабирования, можно воспользоваться децентрализованной архитектурой вычислительной сети. Каждый из ее участников будет на добровольной основе выделять определенное процессорное время на распределенную компиляцию.

Таким образом, пользователи подобной сети будут одновременно поставщиками ее вычислительных ресурсов. Более того, ввиду наличия внутри сети различных компиляторов, можно говорить о понятии метакомпиляции (аналогия с метакомпьютингом) - пользователь указывает “что нужно получить”, а система уже сама решает, “как этого добиться” и какие ресурсы (аппаратные и программные) для этого задействовать. Также достоинство подобной системы заключается в простоте масштабирования. В ней отсутствует центральное управляющее звено, вычислительная мощность которого должна расти пропорционально количеству узлов системы

Рассмотрим основные проблемы, которые возникают при работе подобной распределенной системы. В качестве средств сборки программ будут использованы компиляторы языка С\С++ для разных платформ.

1. Поиск ресурсов. Как и в любой другой сети, в которой отсутствует центральный индексирующий элемент, поиск необходимых узлов для выполнения некоторого распределенного задания является нетривиальной задачей. Структура сети постоянно меняется, поэтому поисковый алгоритм должен каким-то образом обходить каждый раз все доступные ему узлы.

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

Далее происходит каскадная рассылка уведомления о поступлении задания в систему, вместе с информацией о целевой платформе. Узлы, которые могут выполнить компиляцию для указанной платформы, после этого начинают запрашивать новые части задания и исполнять их. При этом распределяется только компиляция, компоновка программы происходит на некотором выбранном узле, который имеет нужные библиотеки и заголовочные файлы.

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

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

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

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

Кроме того, в самом стандарте есть некоторые положения, которые не документированы строго и оставляются на усмотрение производителя компилятора. Поэтому, нужно применять правило обратной совместимости - выполнять задание могут только те узлы, у которых есть компилятор версии не ниже той, что используется на компонующем узле.

Авторами разработана система, способная распределять компиляцию программ, написанных на языках С и С++. Исходный узел анализирует стандартные файлы сборки проектов формата makefile и рассчитывает приблизительную трудоемкость каждого компилируемого файла по описанному выше способу, с учетом выборочной компиляции. Затем эти подзадачи объединяются в группы определенного размера и выполняются параллельно на подходящих заданию узлах. Для обеспечения взаимодействия между узлами в децентрализованной системе был разработан протокол сетевых сообщений, учитывающий ее постоянно меняющуюся структуру. Особенность рассмотренной задачи компиляции заключается в том, что время, затрачиваемое на рассылку части задания, достаточно мало по сравнению со временем ее исполнения. Поэтому при экспериментах было получено почти линейное ускорение компиляции. Отметим, что в теории система может работать с любыми компиляторами, так как для описания процесса компиляции используются стандартные makefile. На практике работа системы проверена только на широко распространенных компиляторах  GCC и Microsoft C Compiler.

В заключение отметим, что подобный способ разбиения и распределенного исполнения задач можно применить не только к компиляции программ.  Децентрализованную сеть вычислительных узлов можно использовать для решения любой проблемы, которая в достаточной степени поддается разбиению на независимые подзадачи. Это может быть рендеринг видео, компьютерная обработка каких-либо научных данных или любой другой вид обработки информации, предполагающий наличие однородного дискретного входного потока. Особенность рассмотренной задачи компиляции заключается в том, что время, затрачиваемое на рассылку части задания, достаточно мало по сравнению со временем ее исполнения.

ЛИТЕРАТУРА

1.      Alfred V., Monica S., Ravi S., Jeffrey D. Compilers: Principles, Techniques, and Tools. — Addison Wesley, 2006. —  1038 с.

2.      Стивенс У. UNIX: разработка сетевых приложений. — СПб.: Питер, 2003. —  1088 с.