Почему полная сборка лучше показывает предел железа, чем инкрементальная
В повседневной работе инкрементальная сборка Xcode скрывает большую часть разницы в железе:
изменили файл — пересобрались несколько target'ов, и MacBook Air на 8 ГБ с Pro на 16 ГБ кажутся «достаточными».
Но после merge в main CI запускает Clean Build, или локально выполняется
xcodebuild clean build для проверки Release-конфигурации —
компилятор поднимает все Swift-модули, делает полную проверку типов и линкует каждое расширение.
Это настоящий стресс-тест для памяти, охлаждения и длительной нагрузки на CPU.
Частая жалоба indie-разработчиков: «на полпути сборки вентилятор ревёт, симулятор зависает, Slack еле отвечает». Причина редко в версии Xcode — полная сборка держит машину под высокой нагрузкой 5–10 минут. Охлаждение и unified memory рассчитаны на мобильность, а не на непрерывную компиляцию. Эта статья сравнивает «один проект, одна команда, разные машины», чтобы решить: апгрейдить локальный Mac или перенести тяжёлые сборки на выделенный облачный узел?
Проект: среднее SwiftUI-приложение (~118 000 строк Swift/ObjC, Widget + Share Extension + Notification Service Extension, 3 extension target), CocoaPods + 14 локальных зависимостей Swift Package.
Toolchain: macOS 15 Sequoia, Xcode 16.4, -jobs 10 (по числу физических ядер M4).
Эталон A: MacBook Air M2 · 8 ГБ · 256 ГБ · без вентилятора (2022).
Эталон B: MacBook Pro 14" M1 Pro · 16 ГБ · 512 ГБ (2021, от сети).
Облако: PixVPS Mac mini M4 · 10 ядер · 16 ГБ · 256 ГБ NVMe · выделенный канал 1 Гбит/с (узел Япония Токио).
Каждый тест 3 раза, берём медиану; перед каждым прогоном rm -rf ~/Library/Developer/Xcode/DerivedData/* для cold start.
Единая методика: без «каждый собирает по-своему»
Типичная ошибка при сравнении времени сборки — разное состояние машин: Chrome с 40 вкладками, свежий reboot или DerivedData прошлой недели. Мы зафиксировали базовую линию:
-
01
Один и тот же Git commit
На всех трёх машинах
git checkout v2.4.0-buildbench, зафиксированPackage.resolved, чтобы SPM не влиял на замер. -
02
Сборка из CLI, GUI закрыт
Без Product → Build в Xcode: только
xcodebuildи/usr/bin/time -lдля wall-clock и пика памяти. -
03
Реалистичная фоновая нагрузка
Локальные MacBook: Slack + Chrome (8 вкладок), симулятор выключен. Облачный M4: только SSH и скрипт сборки — имитация unattended CI.
-
04
Два типа сборки
Debug
clean build(разработка) и Releasearchive(релиз), отдельно время и страницы swap.
Пример Debug:
xcodebuild -workspace MyApp.xcworkspace -scheme MyApp -configuration Debug clean build -destination 'generic/platform=iOS' -jobs 10.
Отсчёт от Enter до ** BUILD SUCCEEDED **, без pod install и импорта сертификатов.
Clean Build: wall-clock M4 против двух поколений MacBook
Медиана полной Debug-сборки ниже. Облачный M4 примерно на 15 % быстрее локального M1 Pro и более чем вдвое быстрее M2 Air — главным образом потому, что Air к третьей минуте уходит в сжатие памяти и swap, а CPU из-за перегрева падает с ~3,4 ГГц до ~2,6 ГГц.
| Машина | Debug Clean Build | Release Archive | Пик памяти | Swap |
|---|---|---|---|---|
| Mac mini M4 (PixVPS выделенный) | 3m 41s | 3m 52s | 12,4 ГБ | Нет |
| MacBook Pro M1 Pro 16 ГБ | 4m 18s | 4m 56s | 14,8 ГБ | Незначительный (< 200 МБ) |
| MacBook Air M2 8 ГБ | 7m 09s | 8m 47s | 7,9 ГБ + 3,2 ГБ swap | Да |
На Release Archive разрыв ещё больше: выше оптимизация, тяжелее линковка. M2 Air достигает 4,1 ГБ swap и почти 9 минут wall-clock; на M4 всё остаётся в пределах 16 ГБ физической памяти — без резкого замедления в конце. Если вы делаете полный Archive хотя бы раз в неделю, разница превращается в ожидание и недоступность ноутбука для других задач.
Инкрементальная сборка: почему ощущения и выводы о полной сборке расходятся
Дополнительно замерили «изменили один Swift-файл, инкрементальная сборка» — типичный Cmd+B. Разрыв сжимается: M4 облако 17 с, M1 Pro 22 с, M2 Air 38 с (Chrome в фоне). Пересобираются только затронутые модули; пик RAM ниже 5 ГБ, swap на Air не возникает.
Вывод: если 90 % времени — мелкие итерации, локального Air 8 ГБ часто хватает. Боль возникает в релизную неделю, при крупном обновлении зависимостей или первой сборке после смены ветки. Многие команды делят «код локально, полная сборка в CI» — но hosted Runner GitHub в пике даёт 20+ минут очереди, что хуже локального Air. Выделенный облачный M4 для полных сборок — третий путь между «терпеть на ноутбуке» и «публичная очередь».
С сохранённым DerivedData вторая полная Debug-сборка: M4 2m 14s, M1 Pro 2m 48s. Облачный узел удобен для долгого кэша; Air с 20 ГБ свободного диска чаще чистит кэш — и чаще попадает на cold start полной сборки. Нужно место: SSD +1 ТБ ($2,9/день) для нескольких версий Xcode и кэшей в облаке.
Нагрев, вентилятор и параллельная работа во время сборки
Сборка — не только секундомер, но и workflow. Во время полной сборки фиксировали температуру и RPM (внешний датчик на M4 mini, встроенные — на ноутбуках):
| Машина | Темп. на 3-й мин (клавиатура/корпус) | Вентилятор / шум | Симулятор параллельно |
|---|---|---|---|
| Mac mini M4 (облако) | корпус ~38 °C | почти неслышно, < 25 дБ | возможно (наблюдение по SSH) |
| MacBook Pro M1 Pro | клавиатура ~44 °C | ~3200 RPM, слышно | возможно, UI дёргается |
| MacBook Air M2 8 ГБ | клавиатура ~47 °C | пассив, горячий корпус, throttling | не рекомендуется, симулятор нестабилен |
Пассивный M2 Air под длительной нагрузкой снижает частоту — первые 2 минуты близко к M1 Pro, затем растягивание до 7 минут суммарно. Облачный Mac mini с настольным охлаждением держит 10 ядер M4 дольше на высокой частоте. На практике: полную сборку в фоне по SSH, на Windows/Linux продолжать код, созвоны, документы — без борьбы за ресурсы ноутбука.
16 ГБ unified memory: граница «хватает» и «впритык»
Пик полной Debug-сборки здесь ~12,4 ГБ — комфортная зона для 16 ГБ. При росте проекта за 200 000 строк или при Xcode + симулятор + Instruments одновременно и 16 ГБ локально на пределе: на том же M1 Pro с UI-тестами на симуляторе iOS 17 — пик 15,6 ГБ, 800 МБ swap, сборка с 4m 18s до 5m 44s.
M2 Air 8 ГБ упирается в потолок примерно через 90 секунд — сжатие и подкачка.
В логах memory_pressure много событий vm_compressor;
потоки компиляции ждут освобождения памяти, wall-clock почти удваивается.
Это не «плохая оптимизация Xcode», а физический лимит RAM —
16 ГБ на облачном M4 для многих средних iOS-проектов — минимум без swap, а не избыточная конфигурация.
У части провайдеров macOS виртуализирован или хост перепродан: время полной сборки плавает >30 % от нагрузки соседей. PixVPS: выделенный физический Mac mini M4, без виртуализации и overselling — стандартное отклонение за три прогона меньше 4 секунд, стабильная база для сравнения.
Воспроизвести бенчмарк: скрипт и подводные камни
Минимальный скрипт для локальной и облачной машины, результаты в CSV для сравнения.
В корне проекта сохранить как scripts/bench_clean_build.sh:
#!/bin/bash
set -euo pipefail
SCHEME="MyApp"
WORKSPACE="MyApp.xcworkspace"
rm -rf ~/Library/Developer/Xcode/DerivedData/*
/usr/bin/time -l xcodebuild -workspace "$WORKSPACE" -scheme "$SCHEME" -configuration Debug clean build -destination 'generic/platform=iOS' -jobs "$(sysctl -n hw.ncpu)" 2>&1 | tee /tmp/xcode_bench.log
grep -E 'real|maximum resident set size' /tmp/xcode_bench.log
Перед первым замером на новой машине: xcodebuild -runFirstLaunch;
проверить xcode-select -p;
не открывать GUI Xcode во время замера (SourceKit/индексация);
в облаке — SSH и caffeinate -dims против сна (то же на локальном ноутбуке).
VNC только для наблюдения — не запускать GUI Xcode в VNC параллельно CLI-сборке (+1–2 ГБ RAM).
Когда переносить полную сборку на облачный M4
Апгрейд до M4 Pro 32 ГБ стоит тысячи долларов, а релизные пики — десятки дней в год. Если боль — «раз-два в неделю Archive делает ноутбук раскалённым на коленях», а не «30 секунд на каждое сохранение», посуточная аренда выделенного M4 часто выгоднее: $21,1/день, $57,1/неделя, $105,7/месяц, без контракта, готовность 1–5 минут, SSH и VNC в браузере.
Пять узлов PixVPS — Сингапур, Япония (Токио), Корея (Сеул), Гонконг, Восток США — одинаковая цена железа.
Для команд из СНГ и Европы часто удобны Сингапур или Гонконг; для работы с американскими API — восток США.
По SSH wall-clock сборки сопоставим с локальным — нагрев и шум остаются в дата-центре.
Интеграция с self-hosted GitHub Actions или Fastlane: push локально, xcodebuild archive в облаке,
.ipa обратно или сразу в TestFlight (см. практическое руководство Xcode CI/CD на облачном Mac).
Другой сценарий: основной ПК — Windows, iOS-фриланс изредка — MacBook покупать не обязательно; на релизную неделю арендовать облачный M4 на пару дней, поставить Xcode и сертификаты, затем освободить. 16 ГБ unified memory, выделенный 1 Гбит/с, SLA 99,9 %, поддержка людьми 7×24, тикеты ~1 час. Большие кэши: SSD +1 ТБ ($2,9/день). Ночные матрицы нескольких приложений: Thunderbolt 5 parallel ($1,9/день, 80 Gbps) — см. тест кластера TB5.
| Ваша ситуация | Рекомендация | Роль облачного M4 |
|---|---|---|
| M2 Air 8 ГБ, <4 полных сборок/мес | аренда посуточно в день релиза | временная compile-станция, без swap |
| M1/M2 Pro 16 ГБ, будни ОК, релизная неделя медленная | Archive в облако на релизной неделе | код локально, тяжёлая сборка в облаке |
| Нет Mac, iOS-фриланс | аренда на неделю + отладка по VNC | полный Xcode, освобождение после |
| Несколько полных сборок/день в CI | постоянный облачный M4 Runner | $105,7/мес, без очереди |
Полная сборка измеряет не «у кого выше benchmark», а кто стабильно укладывается в ~5 минут, не жертвуя параллельной работой. MacBook остаётся лучшим терминалом для ежедневного кодинга; выделенный облачный M4 снимает пики компиляции — посуточно, без покупки железа под десятки дней в год.
Перенесите полные сборки Xcode на тихий узел M4
PixVPS Mac mini M4 выделенный: 10 ядер CPU, 16 ГБ unified memory, полная Debug-сборка ~3m 40s, без swap. SSH / VNC, от $21,1/день.