2016-11-08 11 views
2

мне было интересно, если есть/AVX2 целого число инструкции SSE2 или последовательность команд (или встроенные функции) должны быть выполнены для того, чтобы достичь следующего результата:C++ SSE2 или AVX2 встроенные функции для градации серого для преобразования ARGB

Учитывая строка 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.

Я не уверен, что это предполагаемое поведение, или я все еще что-то пропустил.

Спасибо.

+0

Ваш код выглядит правильно. Вы уверены, что не просто проверяете это неправильно? Или что компилятор не оптимизировал некоторые из них, потому что результаты не используются? [On Godbolt] (https://godbolt.org/g/TR15E9), мне пришлось использовать '-O0', чтобы компилятор сохранял векторные ops. Даже '-Og' или' -O1' оптимизировали все, кроме malloc/free. Попробуйте сохранить вектор в массив 'uint32_t' и напечатать его с помощью' printf' или что-то вроде C++ ish. –

+0

Оптимизатор не вызывает беспокойства, поскольку я тестировал это в режиме отладки, но вы все еще были правы, хотя :) По-видимому, отладчик VS не отображает значения '_m256i' правильно. Похоже, что он обрезает их на границе '_m128i'. Кроме того, окно регистров тоже не помогло. Все выглядит хорошо после того, как я сохранил вектор в памяти и сделал «printf», поэтому, я думаю, спасибо в порядке :) – redeye

+0

О, ничего не значит, все плохо, если вы не можете доверять отладчику! Получается ли лучше с отладчиком, когда вы используете результат? –

ответ

3

Start с PMOVZX как Марк предлагает.

Но после этого PSHUFB (_mm256_shuffle_epi8) будет намного быстрее, чем PMULLD, за исключением того, что он конкурирует за порт в случайном порядке с PMOVZX. (И он работает в полосе, поэтому вам все равно нужен PMOVZX).

Так что, если вас беспокоит только пропускная способность, а не латентность, то _mm256_mullo_epi32 - это хорошо. Но если латентность имеет значение или если ваши узкие места в пропускной способности на что-то отличное от двух инструкций по перемещению портов на вектор в любом случае, то лучше всего использовать PSHUFB для дублирования байтов в каждом пикселе.

На самом деле, даже для пропускной способности, _mm256_mullo_epi32 плохой для HSW и BDW: это 2 часа (10 с) для p0, поэтому для одного порта это 2 устройства.

В SKL это 2 часа (10 с) для p01, поэтому он может поддерживать одну и ту же пропускную способность за такт в качестве VPMOVZXBD. Но это лишний 1 мкп, что делает его более узким местом.

(VPSHUFB 1 моп, 1c задержка, для порта 5, на всех процессорах Intel, которые поддерживают AVX2.)

2

Вы можете загрузить упакованные байты в регистр, call __m256i _mm256_cvtepu8_epi32 (__m128i a) для преобразования 32-битных значений, а затем умножить на 0x00010101 повторить серую шкалу в R, G и B.

+0

pshufb часто будет лучше, чем умножить. См. Мой ответ. –