Вы когда-нибудь задумывались, какой из них быстрее?

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

Типы в Swift

Что касается типов в Swift, то их два:

Тип ссылки
Класс, в котором экземпляр использует одну копию в памяти, на которую ссылается указатель на память. Когда ссылочный тип передается в качестве аргумента в функцию или закрытие, передается фактический указатель на объект в памяти.

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

Тип значения
Любая структура, перечисление, примитивы (int, bool,…), замыкание, кортежи и, возможно, некоторые другие являются типами значений. Типы значений копируются при передаче в другую функцию в качестве аргумента.

Изменение значения переменной в вызываемой функции не повлияет на ссылку вызывающей стороны.

Что происходит под капотом?

Когда тип значения передается в другую функцию, его значение копируется в стек. Из стека значения могут быть легко доступны для ЦП. Нет необходимости говорить, что копирование значения в стек имеет обратную сторону.

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

Нет необходимости упоминать, что будет некоторая точка перелома, когда типы значений будут быстрее, чем ссылочные типы, и наоборот. Продолжайте читать, чтобы узнать, где находится предел. :)

Бенчмаркинг

Для замера выбрал полюбившиеся им типы struct и class. Измерения проводились с помощью оптимизации компилятора (производственная сборка AppStore) и выполнялись на iPhone 5s под управлением iOS 12.4.4.

Объекты тестирования показаны ниже. Простая модель anAddress и aUser.

Измеряемая функция просто обращается ко всем значениям этих объектов и возвращает сумму всех значений, как показано ниже.

Наконец, функция эталонного тестирования, которая создает адрес, пользователя и в закрытии measure, передает их 1_000_000 раз для создания идентификатора пользователя.

Полученные результаты

Каждый тест (1_000_000 операций) измерялся 5 раз.

Передано по ссылке (класс User):

  1. 1.0879960060119630
  2. 1.0761229991912842
  3. 1.0753719806671143
  4. 1.0964360237121582
  5. 1.0846699476242065

СРЕДНЕЕ: 1.08411939

Передано по значению (структура User):

  1. 1.0615510940551758
  2. 1.0725200176239014
  3. 1.0685210227966309
  4. 1.0658539533615112
  5. 1.0635479688644410

СРЕДНЕЕ: 1.06639881

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

Прохождение большого объекта

Как упоминалось в начале статьи, скорость передачи структуры зависит от ее размера. В следующем измерении все то же самое, кроме размера User. Пользовательский объект теперь имеет 32 следующие переменные:

Результаты выполнения того же тестирования с переданным более крупным объектом:

Передано по ссылке (класс User):

  1. 1.0702630281448364
  2. 1.075631022453308
  3. 1.0900360345840454
  4. 1.0741839408874512
  5. 1.092007040977478

СРЕДНЕЕ: 1.0804242134094237

Передано по значению (структура User):

  1. 1.6252110004425049
  2. 1.597877025604248
  3. 1.6124770641326904
  4. 1.6013799905776978
  5. 1.6155270338058472

СРЕДНЕЕ: 1.6104944229125977

Для больших структур этот тест показал, что передача по значению примерно на 65% медленнее, чем передача того же объекта по ссылке. Передача по ссылке дает почти те же результаты, что и в первом примере.

Причина в том, что указатель остался прежним. Однако выделенная память в куче изменилась.

Точка разрыва

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

В первом примере, где результаты были почти одинаковыми, размер структуры User составлял 96 бит (12 байтов). Размер указателя на 64-битной архитектуре, на которой проводился тест, составляет 64 бита (8 байтов). При использовании ссылочного типа ЦП нужно было предпринять некоторые дополнительные шаги, чтобы разыменовать указатель и получить значение в регистры. Это могло быть разницей в том, почему, хотя структура была на 4 байта больше, чем размер указателя, структура все равно была немного быстрее.

Во втором тесте, где структура значительно медленнее, размер структуры составляет 768 бит или 96 байт. Пока размер указателя остается 8 байт.

В заключение

В этой статье показано, что скорость передачи ссылочных типов и типов значений существенно различается в зависимости от размера типа значения. Запомни. :)

Спасибо за прочтение.

Если вам нравится то, что вы читаете, поделитесь и похлопайте по пятидесяти. :)

Если вам понравилась эта статья и вы хотите узнать больше, то, пожалуйста, ознакомьтесь с моей книгой Модульная архитектура на iOS и macOS - Создание больших масштабируемых приложений и фреймворков iOS и macOS с помощью Domain Driven Design, где я храню последние практики разработки, которые я изучил вместе путь. Или просто свяжитесь с LinkedIn.