Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/56.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 如何使用SIMD加速两个内存块的异或运算?_C_Xor_Simd - Fatal编程技术网

C 如何使用SIMD加速两个内存块的异或运算?

C 如何使用SIMD加速两个内存块的异或运算?,c,xor,simd,C,Xor,Simd,我想尽快对两块内存进行异或运算,如何使用SIMD来加速 我的原始代码如下: void region_xor_w64( unsigned char *r1, /* Region 1 */ unsigned char *r2, /* Region 2 */ int nbytes) /* Number of bytes in region */ { uin

我想尽快对两块内存进行异或运算,如何使用SIMD来加速

我的原始代码如下:

void region_xor_w64(   unsigned char *r1,         /* Region 1 */
                       unsigned char *r2,         /* Region 2 */
                       int nbytes)       /* Number of bytes in region */
{
    uint64_t *l1;
    uint64_t *l2;
    uint64_t *ltop;
    unsigned char *ctop;

    ctop = r1 + nbytes;
    ltop = (uint64_t *) ctop;
    l1 = (uint64_t *) r1;
    l2 = (uint64_t *) r2;

    while (l1 < ltop) {
        *l2 = ((*l1)  ^ (*l2));
        l1++;
        l2++;
    }
}
void region\u xor\u w64(无符号字符*r1,/*区域1*/
无符号字符*r2,/*区域2*/
int nbytes)/*区域中的字节数*/
{
uint64_t*l1;
uint64_t*l2;
uint64_t*ltop;
无符号字符*ctop;
ctop=r1+N字节;
ltop=(uint64_t*)ctop;
l1=(uint64_t*)r1;
l2=(uint64_t*)r2;
而(l1
我自己写了一本,但速度几乎没有提高

void region_xor_sse(   unsigned char* dst,
                       unsigned char* src,
                       int block_size){
  const __m128i* wrd_ptr = (__m128i*)src;
  const __m128i* wrd_end = (__m128i*)(src+block_size);
  __m128i* dst_ptr = (__m128i*)dst;

  do{
    __m128i xmm1 = _mm_load_si128(wrd_ptr);
    __m128i xmm2 = _mm_load_si128(dst_ptr);

    xmm2 = _mm_xor_si128(xmm1, xmm2);
    _mm_store_si128(dst_ptr, xmm2);
    ++dst_ptr;
    ++wrd_ptr;
  }while(wrd_ptr < wrd_end);
}
void region\u xor\u sse(无符号字符*dst,
无符号字符*src,
整数块(单位大小){
常数m128i*wrd\u ptr=(\uu m128i*)src;
常数m128i*wrd\u end=(\uu m128i*)(src+块大小);
__m128i*dst_ptr=(u m128i*)dst;
做{
__m128i xmm1=_mm_load_si128(wrd_ptr);
__m128i xmm2=毫米负载si128(dst ptr);
xmm2=(xmm1,xmm2);
_mm_store_si128(dst_ptr,xmm2);
++dst_ptr;
++wrd_ptr;
}而(wrd_ptr
更重要的问题是,为什么要手动执行此操作。你有没有一个你认为你能比得上的古老编译器?那些不得不手动编写SIMD指令的好时光已经过去了。今天,在99%的情况下,编译器会为您完成这项工作,而且很可能它会做得更好。另外,不要忘记,随着越来越多的扩展指令集,每隔一段时间就会出现新的体系结构。所以问自己一个问题——您是否希望为每个平台维护N个实现副本?您是否希望不断测试您的实现以确保它值得维护?答案很可能是否定的

您需要做的唯一一件事就是编写尽可能简单的代码。编译器将完成其余的工作。例如,我将如何编写您的函数:

void region_xor_w64(unsigned char *r1, unsigned char *r2, unsigned int len)
{
    unsigned int i;
    for (i = 0; i < len; ++i)
        r2[i] = r1[i] ^ r2[i];
}
正如@Mystical所指出的,上面的代码使用的指令支持未对齐的访问。那些比较慢。然而,若程序员能够正确地假设对齐访问,那个么就有可能让编译器知道它。例如:

void region_xor_w64(unsigned char * restrict r1,
                    unsigned char * restrict r2,
                    unsigned int len)
{
    unsigned char * restrict p1 = __builtin_assume_aligned(r1, 16);
    unsigned char * restrict p2 = __builtin_assume_aligned(r2, 16);

    unsigned int i;
    for (i = 0; i < len; ++i)
        p2[i] = p1[i] ^ p2[i];
}
明天,当我给自己买一台装有Haswell CPU的笔记本电脑时,编译器将为我生成一个使用256位指令的代码,而不是使用同一代码中的128位指令,从而使向量性能提高一倍。即使我不知道哈斯韦尔有能力做到这一点,它也能做到。您不仅需要了解该特性,还需要编写代码的另一个版本,并花费一些时间对其进行测试

顺便说一句,您的实现中似乎也有一个bug,代码最多可以跳过数据向量中剩余的3个字节

无论如何,我建议您信任您的编译器,并学习如何验证生成的内容(即熟悉
objdump
)。下一个选择是更改编译器。然后才开始考虑手动编写向量处理指令。否则你就要倒霉了


希望能有帮助。祝你好运

由于区域的大小是通过值传递的,为什么代码不是:

void region_xor_w64(unsigned char *r1, unsigned char *r2, unsigned int i)
{
    while (i--)
        r2[i] = r1[i] ^ r2[i];
}
甚至:

void region_xor_w64(unsigned char *r1, unsigned char *r2, unsigned int i)
{
    while (i--)
        r2[i] ^= r1[i];
}
如果倾向于向前(“向上内存”)和使用指针,那么:

void region_xor_w64(unsigned char *r1, unsigned char *r2, unsigned int i)
{
    while (i--)
        *r2++ ^= *r1++;
}

你在哪个站台上跑步?您可以使用的SIMD工具非常特定于平台。@JasonR 64位linux支持SSE4.2您可以尝试展开循环。当你没有对每一个输出值进行大量运算时,很难获得很大的性能提升。另外,如果要使用对齐的加载/存储指令,请注意缓冲区的对齐。忘记我的注释。我有点心烦意乱,没有注意到您在最后提到了对齐的内容。同时,我要注意,增加数据类型大小实际上没有帮助,除非您一直到
\uuum128i
。因为即使是64位整数对齐也不足以消除对
movdqu
的需要。我从来没有真正使用过它,因为我的大部分工作都是在MSVC中完成的。我发现,在大多数情况下,如果一个简单的循环恰好对性能至关重要,通常可以进行更高级别的转换,以获得比简单的矢量化更多的改进。但也就是说,这些转换是特定于应用程序的,有时并不那么容易实现。因此,对大多数人来说,破解编译器扩展可能是更简单的方法。@VladLazarenko:非常感谢,这个功能是我代码的一部分,对齐检查功能将确保大小是128位的倍数。99%的时间是夸大的。有很多关于矢量化的问题gcc根本不会自动矢量化。有时,叮当声或ICC会。或者有时候gcc会,但clang不会。您可以使用SSE4/AVX2做很多事情,而不仅仅是像这样简单的垂直的东西。我的意思是,在适当的情况下使用
restrict
启用自动矢量化是很好的,编译器在这种情况下会做得很好。在涉及加宽或变窄的更复杂的情况下,它们有时在自动矢量化方面做得很糟糕,在自动矢量化中,您可以以2倍或更大的倍数击败它们。HW预取通常在向前运行时效果稍好一些。Intel CPU中的L2拖缆在两个方向上都能正常工作,但我不确定L1d预取。如果您有AVX,那么您通常希望编译器使用指针增量而不是索引寻址模式,因此以OP的方式编写它更接近您想要的asm。除非这与编译器的实际操作无关(或者对于Sandybridge/IvyBridge,也可以使用SSE2存储和
px或xmm0,[rsi]
,如果编译器对齐输入以便可以在主循环中折叠负载。)@PeterCordes我添加了一个版本,用于提升内存和使用指针,但仍然
void region_xor_w64(unsigned char *r1, unsigned char *r2, unsigned int i)
{
    while (i--)
        r2[i] ^= r1[i];
}
void region_xor_w64(unsigned char *r1, unsigned char *r2, unsigned int i)
{
    while (i--)
        *r2++ ^= *r1++;
}