Комментарии 10
<
вместо <=
в качестве низкоуровневой оптимизации, которая экономит одну процессорную инструкцию в некоторых менее распространённых или устаревших архитектурах. Существуют и противоположные точки зрения: например, для определённых версий компилятора Microsoft Visual C++ >=
лучше чем >
и <=
, то есть итерироваться надо в обратном порядке и декрементить переменную цикла.Если же мы посмотрим на ассемблерный код, выдаваемый современным компилятором С++ под платформу x86_64 (GCC 11.2), то окажется, что для обоих вариантов функции, выполняющих итерацию по массиву, выдаётся практически идентичный набор инструкций. Отличие лишь в инструкциях
jl
и jle
:1) godbolt.org/z/MKMM57EK6
2) godbolt.org/z/Kacd7xeEW
Вопрос в том, выполняются ли
jl
и jle
за одно и то же время. Быстрый ответ — да. Но точный ответ весьма затруднён из-за огромного разнообразия процессорных архитектур и оптимизаций, применяемых в современных процессорах: конвейеризация и суперскалярность позволяет нескольким инструкциям процессора выполняться параллельно, а внеочередное исполнение может менять порядок исполнения инструкций. Поэтому для анализа производительности инструкций в основном применяются статистические подходы.Но всё же есть специальные таблицы, позволяющие определить количество микроопераций, выполняемых процессором при проведении макроопераций, таких как
jl
и jle
. Согласно этому документу на большинстве архитектур conditional jump
в любом варианте «стоит» ровно одну микрооперацию — то есть между jl
и jle
разницы нет.Что же касается сравнения операторов
<
и !=
, то первый считается чуть более безопасным, точнее, располагающим к написанию более безопасного кода.Картинки красивые, но статья, на мой взгляд, так себе.
Очень поверхностно пробежались по теме, вида "скопипасть вот это в код чтобы программа работала быстрее". Причём в 99% случаев это не поможет.
Не раскрыта тема авто-векторизации, почему компилятор может или не может её применять (в том числе не рассказано про диагностику таких случаев).
Не раскрыта тема SIMD инструкций, их семейства, доступность, что именно они могут делать, и.т.п. Словно бы это просто некий "волшебный ускоритель"
Нет вывода дизассемблера. Скопипастили в код, тык-мык, минус сколько-то миллисекунд, продолжаем пить смузи. Прямо stackoverflow programming.
Помимо захардкоживания intrinsics или танцами с бубном над компилятором есть кросс-платформенные библиотеки, где весь векторизованный код уже может быть написан.
Это уровень доклада на школьной (даже не студенческой) конференции, несерьёзно.
Чуть более подробная статья на эту тему: https://habr.com/ru/post/529204/
Действительно как-то очень простенько.
Фактически всё ограничивается знакомством с некими магическими заклинаниями для ускорения кода, "но всемогущий маг лишь на бумаге я" - 18% это очень мало для успешной векторизации.
Я попытался восстановить полный код примера, правда на x86, c ARM/Neon я не знаком:
https://gcc.godbolt.org/z/99Yfd7Y9E
Видно, что векторизует, но масса вот этих vpinsrb, выдёргиваний по 1 байтику - это явно неэффективно.
Буферы U,V имеют в 2 раза меньшее разрешение - логично считать за одну итерацию квадрат 2*2 пикселя, чтобы не читать одни те же значения U,V по 4 раза:
https://gcc.godbolt.org/z/T68hdvTcs
Вроде бы стало лучше, во всяком случае махинаций с байтиками сходу не вижу. Но надо тестировать.
Также я заменил прагму assume_safety на модификатор __restrict для параметров, действует аналогично, но совместимо с gcc.
Ещё следует подумать, можно ли избавиться от плавающей точки с сохранением приемлемой точности, это уберёт лишние команды конверсии и может улучшить векторизацию (если обойтись int16, то их больше влезет в вектор, чем float32).
В общем, надо дописывать свою статью про высокоуровневую векторизацию (у меня на другом примере, но не суть).
С тестом:
https://gcc.godbolt.org/z/9n53e9588
Квадрат 2*2 оказался медленнее, видимо непоследовательная запись в память всё портила, поэтому переделал на 2 соседних пикселя по горизонтали, теперь в 2 раза быстрее. А простое дописывание vectorize(assume_safety) к циклу почти ничего не даёт (от 0 до 15%, но чаще 0).
Также в варианте с 2*2 я ошибся и не использовал y_pixel_stride, uv_row_stride, uv_pixel_stride - из-за этого и пропали vpinsrb. Все эти параметры дают гибкость, но с ними приходится брать по байтику, компилятор не может быть уверен, что данные лежат последовательно. Впрочем, по факту простыня vpinsrb не делает хуже, ускоряют именно 2 пикселя за итерацию.
Хотя 2 раза - тоже далеко не предел мечтаний, но больше не хочется тратить на это времени.
Полезная информация. Утащил в закладки.
Супер идея , вкрутите в "Редактор МойОфис Текст" рамочки по ГОСТ, и чтобы они работали не через неадекватные колонтитулы которые все ненавидят, и люди к вам потянутся, и студенты. и производственники с нашим всеми любимым ЕСКД.
Как помочь компилятору повысить быстродействие вашей программы