C 尽可能快地比较缓冲区

C 尽可能快地比较缓冲区,c,windows,sse,C,Windows,Sse,我需要比较两个缓冲区是否相等。我不需要关于两个缓冲区之间关系的信息,只要两个块是否相等。我的英特尔计算机最多支持SSE4.2 天真的做法是: const size_t CHUNK_SIZE = 16; //128bit for SSE2 integer registers const int ARRAY_SIZE = 200000000; char* array_1 = (char*)_aligned_malloc(ARRAY_SIZE, 16); char* array_2 = (char*

我需要比较两个缓冲区是否相等。我不需要关于两个缓冲区之间关系的信息,只要两个块是否相等。我的英特尔计算机最多支持SSE4.2

天真的做法是:

const size_t CHUNK_SIZE = 16; //128bit for SSE2 integer registers
const int ARRAY_SIZE = 200000000;

char* array_1 = (char*)_aligned_malloc(ARRAY_SIZE, 16);
char* array_2 = (char*)_aligned_malloc(ARRAY_SIZE, 16);

for (size_t i = 0; i < ARRAY_SIZE; )
{
    volatile bool result = memcmp(array_1+i, array_2+i, CHUNK_SIZE);
    i += CHUNK_SIZE;
}
const size\u t CHUNK\u size=16//SSE2整数寄存器的128位
常量int数组_SIZE=200000000;
字符*数组\ 1=(字符*)\对齐\ malloc(数组\大小,16);
字符*数组2=(字符*)\u对齐的\u malloc(数组大小,16);
对于(大小i=0;i<数组大小;)
{
volatile bool result=memcmp(数组_1+i、数组_2+i、块大小);
i+=块大小;
}
与我第一次尝试使用SSE相比:

union U
{
    __m128i m;
    volatile int i[4];
} res;

for (size_t i = 0; i < ARRAY_SIZE; )
{
    __m128i* pa1 = (__m128i*)(array_1+i);
    __m128i* pa2 = (__m128i*)(array_2+i);
    res.m = _mm_cmpeq_epi32(*pa1, *pa2);
    volatile bool result =  ( (res.i[0]==0) || (res.i[1]==0) || (res.i[2]==0) || (res.i[3]==0) );
    i += CHUNK_SIZE;
}
union U
{
__m128im;
挥发性intⅠ[4];
}res;
对于(大小i=0;i<数组大小;)
{
__m128i*pa1=(u m128i*)(数组_1+i);
__m128i*pa2=(u m128i*)(数组_2+i);
res.m=_-mm_-cmpeq_-epi32(*pa1,*pa2);
volatile bool result=((res.i[0]==0)| |(res.i[1]==0)| |(res.i[2]==0)| |(res.i[3]==0));
i+=块大小;
}

速度增益约为33%。我能做得更好吗?

您真的不应该使用标量代码和联合来测试所有单独的向量元素,而是应该这样做:

for (size_t i = 0; i < ARRAY_SIZE; i += CHUNK_SIZE)
{
    const __m128i a1 = _mm_load_si128(array_1 + i);
    const __m128i a2 = _mm_load_si128(array_2 + i);
    const __m128i vcmp = _mm_cmpeq_epi32(a1, a2);
    const int vmask = _mm_movemask_epi8(vcmp);
    const bool result = (vmask == 0xffff);
    // you probably want to break here if you get a mismatch ???
}
for(size\u t i=0;i
既然您可以使用SSE 4.1,还有另一种可能更快的选择:

for (size_t i = 0; i < ARRAY_SIZE; i += CHUNK_SIZE;)
{
    __m128i* pa1 = (__m128i*)(array_1+i);
    __m128i* pa2 = (__m128i*)(array_2+i);
    __m128i temp = _mm_xor_si128(*pa1, *pa2);
    bool result = (bool)_mm_testz_si128(temp, temp);
}
for(size\u t i=0;i

如果
a&b!=如果
a&b==0,则返回
1
。其优点是,您也可以将此版本与新的AVX指令一起使用,其中块大小为32字节。

在这段特定代码中是否存在瓶颈?是,这是我的程序中的主要热点。除非您的
memcmpy
实现被破坏,否则您将很难打败它-它应该已经进行了SIMD优化。但我仍然测量了增益。我正在使用VS2005并进入发布模式编译的memcmp函数不会显示任何SSE操作码。添加了“windows”标记,因为您正在与MSVC版本的memcmp进行比较。谢谢,这正是我在寻找的信息,但不幸的是,与在这种特殊情况下使用union相比,收益是相同的。很可能是您的瓶颈是内存带宽,因此,加速比较操作可能是徒劳的。对于我的设置,有没有简单的方法来证明这一点?你只需做数学运算,看看你在比较多少MB/秒,然后将其与你的FSB带宽进行比较。谢谢。不幸的是,正如我在那一刻学到的,VS2005不支持v2以上的SSE。也许我可以找出操作码,改用内联asm。我尝试使用支持此指令的英特尔编译器。性能与这里介绍的其他两种方法相当。感谢您尝试代码。正如Paul R所指出的,内存带宽很可能是这里的瓶颈。这段代码可能会快1到2个周期。