2012-04-02 2 views
12

Внутренний _mm_slli_si128 выполнит логический сдвиг влево от 128-битного регистра, но ограничен немедленными значениями сдвига и сдвигает байты, а не бит.Ищет операцию сдвига sse 128 бит для значения немедленного сдвига

Я могу использовать встроенную функцию, например _mm_sll_epi64 или _mm_sll_epi32, чтобы сдвинуть влево набор значений в регистре __m128i, но они не несут бит «переполнения».

Для сдвига на N битах представьте себе, что я мог бы сделать что-то вроде:

  • _mm_sll_epi64
  • _mm_srr_epi64 (для бит я хочу нести: переместить их в низкий порядок)
  • перетасовать результат srr
  • или вместе.

(но, вероятно, также необходимо включить проверки N относительно 64).

Есть ли лучший способ?

+1

Я не думаю, что есть лучший способ. Я написал ответ на недавний дубликат этого вопроса: http://stackoverflow.com/q/34478328/224132. Для подсчетов постоянной времени компиляции он превращается в 4 insns или 2 insns с count> = 64. С подсчетом переменной он разветвляется и должен «movd» считать и 64-счет от целочисленных до векторных регистров. '__uint128_t' лучше в этом случае, если данные уже находятся в целочисленных регистрах. –

ответ

4

Не ваше идеальное решение, но если вы хотите повернуть или сдвинуть регистр SSE на несколько бит, что кратно 8, то может помочь инструкция PSHUFB (и встроенная операционная система _mm_shuffle_epi8()). В качестве входа требуется второй регистр SSE; каждый байт в регистре содержит значение, которое используется для индексации байтов в первом входном регистре.

+4

Я думаю, что ОП конкретно заявлял, что он хочет бит-гранулярности и не ограничивается непосредственными действиями. '_mm_shuffle_epi8()' является как байт-гранулярностью, так и требует немедленного. – Mysticial

+4

Я знаю, что он хотел бит зернистости, следовательно, первое предложение в моем ответе. Кроме того, '_mm_shuffle_epi8()' не требует немедленного; второй аргумент - значение '__m128i'. [См. Документацию здесь] (http://msdn.microsoft.com/en-us/library/bb531427.aspx). –

+1

Следует отметить, что для этой функции требуется поддержка SSSE3, чего может быть недостаточно, если вы хотите работать на старых компьютерах. –

4

Это появилось как побочный вопрос в блоге (мой) на unusual C preprocessor uses. Для 127 различных смещений сдвига есть четыре различные оптимальные последовательности команд SSE2 для сдвига бит. Препроцессор делает разумным создание функции сдвига, которая составляет 129-тактный оператор switch. Простите здесь необработанный код; Я не знаком с отправкой кода прямо здесь. Проверьте сообщение в блоге, чтобы узнать, что происходит.

#include <emmintrin.h> 

typedef __m128i XMM; 
#define xmbshl(x,n) _mm_slli_si128(x,n) // xm <<= 8*n -- BYTE shift left 
#define xmbshr(x,n) _mm_srli_si128(x,n) // xm >>= 8*n -- BYTE shift right 
#define xmshl64(x,n) _mm_slli_epi64(x,n) // xm.hi <<= n, xm.lo <<= n 
#define xmshr64(x,n) _mm_srli_epi64(x,n) // xm.hi >>= n, xm.lo >>= n 
#define xmand(a,b) _mm_and_si128(a,b) 
#define xmor(a,b) _mm_or_si128(a,b) 
#define xmxor(a,b) _mm_xor_si128(a,b) 
#define xmzero  _mm_setzero_si128() 

XMM xm_shl(XMM x, unsigned nbits) 
{ 
    // These macros generate (1,2,5,6) SSE2 instructions, respectively: 
    #define F1(n) case 8*(n): x = xmbshl(x, n); break; 
    #define F2(n) case n: x = xmshl64(xmbshl(x, (n)>>3), (n)&15); break; 
    #define F5(n) case n: x = xmor(xmshl64(x, n), xmshr64(xmbshl(x, 8), 64-(n))); break; 
    #define F6(n) case n: x = xmor(xmshl64(xmbshl(x, (n)>>3), (n)&15),\ 
            xmshr64(xmbshl(x, 8+((n)>>3)), 64-((n)&155))); break; 
    // These macros expand to 7 or 49 cases each: 
    #define DO_7(f,x) f((x)+1) f((x)+2) f((x)+3) f((x)+4) f((x)+5) f((x)+6) f((x)+7) 
    #define DO_7x7(f,y) DO_7(f,(y)+1*8) DO_7(f,(y)+2*8) DO_7(f,(y)+3*8) DO_7(f,(y)+4*8) \ 
             DO_7(f,(y)+5*8) DO_7(f,(y)+6*8) DO_7(f,(y)+7*8) 
    switch (nbits) { 
    case 0: break; 
    DO_7(F5, 0) // 1..7 
    DO_7(F1, 0) // 8,16,..56 
    DO_7(F1, 7) // 64,72,..120 
    DO_7x7(F6, 0) // 9..15 17..23 ... 57..63 i.e. [9..63]\[16,24,..,56] 
    DO_7x7(F2,56) // 65..71 73..79 ... 121..127 i.e. [65..127]\[64,72,..,120] 
    default: x = xmzero; 
    } 
    return x; 
} 

xm_shr составляет выше, но подкачка "SHL" и "СГК" всюду в F [1256] макросов. НТН.

+2

Собственно, приведенный выше код не работает примерно на половину значений сдвига. Я тестировал его со стандартным сдвигом на 128-битные целые числа (gcc поддерживает __uint128_t), и результаты заметно отличаются.Например, все сдвиги выше 120 всего лишь нулевые все биты. – seba

+1

Для подсчета смены времени во время компиляции вам не требуется больше 4 инструкций (или 5 без AVX: дополнительный «movdqa'). Для счетчика <64, сдвиг байта слева на 64b, затем сдвиг бит, который переносится справа на 64-счет. 'OR' переносить с' psllq xmm0, 64'. Я написал его с 'if', и он отлично компилируется для подсчета постоянной времени компиляции: http://goo.gl/O14GhI. См. Http://stackoverflow.com/a/34482688/224132 –

+0

Чтобы исправить код, просто замените каждое выражение & 15 или & 155 на & 7. Это говорит о том, что этот код очень медленный (знаете ли вы о ветвлении ?!), и предложение Питера Кордес выглядит гораздо более перспективным. –

Смежные вопросы