Библиотека сайта rus-linux.net
Ninja
Глава 3 из книги "Производительность приложений с открытым исходным кодом".
Оригинал: Ninja
Автор: Evan Martin
Дата публикации: 21 Сентября 2012 г.
Перевод: А.Панин
Дата перевода: 25 Декабря 2013 г.
Ninja является системой сборки проектов, аналогичной системе Make. В качестве ее входных данных используются команды, необходимые для преобразования исходных файлов в целевые файлы. Ninja использует эти команды для поддержания целевых файлов в актуальном состоянии. В отличие от многих других систем сборки проектов, главная цель процесса проектирования системы Ninja заключалась в достижении максимальной скорости работы.
Я разрабатывал Ninja во время работы над Google Chrome. Работа над Ninja была начата в рамках эксперимента, нацеленного на установление того, может ли быть ускорена сборка Chrome. Для возможности успешной сборки Chrome была добавлена дополнительная цель процесса проектирования: система Ninja должна была без лишних сложностей интегрироваться в состав более масштабной системы сборки проектов.
Система Ninja развивалась достаточно успешно, последовательно заменяя другие системы сборки проектов, используемые в Chrome. После представления системы Ninja широкой общественности сторонними разработчиками был создан код для генерации популярной системой сборки проектов CMake файлов Ninja - с этого момента система Ninja использовалась также для разработки таких проектов на основе CMake, как LLVM и ReactOS. Другие проекты, примером которых может служить TextMate, непосредственно использовали систему Ninja в процессе создания специальных сборок.
Я работал над Chrome с 2007 до 2012 года, а разработку системы Ninja начал в 2010 году. Существует множество факторов, влияющих на производительность процесса сборки такого значительного проекта, как Chrome (на сегодняшний день исходный код проекта состоит из 40000 файлов кода на языке C++, на основе которых генерируется бинарный файл размером около 90МБ). Во время работы я исследовал многие из них, начиная от распределения процесса компиляции между несколькими машинами и заканчивая различными приемами, направленными на оптимизацию процесса связывания. Система Ninja в большей степени использует единственный фактор - фронт сборки. Этот параметр характеризует период времени ожидания с начала сборки до момента выполнения первой операции компиляции. Для понимания важности этого фактора необходимо понять сформулированные нами принципы достижения высокой производительности самого браузера Chrome.
Небольшой экскурс в историю Chrome
Дискуссия обо всех целях проектирования Chrome выходит за рамки данной главы, но следует упомянуть о том, что одной из основных целей разработки проекта была высокая скорость работы. Достижение высокой производительности является всеобъемлющей целью, которая распределяется между всеми направлениями компьютерных наук, поэтому Chrome использует практически все доступные приемы повышения производительности, начиная с кэширования данных и параллельного выполнения операций и заканчивая компиляцией сценариев в процессе исполнения. После этого рассматривалась скорость запуска - сколько времени требуется программе для показа окна на экране после нажатия на иконку - но этот параметр рассматривался как незначительный в сравнении описанными.
Почему следует беспокоиться о скорости запуска приложения? В случае браузера быстрый запуск формирует ощущение легкости, благодаря которому можно предположить, что выполнение какой-либо работы в сети является таким же тривиальным действием, как и открытие текстового файла. Более того, воздействие задержек при работе приложений на ваше настроение и концентрацию внимания достаточно хорошо изучено в ходе исследований процесса взаимодействия человека с компьютером. Задержки особенно актуальны для таких компаний, предлагающих веб-сервисы, как Google или Amazon, у которых есть возможность проводить измерения и эксперименты над эффектом задержек и которые уже провели эксперименты, показывающие, что даже миллисекундные задержки оказывают влияние на частоту посещения пользователями их сайтов или осуществления покупок. Из-за задержек возникает небольшое расстройство, которое постепенно нарастает на подсознательном уровне.
Методика быстрого запуска Chrome основана на замечательном приеме, примененном одним из первых инженеров, работавших над Chrome. Как только в распоряжении разработчиков появился прототип приложения, выполняющий действия вплоть до показа окна на экране, они создали тест производительности, с помощью которого можно было измерить скорость запуска наряду со скоростью сборки приложения для тестирования. После этого, по словам Brett Wilson, "применялось простое правило: этот тест никогда не должен был выполняться медленнее, чем в прошлый раз."1 По мере добавления кода в Chrome, поддержка данной системы тестирования производительности требовала дополнительных усилий от разработчиков2 - в некоторых случаях работа задерживалась до того момента, когда действительно требовалось произвести ее или данные, используемые в ходе запуска приложения, были предварительно обработаны - но основной "прием" для повышения производительности, который произвел на меня благоприятнейшее впечатление, просто заключался в выполнении меньшего объема работы.
Я присоединился к команде разработчиков Chrome без какого-либо намерения работать над инструментами сборки проектов. Мои знания относились к моей рабочей платформе Linux и я хотел быть Linux-разработчиком. Для форсирования разработки проекта, изначально предназначенного для работы исключительно под управлением Windows, я посчитал разумным оказать помощь в завершении реализации приложения для платформы Windows, после чего я мог бы перенести ее на платформу Linux.
В момент начала работы над версиями для других платформ первым препятствием был выбор системы сборки проекта. В тот момент Chrome был уже достаточно большим проектом (завершенная версия Chrome для Windows была выпущена в 2008 году, когда фактически еще не была начата работа над портами на другие платформы), поэтому даже мероприятия, направленные на переход с системы сборки проекта из состава Visual Studio к другой разновидности системы сборки проекта шли в разрез с текущим процессом разработки. Я столкнулся с необходимостью замены инструментария сборки проектов в процессе его использования.
Участники команды разработки Chrome предложили промежуточное решение под названием GYP3, которое могло использоваться для последовательной генерации уже используемых Chrome файлов сборки для Visual Studio в дополнение к файлам сборки, которые могли использоваться на других платформах.
Входные данные GYP были представлены в достаточно простом формате: указывалось желаемое имя выходного файла вместе со списками файлов исходного кода в простом текстовом формате, такое специфическое правило, как "обрабатывать каждый файл IDL для генерации дополнительного файла исходного кода", а также некоторые условия (например, условия использования только определенных файлов на определенных платформах). После формирования данного высокоуровневого описания GYP обрабатывал его и формировал специфичные для платформы файлы сборки проекта.4
На платформе Mac под "специфичными для платформы файлами" подразумеваются файлы проекта Xcode. На платформе Linux, однако, не было очевидного выбора. В первом варианте системы использовалась система сборки проекта Scons, но меня встревожил тот факт, что после генерации файлов сборки средствами GYP, сборка с использованием системы Scons начиналась по истечении 30 секунд из-за поиска системой Scons измененных файлов. Я выяснил, что объем кодовой базы Chrome соответствовал объему кодовой базы ядра Linux, поэтому подход к сборке, применяемый в случае ядра, мог сработать и в нашем случае. После этого я засучил рукава и разработал код, позволяющий GYP генерировать простые файлы Makefile с использованием приемов, применяемых в файлах Makefile ядра.
Таким образом я неумышленно начал свое путешествие в сумасшедший мир систем сборки проектов. Существует множество параметров, которые приводят к затратам времени при сборке программного обеспечения, начиная с медленных линковщиков и заканчивая недостаточным параллелизмом процессов, причем я сталкивался со всеми этими параметрами. Изначально подход с использованием файлов Makefile позволял осуществлять сборку достаточно быстро, но по мере переноса больших объемов кода Chrome на платформу Linux с возрастанием количества файлов, используемых при сборке, она стала выполняться медленнее.5
По мере работы над портом я обнаружил одну очень неприятную особенность процесса сборки: я мог произвести изменение кода в одном файле, выполнить команду make
, понять, что я забыл о точке с запятой, снова выполнить make
и каждый раз ожидание завершения процесса будет достаточным для того, чтобы я забыл о том, над чем в данный момент работаю. После этого я и задумался над тем, как сложно нам далась борьба с задержками, ощущаемыми конечными пользователями. Я подумал: "Как этот процесс может занимать так много времени. Для исправления ситуации не должно потребоваться больших усилий". В качестве эксперимента для установления того, насколько простым способом удастся решить поставленную задачу, я начал работу над проектом Ninja.
Продолжение статьи: Архитектура системы Ninja.