Михаил Рубанов Profile picture
Mobile Head at Dodo Brands. Люблю интерфейсы, тесты и доступность. Написал книгу «Про доступность iOS». Купил сто клавиатур, пишу на одной

Oct 21, 2021, 26 tweets

Как я сборку ускорял

Пару недель назад столкнулся с тем, что проект долго собирается. Ну вы знаете, если программируете на iOS. Начал разбираться.

Тред

Взял тулзу xcode build times, она ставит метки вокруг каждого фреймворка. Прогнал билд, нарисовал диаграмму Ганта.

github.com/PaulTaykalo/xc…

Мы уже пару лет занимаемся распилом и 70% кода в модулях. Но тогда на графе 60% времени уходит на компиляции 30% монолита.

Прогнал xcodebuild с showBuildTimingSummary. Увидел, что какие-то функции компилятся очень долго. Копаем дальше.

Включаю варнинги от фронтенда свифта. Я никогда особо не верил в эти флаги: они обычно показывают, что у меня что-то по 0.5 секунды компилится. Я даже если все места починю выиграю секунд 20.

В этот раз все было намного интересней. Смотрите какой мне красавчик попался. 4.5 минуты пытается понять что я написал.

Вот мой чендж на миллион. Я не знаю, за сколько оно собирается сейчас, точно меньше 100мс. Т.е. больше чем в 2750 раз.

Там было еще несколько похожих случаев, но тип сложно было зарезолвить на уровни кложи. Решилось тоже явным указанием типа. В итоге, граф стал похож на нормальный.

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

Насыпьте лайков, расскажу историю про оптимизации порядка сборки модулей по этому графику зависимостей

Продолжаем про модульность и скорость компиляции.

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

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

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

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

Как знаете, недавно я задался вопросом скорости и начал смотреть.

Предыдущая визуализация меня не устраивала по куче параметров, поэтому взял данные, которые генерировал xcode build times и нарисовал свой график.

На нем несколько интересных мест.

Сначала идут зависимости из кокоаподс.

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

Затем начинается жидкая струйка из разного файрбейса, плюс что-то еще подкомпиливается рядом.

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

Ускорить этот этап можно двумя путями.
- Удалить Firebase и все его модули)))))))))))))))))))))))) Хотя бы для дебага это нам не нужно.
- Пребилдить все внешние зависимости. Можно сэкномить 20% времени

Второе интересное место — одиночная линия, после которой начинается массовая параллельность.

Это модуль с UI, он оказался очень жирным, но при этом нужен для всех модулей. Чтобы ускорить надо разделить на части. Сэкономит еще 10%.

Забегая наперед — половина времени это ассеты

Затем блок из разных фичевых модулей.

Они странно делятся на три части, но это потому, что сбились связи. Например, оказалось, что базовый модуль моделей зависит от UI (!! ну бывает, человек случайно туда ячейку положил). Поправив зависимости мы это все сравняли. -5%.

Часть фичевых модулей все-таки зависит от других.

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

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

В конце висит 30% монолита.

Есть как минимум еще три больших фичи, которые нужно отпилить. Если приходится много работать с монолитом, то можно закешировать все остальные фреймворки, компиляция снова станет быстрой. Наверно, тут можно срезать половину, 15%

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

Раскрасил график:
- темно-серым — внешние зависимости
- красным — блокирующие зависимости, которых ждут другие фреймворки

Мы сейчас в процессе перехода на Tuist. Все зависимости заменили на SPM, у них нет .xcproject, поэтому разметка перестала работать для них и нужно новое решение.

Если прекомпилировать все зависимости, то график будет такой.

Важна не только сборка проекта, но и тестов.

Для тестов надо собрать основной код, вспомогательный код для дублеров и сами тесты. Все это отнимает время и полный график намного красочней. Я покрасил в серый основной код, синий — дублеры, красный — тесты. Гирлянда к НГ готова.

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

К этому моменту оказалось, что я зашел уже далеко. В следующий раз расскажу про эту тулзу.

Если нравится тред, то можно и поретвитить.

Share this Scrolly Tale with your friends.

A Scrolly Tale is a new way to read Twitter threads with a more visually immersive experience.
Discover more beautiful Scrolly Tales like this.

Keep scrolling