gpt4 book ai didi

c - 如何使用SIMD来加速两个内存块的异或?

转载 作者:行者123 更新时间:2023-11-30 17:10:12 36 4
gpt4 key购买 nike

我想尽快对两个内存块进行异或,如何使用 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_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);
}

最佳答案

更重要的问题是为什么要手动执行此操作。您是否有一个您认为可以智取的古老编译器?那些必须手动编写 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];
}

简单一点,不是吗?你猜怎么着,编译器正在生成使用 MOVDQU 执行 128 位 XOR 的代码。和 PXOR ,关键路径如下所示:

4008a0:       f3 0f 6f 04 06          movdqu xmm0,XMMWORD PTR [rsi+rax*1]
4008a5: 41 83 c0 01 add r8d,0x1
4008a9: f3 0f 6f 0c 07 movdqu xmm1,XMMWORD PTR [rdi+rax*1]
4008ae: 66 0f ef c1 pxor xmm0,xmm1
4008b2: f3 0f 7f 04 06 movdqu XMMWORD PTR [rsi+rax*1],xmm0
4008b7: 48 83 c0 10 add rax,0x10
4008bb: 45 39 c1 cmp r9d,r8d
4008be: 77 e0 ja 4008a0 <region_xor_w64+0x40>

正如@Mysticial 所指出的,上面的代码使用的是支持未对齐访问的指令。那些比较慢。然而,如果程序员可以正确地假设对齐访问,那么就有可能让编译器知道这一点。例如:

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];
}

编译器为上述 C 代码生成以下内容(注意 movdqa):

400880:       66 0f 6f 04 06          movdqa xmm0,XMMWORD PTR [rsi+rax*1]
400885: 41 83 c0 01 add r8d,0x1
400889: 66 0f ef 04 07 pxor xmm0,XMMWORD PTR [rdi+rax*1]
40088e: 66 0f 7f 04 06 movdqa XMMWORD PTR [rsi+rax*1],xmm0
400893: 48 83 c0 10 add rax,0x10
400897: 45 39 c1 cmp r9d,r8d
40089a: 77 e4 ja 400880 <region_xor_w64+0x20>

明天,当我给自己买一台配备 Haswell CPU 的笔记本电脑时,编译器将为我生成一个使用 256 位指令的代码,而不是同一代码中的 128 位指令,从而使 vector 性能提高两倍。即使我不知道 Haswell 有能力,它也会做到这一点。您不仅必须了解该功能,还必须编写代码的另一个版本并花一些时间对其进行测试。

顺便说一句,您的实现中似乎也存在一个错误,即代码可以跳过数据 vector 中最多 3 个剩余字节。

无论如何,我建议您相信您的编译器并学习如何验证生成的内容(即熟悉objdump)。下一个选择是更改编译器。然后才开始考虑手动编写 vector 处理指令。否则你会过得很糟糕!

希望有帮助。祝你好运!

关于c - 如何使用SIMD来加速两个内存块的异或?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32969901/

36 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com