Более Быстрые Сборки Apple с помощью lld Linker

10 января 2023


20 декабря 2022 г.

TL; DR: lld — отличный выбор для более быстрой компоновки отладочных двоичных файлов iOS, macOS и т. д. Он завершает работу на 50-80% быстрее, чем ld64, и сейчас используется многими крупными компаниями. Шаги по интеграции описаны в разделе ниже.

Linker - Компоновщик (также редактор связей, от англ. link editor) — инструментальная программа, которая производит компоновку («линковку»): принимает на вход один или несколько объектных модулей и собирает из них исполняемый или библиотечный файл-модуль.

 

Вступление

«Линковка» - является одним из основных критических параметров для инкрементных сборок. Тысячи и тысячи часов разработчиков ежегодно тратятся на ожидание компоновки отладочных сборок, поэтому оптимизация линкера является важной темой. Линкеры — это сложные звери, которым приходится молниеносно выполнять сложные преобразования огромных объемов данных, а для этого требуется много работы. В этом посте мы обсудим прошлое, настоящее и будущее оптимизации линкера для платформ Apple. Он также включает практический раздел о том, как сегодня интегрировать lld. Если вы не знакомы с компоновкой, прочитайте об этом здесь и найдите момент компоновки в конце ваших логов сборки.

 

История Оптимизации Linker для Платформ Apple

До 2020 г.

В течение многих лет широко использовался только один линкер — стандартный линкер Apple, известный как ld64. К сожалению, этому не уделялось должного внимания в вопросе скорости. Например, когда в 2017 году я упомянул инженеру Apple, что для линковки основного приложения требуется 20 секунд, они, похоже, очень удивились. Хотя линкер активно развивался, например, для поддержки Swift, скорость не была главной задачей. Apple, похоже, не считала это таким уж “бутылочным горлышком” для сторонних разработчиков.

Релиз zld

Форк (fork с англ. — «развилка, вилка») или ответвление — использование кодовой базы программного проекта в качестве старта для другого, при этом основной проект может как продолжать существование, так и прекратить его.

Весной 2020 года я выпустил собственный форк ld64 под названием zld. Он добавил ряд оптимизаций поверх ld64, таких как использование более быстрых реализаций хэш-карт в ключевых местах. На самом деле в нем было много более чем доступных ( *быстро исполняемых) решений, и в итоге он был примерно на 40% быстрее, чем ld64. Поскольку серьезного финансирования на это не было, я мог работать над ним только в свободное время, но тем не менее это дало результаты. Его выпуск ознаменовал начало новой эры сосредоточения внимания на скорости компоновщика.

Модернизация lld

Вскоре после выпуска zld больше внимания стало уделяться другому компоновщику, lld. lld — это компоновщик в рамках проекта LLVM, который существует уже много лет и поддерживает платформы Apple и Linux. Для Linux он уже долгое время был хорошим выбором для ускорения линковки, но до весны 2020 года поддержка платформ Apple была недостаточной. Он не поддерживал многие из новых дополнений к ld64 и обычно выдавал ошибку, когда использовался для сборки новых проектов Swift или Objective-C. Казалось, что он в основном используется Google и предназначен для связывания Chromium. Однако всего через несколько дней после выпуска zld и, возможно, под его влиянием, ситуация изменилась. Facebook объявил в списках рассылки LLVM, что они будут выделять на это больше ресурсов и привлекать инженеров. Цель состояла в том, чтобы сделать lld жизнеспособной заменой ld64, такой как zld, с потенциалом стать намного быстрее. Предстоял долгий путь, но у него была многообещающая основа.

В отличие от zld, который был ответвлением ld64, lld был написан с нуля, ставя в приоритет скорость. Например, если в ld64 каждый символ двоичного файла итерировался много раз, - один раз для каждого «прохода», - lld пытался объединить несколько проходов в каждой итерации. При итерации миллионов символов в большом двоичном файле это может стать существенным выигрышем. lld также может полагаться на многочисленные оптимизированные структуры данных проекта LLVM. В течение следующих месяцев инженеры из Facebook и Google, а также других компаний, улучшили его, пока он не смог эффективно линковать некоторые из самых больших приложений. Примерно в конце 2021 — начале 2022 года lld в целом стал работоспособным компоновщиком для приложений со значительно более высокой скоростью, чем ld64 и zld. Теперь он линкер по умолчанию для отладочных сборок не только для Google и Facebook, но и для ряда других крупных компаний.

Улучшения в ld64 от Apple

Возможно, в связи с тем, что в сообщество стало уделять все внимание скорости компоновщика, Apple начала оптимизировать ld64 в 2021 и 2022 годах. Команда, владеющая ld64, ранее была занята такими вещами, как сокращение времени, затрачиваемого Apple на запуск приложения, но с выпуском Xcode 14 в компоновщик был добавлен ряд оптимизаций. Эти оптимизации обсуждаются в этом видео WWDC. Теперь даже те разработчики, которые не решили использовать другой линкер, могут рассчитывать на более быструю компоновку.

 

Текущее состояние zld

zld находится в режиме обслуживания! Поскольку lld стал превосходной альтернативой, а в Xcode 14 появилось хитрое изменение, поддержку которого я не хотел добавлять, я решил, что пришло время выкинуть полотенце. zld всегда был в значительной степени нефинансируемым проектом, над которым я работал в свободное время, и после двух лет ускорения сборки для разработчиков я увидел, что в 2022 году проекты с лучшим финансированием догнали и превзошли его.

 

Интеграция IId

Указания в документации LLVM объясняют, как правильно его интегрировать. Обратите внимание, что в текущем официальном релизе LLVM не хватает пары ключевых исправлений, особенно для тех, кто занимается сборкой iOS и/или сборкой с помощью Xcode 14. Чтобы получить версию с этими исправлениями, загрузите последнюю версию отсюда.

 

Следует ли использовать lld для релизных сборок?

На мой взгляд, ответ — нет, по крайней мере, не сейчас. По двум причинам:

  • lld не хватает некоторых функций, которые есть у ld64. Например, цепочка исправлений ld64, функция, которая может сократить время запуска, еще недостаточно протестирована для lld. Другими словами, при переключении на lld время запуска может ухудшиться.
  • ld64 выпускается синхронно для каждой версии Xcode с другими частями цепочки инструментов, такими как clang и libLLVM. Одна копия lld может полагаться на другую версию LLVM, что теоретически может вызвать проблемы. Например, могут возникнуть проблемы, когда код LTO, созданный clang, передается несоответствующей версии lld.

В общем, lld не хватает тестирования и функций, чтобы рекомендовать его в масштабе. Лично я не уверен, что это плохо. Релизные сборки не нуждаются в такой экстремальной скорости компоновки, как отладочные сборки, и к ним предъявляются более высокие требования по стабильности и стандартизации для всех приложений. Однако Google использует его для продакшн-сборок, и теоретически lld может получить новые оптимизации, которых нет в ld64. Он уже может создавать двоичные файлы меньшего размера благодаря Identical Code Folding, так что посмотрим, что произойдет.

 

Заключение

Всегда будет над чем работать, будь то дальнейшая оптимизация этих компоновщиков или поддержка новых функций, таких как цепочек исправлений. Ландшафт также может измениться. Пока неясно, станет ли перспективный платный линкер sold, который в настоящее время быстрее, чем lld, готовым к продакшину больших приложений. Apple также может попытаться сократить разрыв между ld64 и этими более быстрыми компоновщиками. Но независимо от того, что произойдет в будущем, ясно, что скорость “линковки” сейчас получает то внимание, которое она заслуживает.

Содержание