Почему куча медленнее стека
Что проверяют
Переводите ли вы общую теорию на язык Swift. Здесь ждут не рассказ про две быстрые операции со стеком, а понимание того, что у объекта в куче есть заголовок и счётчик ссылок, и что основная цена в приложении это трафик ARC и промахи кэша, а не вызов аллокатора.
Как отвечать
Структура или перечисление без ссылочных полей и с известным размером живут по месту: в кадре стека, в регистрах или внутри объемлющего объекта. Освобождение при выходе из функции бесплатно. Экземпляр класса всегда выделяется в куче, несёт заголовок со счётчиком ссылок и указателем на метаданные, а живёт столько, сколько его держат ссылки.
Из этого складываются три статьи расхода:
- вызов аллокатора и лишняя память под заголовок;
- ARC:
retainиreleaseэто атомарные операции, они стоят даже без конкуренции, и оптимизатор убирает их не всегда, а только когда видит код целиком; - локальность: массив классов это массив указателей, обход прыгает по памяти, тогда как массив структур лежит подряд.
Обязательно добавьте, что структуры не бесплатны. Массивы, строки и словари это значимые типы с буфером в куче: копирование при записи выделяет новый буфер, когда ссылок больше одной. Структура в убегающем замыкании или в свойстве класса тоже оказывается в куче, а упаковка большой структуры в протокольный тип добавляет отдельное выделение.
Замерять это стоит в Instruments: шаблон Allocations покажет число выделений, профилировщик времени покажет swift_retain и swift_release.
Где спотыкаются
Заявляют, что структуры всегда на стеке, а классы всегда в куче. Это правило большого пальца, а не закон: захват убегающим замыканием, хранение в свойстве и протокольный тип уводят структуру в кучу.
Второе: описывают кучу в терминах ручного выделения и освобождения и забывают про ARC. В Swift освобождение автоматическое, утечка выглядит как цикл сильных ссылок и лечится weak или unowned, а цена на горячем пути это атомарные счётчики.
Третье: переписывают классы в структуры ради скорости, ничего не измерив. Большая структура копируется при каждой передаче, а копирование при записи разворачивает весь буфер целиком, и профиль после такой правки нередко становится хуже.