Мне было интересно, есть ли целочисленная инструкция SSE2 / AVX2 или последовательность инструкций (или встроенных функций), которые должны быть выполнены для достижения следующего результата:
Дана строка из 8 байтовых пикселей вида:
A = {a, b, c, d, e, f, g, h}
Есть ли способ загрузить эти пиксели в регистр YMM, который содержит 8 32-битных пикселей ARGB, так что начальное значение в градациях серого передается в другие 2 байта каждого соответствующего 32-битного пикселя? Результат должен быть примерно таким: (0 — это альфа-значение)
B = {0aaa, 0bbb, 0ccc, 0ddd, 0eee, 0fff, 0ggg, 0hhh}
Я полный новичок в области векторных расширений, поэтому я даже не знаю, как к этому подойти или вообще возможно.
Любая помощь будет оценена. Спасибо!
Update1
Спасибо за ваши ответы. У меня все еще есть проблема, хотя:
Я собрал этот небольшой пример и скомпилировал с VS2015 на x64.
int main()
{
unsigned char* pixels = (unsigned char*)_aligned_malloc(64, 32);
memset(pixels, 0, 64);
for (unsigned char i = 0; i < 8; i++)
pixels[i] = 0xaa + i;
__m128i grayscalePix = _mm_load_si128((const __m128i*)pixels);
__m256i rgba = _mm256_cvtepu8_epi32(grayscalePix);
__m256i mulOperand = _mm256_set1_epi32(0x00010101);
__m256i result = _mm256_mullo_epi32(rgba, mulOperand);
_aligned_free(pixels);
return 0;
}
Проблема в том, что после выполнения
__m256i rgba = mm256_cvtepu8_epi32(grayscalePix)
RGBA только первые четыре двойных слова установлены. Последние четыре все 0.
Руководство разработчика Intel гласит:
VPMOVZXBD ymm1, xmm2 / m64
Ноль расширяет 8 упакованных 8-битных целых в младшем 8
байтов от xmm2 / m64 до 8 упакованных 32-битных целых чисел в
ymm1.
Я не уверен, является ли это предполагаемым поведением, или я все еще что-то упускаю.
Благодарю.
Начните с PMOVZX, как предлагает Марк.
Но после этого PSHUFB (_mm256_shuffle_epi8
) будет намного быстрее, чем PMULLD, за исключением того, что он конкурирует за случайный порт с PMOVZX. (И он работает на линии, так что вам все еще нужен PMOVZX).
Так что, если вы заботитесь только о пропускной способности, а не о задержке, то _mm256_mullo_epi32
это хорошо. Но если задержка имеет значение, или если ваши узкополосные узкие места в чем-то отличны от 2-х команд в случайном порядке на каждый вектор, тогда PSHUFB для дублирования байтов в каждом пикселе должен быть лучшим.
На самом деле, даже для пропускной способности, _mm256_mullo_epi32
плохо для HSW и BDW: это 2 мопа (задержка 10 с) для p0, поэтому это 2 моп для одного порта.
В SKL это 2 мопа (задержка 10 с) для p01, поэтому он может поддерживать такую же пропускную способность на тактовую частоту, что и VPMOVZXBD. Но это дополнительный 1 моп, что делает его более вероятным узким местом.
(VPSHUFB равен 1 моп, задержка 1 с для порта 5 на всех процессорах Intel, поддерживающих AVX2.)
Вы можете загрузить упакованные байты в регистр,
call __m256i _mm256_cvtepu8_epi32 (__m128i a)
преобразовать в 32-битные значения, а затем умножить на 0x00010101, чтобы воспроизвести серую шкалу в R, G и B.