Gcc 16个8位值的SSE 4 popcount?

Gcc 16个8位值的SSE 4 popcount?,gcc,counter,sse,intrinsics,population,Gcc,Counter,Sse,Intrinsics,Population,我有以下代码,它使用标志-msse4与GCC一起编译,但问题是pop计数只获取转换后的\uuum128i类型的最后四个8位。基本上,我想要的是计算\uum128i类型中的所有16个数字,但我不确定在创建变量popA后要调用什么内在函数。不知何故,popA必须转换成包含所有128位信息的整数?我想有\u mm\u cvtsi128\u si64并且使用了一些洗牌操作,但我的操作系统是32位的。是否只有洗牌方法和使用\u mm\u cvtsi128\u si32 编辑:如果洗牌方法是唯一的选择,我需

我有以下代码,它使用标志
-msse4
与GCC一起编译,但问题是pop计数只获取转换后的
\uuum128i
类型的最后四个8位。基本上,我想要的是计算
\uum128i
类型中的所有16个数字,但我不确定在创建变量
popA
后要调用什么内在函数。不知何故,
popA
必须转换成包含所有128位信息的整数?我想有
\u mm\u cvtsi128\u si64
并且使用了一些洗牌操作,但我的操作系统是32位的。是否只有洗牌方法和使用
\u mm\u cvtsi128\u si32

编辑:如果洗牌方法是唯一的选择,我需要帮助为我的32位操作系统实现它,请

这是密码

#include <stdio.h>
#include <smmintrin.h>
#include <emmintrin.h>

int main(void)
{
    int A = 1;
    __m128i popA = _mm_set_epi8( A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A);

    unsigned int integer = _mm_cvtsi128_si32(popA);
    //long long LONG = _mm_cvtsi128_si64(popA);//my OS is 32-bits so no luck here

    printf("integer = %d\n", integer);
    int pop = _mm_popcnt_u32(integer);
    //int popLONG = _mm_popcnt_u64(LONG);
    printf("popcount = %d\n", pop);
    //printf("popcount LONG = %d\n", popLONG);

    return 0;
}

popcnt
与SSE4.2 ISA扩展同时引入,但不在SSE向量寄存器上运行。对于每个单独的结果,您需要单独的说明

此外,它不是为8位操作数定义的。如果需要每个字节的计数,则需要填充到16位

您可以在64位寄存器中一次对8个字节求和,但这听起来不像您想要的

参考文献:

SSE2溶液。 我还没有对此进行测试,但是您可以使用
0x8080
…和SSE寄存器来获得所有1或所有0的16字节掩码。对一个字节中的所有8位重复此操作,并对掩码求和。因为所有1在2的补码中表示-1,所以对16个字节求反,就得到了所有结果

AND和比较操作应该能够并行运行。添加链是独立的,但它应该运行得非常快,并且适合32条指令。(只需添加7个。)


16个8位值的SSE 4 popcount可通过以下方式并行完成:

#include <stdio.h>
#include <stdint.h>
#include <immintrin.h>

//----------------------------------------------------------------------------
//
// parallelPopcnt16bytes - find population count for 8-bit groups in xmm (16 groups)
//                         each byte of xmm result contains a value ranging from 0 to 8
//
static __m128i parallelPopcnt16bytes (__m128i xmm)
   {
    const __m128i mask4 = _mm_set1_epi8 (0x0F);
    const __m128i lookup = _mm_setr_epi8 (0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4);
   __m128i low, high, count;

   low = _mm_and_si128 (mask4, xmm);
   high = _mm_and_si128 (mask4, _mm_srli_epi16 (xmm, 4));
   count = _mm_add_epi8 (_mm_shuffle_epi8 (lookup, low), _mm_shuffle_epi8 (lookup, high));
   return count;
   }

//----------------------------------------------------------------------------

int main (void)
    {
    int index;
    __m128i testVector = _mm_set_epi8 (1, 2, 4, 8, 16, 32, 64, 128, 0, 1, 3, 7, 15, 31, 63, 127);
    __m128i counts = parallelPopcnt16bytes (testVector);

    printf ("population count for each byte:");
    for (index = 15; index >= 0; index--)
        {
        uint8_t *bytes = (void *) &counts;
        printf (" %d", bytes [index]);
        }
    printf ("\n");
    return 0;
    }

//----------------------------------------------------------------------------
#包括
#包括
#包括
//----------------------------------------------------------------------------
//
//parallelPopcnt16bytes-查找xmm中8位组的填充计数(16组)
//xmm结果的每个字节都包含一个从0到8的值
//
静态uum128i并行POPCNT16bytes(uuuum128i xmm)
{
常数m128i mask4=\u mm\u set1\u epi8(0x0F);
常量m128i查找=_mm_setr_epi8(0,1,1,2,2,3,1,2,2,3,2,3,3,3,3,3,3,4);
__m128i低、高、计数;
低=_mm_和_si128(mask4,xmm);
高=_mm_和_si128(mask4,_mm_srli_epi16(xmm,4));
计数=_mm_add_epi8(_mm_shuffle_epi8(查找,低),_mm_shuffle_epi8(查找,高));
返回计数;
}
//----------------------------------------------------------------------------
内部主(空)
{
整数指数;
__m128i testVector=_mm_set_epi8(1,2,4,8,16,32,64,128,0,1,3,7,15,31,63,127);
__m128i计数=parallelPopcnt16bytes(testVector);
printf(“每个字节的总体计数:”);
对于(索引=15;索引>=0;索引--)
{
uint8_t*字节=(void*)和计数;
printf(“%d”,字节[索引]);
}
printf(“\n”);
返回0;
}
//----------------------------------------------------------------------------

