Простой трюк, чтобы перезарядить ваши петли с помощью «развертывания петель».

Современные устройства невероятно мощные, что часто приводит к тому, что мы упускаем из виду важность эффективности и оптимизации. Легко подумать, зачем беспокоиться об эффективности, когда у нас есть высокопроизводительное оборудование, такое как монстр M2 SoC в наших Mac или iPad? Однако принятие такого мышления не является здоровым. Иногда важно улучшить основы и рассмотреть советы по оптимизации кода. Они могут обогатить наши знания и улучшить наши навыки как разработчиков, даже если они не всегда могут быть практичными.

Теперь давайте рассмотрим функцию, которую мы используем ежедневно: метод фильтра.

Пишем собственный метод фильтра

Массивы фильтров — обычная задача, поэтому интересно попробовать ее оптимизировать.

Давайте возьмем массив имен и попробуем отфильтровать конкретное имя:

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

Я выполнил тест на 500 000 элементов, и время выполнения составило 0,056секунды. Неплохо! Теперь давайте рассмотрим некоторые потенциальные оптимизации для дальнейшего повышения его производительности.

Использование forEach

Некоторые из вас могли заметить, что предыдущая функция казалась немного громоздкой. В конце концов, зачем возиться с созданием нижних и верхних границ, когда мы можем просто использовать метод forEach для массива? Давайте посмотрим на пример того же кода, но реализованного с помощью forEach:

Теперь, когда я выполняю код для тех же 500 000 элементов, результаты получаются значительно быстрее, а время выполнения составляет 0,031 секунды. Это замечательное улучшение на 42%!

Но почему это происходит? Что ж, под поверхностью сама функция forEach все еще выполняет итерацию. Однако стоит отметить, что forEach — это встроенный метод, который может быть оптимизирован базовой средой выполнения или компилятором, что может привести к повышению производительности по сравнению с реализованными вручную циклами.

Итерация в Swift часто более оптимизирована, чем доступ к элементам по отдельности. Последовательная итерация позволяет компилятору выполнять эффективный доступ к элементам и даже выполнять предварительную выборку элементов, чтобы свести к минимуму задержку. Однако можем ли мы раздвинуть границы оптимизации еще дальше? Давайте отправимся в это путешествие и изучим дополнительные способы улучшения нашего кода 😊

Обработка двух элементов в каждой итерации

До сих пор мы обрабатывали один элемент 500 000 раз. Однако что, если мы попробуем другой подход и обработаем два элемента одновременно, но всего за 250 000 итераций? У меня есть подозрение, что обработка 500 000 итераций может привести к снижению производительности. Давайте проверим это и посмотрим на результаты:

Вспомните время выполнения 0,031 секунды, когда мы использовали цикл forEach. Благодаря этому новому подходу мы добились впечатляющего прироста производительности, в результате чего время выполнения составило всего 0,007 секунды. Это представляет собой замечательное улучшение на 71% по сравнению с циклом forEach и поразительное улучшение на 87% по сравнению с нашим исходным кодом!

Посмотрите на различия:

Это удивительно, но почему это происходит?

Трюк называется «раскручивание петли». При развертывании цикла мы выполняем несколько шагов за одну итерацию цикла, тем самым уменьшая общее количество итераций. Вы можете спросить себя: «Но это та же логика, какого черта?» Что ж, оказывается, что много итераций обходится недешево. Во-первых, увеличивается общее количество инструкций, потому что обработка цикла, проверка условия и переход к началу требуют дополнительных инструкций. Более того, как только ЦП «узнает» больше о следующих инструкциях, которые ему необходимо выполнить, он сможет лучше оптимизировать себя.

Заключительные слова об оптимизации

Первое правило оптимизации — «Не».

Второе правило оптимизации: «Еще нет».

Пожалуйста, избегайте немедленного «раскручивания» ваших петель в результате этого поста. Намерение здесь состоит в том, чтобы загрузить вас знаниями, чтобы стать лучшим программистом. Когда-нибудь вы можете столкнуться со сценариями с тяжелыми циклами, которые требуют оптимизации, и развертывание циклов может быть жизнеспособным решением. В вашей повседневной работе 99% циклов будут работать просто отлично.