Вы когда-нибудь задумывались, какой из них быстрее?
Как разработчики, мы хотим, чтобы наш код выполнялся как можно быстрее, мы проектируем его так, чтобы он был безопасным, легко изменяемым, читаемым, эстетичным и т. Д. В этой статье показана разница в скорости между передачей типов значений и ссылочных типов.
Типы в 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.0879960060119630
- 1.0761229991912842
- 1.0753719806671143
- 1.0964360237121582
- 1.0846699476242065
СРЕДНЕЕ: 1.08411939
Передано по значению (структура User):
- 1.0615510940551758
- 1.0725200176239014
- 1.0685210227966309
- 1.0658539533615112
- 1.0635479688644410
СРЕДНЕЕ: 1.06639881
Передача по значению в этом практическом примере была на 0,01811 быстрее, чем по ссылке. На мой взгляд, это очень незначительная разница.
Прохождение большого объекта
Как упоминалось в начале статьи, скорость передачи структуры зависит от ее размера. В следующем измерении все то же самое, кроме размера User. Пользовательский объект теперь имеет 32 следующие переменные:
Результаты выполнения того же тестирования с переданным более крупным объектом:
Передано по ссылке (класс User):
- 1.0702630281448364
- 1.075631022453308
- 1.0900360345840454
- 1.0741839408874512
- 1.092007040977478
СРЕДНЕЕ: 1.0804242134094237
Передано по значению (структура User):
- 1.6252110004425049
- 1.597877025604248
- 1.6124770641326904
- 1.6013799905776978
- 1.6155270338058472
СРЕДНЕЕ: 1.6104944229125977
Для больших структур этот тест показал, что передача по значению примерно на 65% медленнее, чем передача того же объекта по ссылке. Передача по ссылке дает почти те же результаты, что и в первом примере.
Причина в том, что указатель остался прежним. Однако выделенная память в куче изменилась.
Точка разрыва
В теории точка разрыва должна быть там, где размер типа значения больше размера указателя. Типы значений, размер которых меньше размера указателя, должны передаваться быстрее.
В первом примере, где результаты были почти одинаковыми, размер структуры User составлял 96 бит (12 байтов). Размер указателя на 64-битной архитектуре, на которой проводился тест, составляет 64 бита (8 байтов). При использовании ссылочного типа ЦП нужно было предпринять некоторые дополнительные шаги, чтобы разыменовать указатель и получить значение в регистры. Это могло быть разницей в том, почему, хотя структура была на 4 байта больше, чем размер указателя, структура все равно была немного быстрее.
Во втором тесте, где структура значительно медленнее, размер структуры составляет 768 бит или 96 байт. Пока размер указателя остается 8 байт.
В заключение
В этой статье показано, что скорость передачи ссылочных типов и типов значений существенно различается в зависимости от размера типа значения. Запомни. :)
Спасибо за прочтение.
Если вам нравится то, что вы читаете, поделитесь и похлопайте по пятидесяти. :)
Если вам понравилась эта статья и вы хотите узнать больше, то, пожалуйста, ознакомьтесь с моей книгой Модульная архитектура на iOS и macOS - Создание больших масштабируемых приложений и фреймворков iOS и macOS с помощью Domain Driven Design, где я храню последние практики разработки, которые я изучил вместе путь. Или просто свяжитесь с LinkedIn.