Тюнинг Swift компилятора. Часть 1

Тюнинг Swift компилятора. Часть 1

Обзор Swift 3 компилятора и способы его ускорить. Часть 1. Развенчание существующих мифов. Мнение о проблемах autocompletion в Xcode.

Предисловие:

Наша компания занимается разработкой мобильных приложений под ключ. Многие наши iOS разработчики говорят на Objective-C лучше, чем на русском, их девушка Cocoa, а спят они в обнимку с айфоном… и вот стали мы вдруг писать на Swift.

Я не буду говорить про различные косяки синтаксиса, веселые "Segmentation Fault: 11", периодически гаснущую подсветку, это все и так известно. Пусть больно, но терпимо. Но есть кое-что по-настоящему убивающее бизнес, а не просто доставляющее дискомфорт. Медлительный компилятор. Да-да, это не просто громкий заголовок. Когда одинаковые по объему Obj-C и Swift проекты собираются с четырехкратной разницей во времени. Когда при добавлении одного метода стартует пересборка половины всего кода. Когда ошибки компилятора вообще выводят его из строя — это настоящее убийство времени разработчика. А как известно: время — это деньги.

Есть два варианта: продолжить ныть и терпеть, либо решать вопрос. Мы выбрали второе.

Изобретение велосипеда

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

Так зачем же еще одну плодить? А затем, что, во-первых, все это было еще до третьего свифта, во-вторых, некоторые утверждения в статье не совсем верны, а так же список коварных мест было бы неплохо дополнить. Чем мы и займемся.

Материала тут не на одну статью, так что буду выкладывать постепенно. Да и кроме самой скорости компиляции есть еще фактор производительности в runtime, который тоже надо бы осветить.

Начнем с того, что уже было известно, но просто проверим на актуальность в Swift 3.

Nil Coalescing Operator

Моя любимая фишка Swift, сахарный optional. Чем-то похож на nil-safe сообщения в Obj-C.

Возьмем пример из прошлых статей. Сейчас вы поймете, почему они не совсем корректны:

Время компиляции: 12 секунд! Приятель, у тебя третий пень что ли? Даже хуже, чем было в Swift 2.2.

Хочется сказать: "Воу, Apple, что за?", но не спешите с выводами. Давайте немного оптимизируем этот код, разбив длинное выражение на несколько маленьких:

Время компиляции: 30 ms. (миллисекунд)

Получается, дело вовсе не в злых optional? Но нет, это было бы слишком просто. Давайте усложним задачу:

Время компиляции: 35 ms.

Вывод: У Nil Coalescing Operator все стерильно, можно пользоваться. Но тогда в чем же была проблема?

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

Правило следующее: у компилятора вызывают запор выражения с несколькими сложными слагаемыми. То есть теми, которые не просто являются переменными, но и выполняют какие-либо действия. А вот складывать переменные можно сколько угодно.

Вы, наверное, скажете: "Где пруфы, Билли?"

Хорошо. Тогда возьмем предыдущий код, но не будем дробить его на под-операции:

Результата долго ждать не пришлось (пришлось):

Цитирую, если не получилось прочитать со скрина: "Expression was too complex to be solved in reasonable time; consider breaking up the expression into distinct sub-expressions".

Перевод: "Выражение было слишком сложным, чтобы решить за приемлемое время. Разбейте формулу на отдельные под-выражения."

Дальнейшее является наблюдением без теоретической базы.

Многие замечали, что в Xcode регулярно отваливается auto-completion. Это, как правило, происходит в момент фоновой компиляции. Если вы написали что-то вроде выражения, которое вызывает "Expression was too complex", то сразу за этим умрут и подсказки.

Это можно легко проверить. Возьмем тот же метод и начнем писать self.view, чтобы получить подсказку:

А потом добавим наше выражение-убийцу. Все, подсказок вы больше не получите, даже если усиленно лупить по ctrl+space:

Лечится это запуском явной компиляции и устранением ракового кода.

Тернарный оператор

В статье так же освещаются проблемы тернарного оператора. Время компиляции кода можно увидеть в комментариях:

Кстати, у меня такого метода как type0ToString в SDK не нашлось. Я его заменил на упрощенный вариант, разницы никакой:

Время компиляции: 260 ms. Пока все подтверждается.

Но мне кажется, что тернарный оператор несправедливо обвинен. Попробуем снова разбить формулу на отдельные выражения, но без использования if-else:

Время компиляции: 45 ms

Но это не предел. Упростим еще больше:

Время компиляции: 7 ms.

Вердикт: тернарный оператор оправдан.

Еще несколько амнистий

Время компиляции: 6ms

Время компиляции: 19ms

И самое сладкое:

Время компиляции: 86 ms. Могло быть и лучше, но уже хотя бы не 12 часов.

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

В дальнейшем еще пройдемся по аспектам языка, в том числе проверим на быстродействие языковые структуры switch-case, if-else, guard и так далее.

Буду рад обратной связи. Пишите в комментариях, что разхабрать в первую очередь.

📎📎📎📎📎📎📎📎📎📎