build2 вплотную приблизился к Ninja по скорости сборки, а после некоторых настроек даже обогнал его на 2,2%. Борис Колпаков прогнал оба инструмента на полной сборке Xerces-C++ — это 299 C++ файлов, собираемых в общую библиотеку. Ninja (точнее, его генерация через CMake) считается эталоном скорости. Но у этого эталона есть нюанс: CMake тратит 15,6 секунды на генерацию файлов сборки, и только потом Ninja укладывается в 3,4 секунды. То есть полное время — 19 секунд.
build2 — нативная система сборки, ей не нужен шаг генерации. Сначала она показала 3,8 секунды — на 11% медленнее Ninja. Потом автор отключил часть функций, которых нет у Ninja. Первым делом — умное отслеживание изменений: build2 токенизирует исходники и считает контрольную сумму, чтобы не перекомпилировать при изменении только пробелов. В режиме read-only эта проверка отключается, и время упало до 3,4 секунды. Затем он отключил сжатие в file cache — кэше частично препроцессированных файлов. Получилось 3,355 секунды — на 2,2% быстрее Ninja.
При этом build2 выполняет больше работы. Он генерирует заголовок XercesVersion.hpp прямо во время сборки, тогда как у Ninja это делает CMake. Он извлекает информацию о компиляторе: идентификатор, версию, пути поиска заголовков — для этого приходится запускать GCC 10 раз (5 для C, 5 для C++). Он отслеживает изменения не только в заголовках, но и в самом компиляторе, путях и переменных окружения.
Почему build2 справляется? Три решения. Во-первых, он не делает лишней работы, как CMake: в том же примере из статьи CMake 85 раз проверяет путь к Xcode и версию ОС. build2 агрессивно кэширует все обнаруженные данные, но не между запусками. Во-вторых, build2 многопоточный: Ninja запускает компиляторы параллельно, но свою внутреннюю работу (разбор Makefile-фрагментов с зависимостями) выполняет в один поток. build2 делает это в несколько потоков. В-третьих, у build2 другая модель извлечения зависимостей: вместо того чтобы получать их как побочный продукт компиляции, build2 заранее делает частичный препроцессинг (-fdirective-only у GCC) и получает зависимости как побочный продукт препроцессинга, а потом компилирует уже препроцессированный файл. Это нагружает память меньше и лучше использует файловый кэш системы.
Бенчмарк проводился на Intel i9-12900K, Debian, Samsung 980 Pro NVMe (ext4), с отключенным turbo boost. Использовались Xerces-C++ 3.3.0 и пакет libxerces-c-3.3.0+3 для build2.