Я какое-то время выполнял arm asm и пытался оптимизировать простые циклы с помощью x86 asm ssse3. Я не могу найти способ преобразовать порядковый номер в обратный.
ARM NEON имеет одну векторную инструкцию, чтобы сделать именно это, а SSSE3 - нет. Я попытался использовать 2 смены и «или», но для этого нужно перейти на 32 бита на слот вместо 16, если мы смещаемся на 8 влево (данные насыщаются).
Я посмотрел в PSHUFB, но когда я его использую, первая половина 16-битного слова всегда равна 0.
Я использую встроенный asm на x86 для Android. Извините за неправильный синтаксис или другие ошибки, которые могут возникнуть, пожалуйста, поймите, что я имею в виду (это трудно вырвать из моего кода).
# Data
uint16_t dataSrc[] = {0x7000, 0x4401, 0x3801, 0xf002, 0x4800, 0xb802, 0x1800,
0x3c00, 0xd800.....
uint16_t* src = dataSrc;
uint8_t * dst = new uint8_t[16] = {0};
uint8_t * map = new uint8_t[16] = { 9,8, 11,10, 13,12, 15,14, 1,0,3,2,5,4,7,6,};
# I need to convert 0x7000 to 0x0077 by shifting each 16 bit by its byte vectorized.
asm volatile (
"movdqu (%0),%%xmm1\n"
"pshufb %2,%%xmm1\n"
"movdqu %%xmm1,(%1)\n"
: "+r" (src),
"+r" (dst),
"+r" (map)
:
: "memory", "cc", "xmm0", "xmm1", "xmm2", "xmm3", "xmm4"
);
Если я перебираю переменную dataSrc, мои выходные данные для первых 8 байтов:
0: 0
1: 0
2: 0
3: 0
4: 72
5: 696
6: 24
7: 60
Меняются только последние 4, даже если они в неправильном порядке. Почему первые 4 все нули? Независимо от того, как я меняю карту, первая иногда равна 0, а следующие 3 всегда равны нулю, почему? Я делаю что-то не так?
Редактировать
Я выяснил, почему это не сработало, карта не прошла корректно во встроенный ассм, мне пришлось освободить для него входную переменную.
По другим вопросам об интрисике и рукописном асме. Приведенный ниже код предназначен для преобразования 16-байтовых данных видеокадра YUV42010BE в YUVP420 (8 бит), проблема в случайном порядке, если я использую переменную с прямым порядком байтов, то у меня не будет этого раздела.
static const char map[16] = { 9, 8, 11, 10, 13, 12, 15, 14, 1, 0, 3, 2, 5, 4, 7, 6 };
int dstStrideOffset = (dstStride - srcStride / 2);
asm volatile (
"push %%ebp\n"
// All 0s for packing
"xorps %%xmm0, %%xmm0\n"
"movdqu (%5),%%xmm4\n"
"yloop:\n"
// Set the counter for the stride
"mov %2, %%ebp\n"
"xloop:\n"
// Load source data
"movdqu (%0),%%xmm1\n"
"movdqu 16(%0),%%xmm2\n"
"add $32,%0\n"
// The first 4 16-bytes are 0,0,0,0, this is the issue.
"pshufb %%xmm4, %%xmm1\n"
"pshufb %%xmm4, %%xmm2\n"
// Shift each 16 bit to the right to convert
"psrlw $0x2,%%xmm1\n"
"psrlw $0x2,%%xmm2\n"
// Merge both 16bit vectors into 1 8bit vector
"packuswb %%xmm0, %%xmm1\n"
"packuswb %%xmm0, %%xmm2\n"
"unpcklpd %%xmm2, %%xmm1\n"
// Write the data
"movdqu %%xmm1,(%1)\n"
"add $16, %1\n"
// End loop, x = srcStride; x >= 0 ; x -= 32
"sub $32, %%ebp\n"
"jg xloop\n"
// End loop, y = height; y >= 0; --y
"add %4, %1\n"
"sub $1, %3\n"
"jg yloop\n"
"pop %%ebp\n"
: "+r" (src),
"+r" (dst),
"+r" (srcStride),
"+r" (height),
"+r"(dstStrideOffset)
: "x"(map)
: "memory", "cc", "xmm0", "xmm1", "xmm2", "xmm3", "xmm4"
);
Я еще не дошел до реализации shuffle для встроенных функций, используя little endian
const int dstStrideOffset = (dstStride - srcStride / 2);
__m128i mdata, mdata2;
const __m128i zeros = _mm_setzero_si128();
for (int y = height; y > 0; --y) {
for (int x = srcStride; x > 0; x -= 32) {
mdata = _mm_loadu_si128((const __m128i *)src);
mdata2 = _mm_loadu_si128((const __m128i *)(src + 8));
mdata = _mm_packus_epi16(_mm_srli_epi16(mdata, 2), zeros);
mdata2 = _mm_packus_epi16(_mm_srli_epi16(mdata2, 2), zeros);
_mm_storeu_si128( (__m128i *)dst, static_cast<__m128i>(_mm_unpacklo_pd(mdata, mdata2)));
src += 16;
dst += 16;
}
dst += dstStrideOffset;
}
Возможно, написано неправильно, но тестирование на эмуляторе Android (API 27), x86 (SSSE3 - наивысшее, i686) с настройками компилятора по умолчанию и такими дополнительными оптимизациями (хотя и не влияющими на производительность) -Ofast -O3 - funroll-loops -mssse3 -mfpmath = sse в среднем:
Intrinics: 1,9-2,1 мс
Рукописные: 0,7-1 мс
Есть ли способ ускорить это? Может быть, я написал интрисику неправильно, возможно ли приблизить скорость к рукописному написанию интриник?