没关系。我想我会尝试不同的popcount。@user2555139使用
循环8次,比较为零,然后进行加法,然后对最终结果求反,应该能够在26条指令中生成所有16个结果,并且少于26个周期,因为循环迭代不相关。您有可能为我编写这部分代码吗?我不太确定该使用什么本质。仍然是新的。@user2555139请参见编辑。实际上是32英寸,但谁在数呢。我仍然没有尝试编译任何东西,请发布它是否有效以及速度有多快。我已经尽了最大努力,请查看上面我的帖子以了解错误。很抱歉,我没有足够的能力找出原因。您是希望对整个128位向量进行一次总体计数,还是希望对16次总体计数,每8位元素一次?哪种更有效地使用。我现在认为,对于一组
int
来说,使用片上popcount并不好,在32位操作系统上使用64位数据也没用。到目前为止,popcnt还不是瑞士军刀式教学法的雏形。也许到了SSE 5,它会变得很棒。@PaulR我需要它来处理8位值。@user2555139对不起,它是
\u mm\u和()
而不是
\u mm\u和ps
。序列是0x80、0x40、0x20、0x10、0x08、0x04、0x02、0x01。事实上,按什么顺序执行它们并不重要,只要每个位置值只使用一次就行了
popcount.c | 10 |错误:使用类型“int”初始化类型“u m128i”时,类型不兼容。
。我添加了标志
-msse-msse2-msse3-msse4
,并且我正在使用标题
#include#include#include#include
还能是什么?这行
计数=_mm_add_epi8(_mm_shuffle#epi8(查找,低),_mm_shuffle#epi8(查找,高))给了我2个错误<代码>错误:参数1的“_mm_add_epi8”的类型不兼容;错误:参数2的类型不兼容“_mm_add_epi8”|
。我不得不使用GCC添加
#include
。添加#include解决了问题吗?我只使用Microsoft VS2012和mingw+gcc测试了这段代码。非常好-我刚刚编写了一个几乎相同的例程,但你比我快。请注意,(至少在英特尔CPU上)这只需要SSSE3(用于
PSHUFB
),而不需要SSE4,即
#include
。又好又快!仅在注释中,“xmm结果的每个字节都包含一个从0到8的值。”感谢您的更正。我相信这一切都归功于沃伊切赫·穆拉。AVX2自适应是可能的(),在某些情况下甚至可能比popcnt指令更快。
/* init */
__m128i bit0 = _mm_set1_epi8( 0x80 );
__m128i mask0 = _mm_and_si128( in, bit0 );
__m128i sum = _mm_cmpeq_epi8( mask0, _mm_setzero_si128() );

/* general pattern */
__m128i bit1 = _mm_set1_epi8( 0x40 );
__m128i mask1 = _mm_and_si128( in, bit1 );
mask1 = _mm_cmpeq_epi8( mask1, _mm_setzero_si128() );
sum = _mm_add_epi8( sum, mask1 );

/* next bit */
__m128i bit2 = _mm_set1_epi8( 0x20 );
__m128i mask2 = _mm_and_si128( in, bit2 );
mask2 = _mm_cmpeq_epi8( mask2, _mm_setzero_si128() );
sum = _mm_add_epi8( sum, mask2 );

...

/* finish up */
sum = _mm_sub_epi8( _mm_setzero_si128(), sum );
#include <stdio.h>
#include <stdint.h>
#include <immintrin.h>

//----------------------------------------------------------------------------
//
// parallelPopcnt16bytes - find population count for 8-bit groups in xmm (16 groups)
//                         each byte of xmm result contains a value ranging from 0 to 8
//
static __m128i parallelPopcnt16bytes (__m128i xmm)
   {
    const __m128i mask4 = _mm_set1_epi8 (0x0F);
    const __m128i lookup = _mm_setr_epi8 (0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4);
   __m128i low, high, count;

   low = _mm_and_si128 (mask4, xmm);
   high = _mm_and_si128 (mask4, _mm_srli_epi16 (xmm, 4));
   count = _mm_add_epi8 (_mm_shuffle_epi8 (lookup, low), _mm_shuffle_epi8 (lookup, high));
   return count;
   }

//----------------------------------------------------------------------------

int main (void)
    {
    int index;
    __m128i testVector = _mm_set_epi8 (1, 2, 4, 8, 16, 32, 64, 128, 0, 1, 3, 7, 15, 31, 63, 127);
    __m128i counts = parallelPopcnt16bytes (testVector);

    printf ("population count for each byte:");
    for (index = 15; index >= 0; index--)
        {
        uint8_t *bytes = (void *) &counts;
        printf (" %d", bytes [index]);
        }
    printf ("\n");
    return 0;
    }

//----------------------------------------------------------------------------