Про AVX2 и размывание кэшей
Вот наконец удалось ощутить офигенную пользу от AVX2, причем двойную. Вот такой вот код:
_mm256_stream_si256((__m256i *)&drowp[col], _mm256_i32gather_epi32((const int*)table, _mm256_cvttps_epi32(_mm256_load_ps(&srowp[col])), 4));
в 4.5-5 раз быстрее, чем простой SSE2 аналог (в котором, понятно, нет gather) и в ~6 раз быстрее скалярного C-кода:
drowp[col] = table[(unsigned)srowp[col]];
Рассмотрение всего хозяйства под микроскопом показало, что основной взнос в результат дает _mm256_stream, а вовсе не gather. Стоит заменить stream на store, как все сразу портится. По достаточно очевидной причине: длина строки и drowp и srowp - ровно 4 килобайта и вероятность налететь на алиасинг кэша весьма велика.
После переработки SSE-варианта на такой вот:
__m128 fpixel = _mm_load_ps(&srowp[col]);
_ALIGN32_F unsigned int ipix[4] _ALIGN32_E;
_mm_store_si128((__m128i *)ipix, _mm_cvttps_epi32(fpixel));
_ALIGN32_F unsigned int opix[4] _ALIGN32_E;
opix[0] = table[ipix[0]];
opix[1] = table[ipix[1]];
opix[2] = table[ipix[2]];
opix[3] = table[ipix[3]];
__m128i oreg = _mm_load_si128((const __m128i*)opix);
_mm_stream_si128((__m128i*)&drowp[col], oreg);
SSE-вариант стал в разы быстрее (все еще в полтора раза медленее, чем AVX, но не в 5), всего-лишь от замены прямой записи в память (через кэш) на запись мимо кэша (понятно что две последние строчки надо слить в одну, для красоты).
Плохая новость заключается в том, что этот код такой простой т.к. это для ЧБ-изображения, вся обработка уже спрятана в LUT. Внедрить такое же в цветном случае будет сложнее, но результат, похоже, того может стоить.
P.S. В коде выше, _ALIGN* - это макросы для MSVC и clang, вот такие:
#if defined (WIN32)
#define _ALIGN32_F __declspec(align(32))
#define _ALIGN32_E
#else
#define _ALIGN32_F
#define _ALIGN32_E __attribute__ ((aligned (32)))
#endif
P.P.S. Замена _load_ на _stream_load никакого выигрыша не дает. Добавление явного prefetch на следующий элемент (с проверкой длины, конечно) - дает еще малую толику.
P.P.P.S. Для просто просмотра картинок последовательно - выигрыш не будет виден от слова совсем, декодирование жатых файлов жрет больше в изрядно раз.
Comments
Странно, вот ну ни черта же
Странно, вот ну ни черта же не понимаю, кроме названий инструкций, но почему-то читать всё равно интересно. И вообще, это ж хорошо, когда внедряются новые технологии. Так что спасибо :)
Там (в FRV) наконец
Там (в FRV) наконец запланирована совсем большая польза от (продвинутой) видеокарты.
Для этого - нужно внутренние потроха полностью переработать. Вот, тружусь.
Насколько могу вспомнить, к этому месту не подходил года два, а все эти два года занимались бантиками, а не основной функциональностью, ради которой программа была сделана (ну, кроме экранного sharpening).
Бантики -- это, конечно,
Бантики -- это, конечно, нужно, но вот мне из-за их обилия стало не очень интересно заниматься тестами новых версий, потому что большей частью новых функций я не пользуюсь, а времени вникать во всё это нет. Стало очень много настроек, в которых без мануала уже и не разберёшься (это хорошо, когда каждую гайку по своему динамометру можно затянуть, но гаек реально много). Может, там как-то организовать кнопку Turn On Advanced Options или сделать ярлыки оптовых настроек "под Lightroom", "под С1" и т.п., чтобы новых не пугало количество гаек?
Настроек много, да, но
Настроек много, да, но defaults вроде вменяемые.
Может быть надо сделать basic options, либо отдельным диалогом, либо страничкой (первой) в Preferences.
Настройки, по большей части, появляются там, где поведение начинает отклоняться от старых версий - и нужна гайка "чтобы вернуть".
Префетч на СЛЕДУЮЩИЙ элемент
Префетч на СЛЕДУЮЩИЙ элемент — это уже слишком поздно. Надо дальше.
Перед фетчем текущего - я
Перед фетчем текущего - я делаю префетч следующего.
+25% перформанса (было 320мс, стало 250)
Дальше копать не стал, потому что на фоне всего остального (распаковка, то-се) выигрыш уже не виден.