加快x64汇编器ADD循环
我正在计算非常长整数(约100,000十进制数字)的乘法运算。 作为我的图书馆的一部分,我添加两个长号码。
分析表明,我的代码在add()和sub()例程中的运行时间高达25%,所以重要的是它们尽可能快。 但是我看不到太多的潜力。 也许你可以给我一些帮助,建议,见解或想法。 我会测试他们并回复你。
到目前为止,我的添加例程会进行一些设置,然后使用8倍展开的循环:
mov rax, QWORD PTR [rdx+r11*8-64]
mov r10, QWORD PTR [r8+r11*8-64]
adc rax, r10
mov QWORD PTR [rcx+r11*8-64], rax
随后有7个不同偏移量的块继续循环。
我尝试从早期的内存加载值,但没有帮助。 我想这是因为预取良好。 我使用英特尔i7-3770 Ivy Bridge 4核CPU。 但我想写一些适用于任何现代CPU的代码。
编辑 :我做了一些时间安排:它以2.25个周期/字增加了1k个单词。 如果我移除ADC,那么只剩下MOV,它仍然需要大约1.95个周期/字。 所以主要的瓶颈似乎是内存访问。 一个库memcpy()
工作在大约0.65个周期/字,但只有一个输入,而不是两个。 不过,我猜,由于使用了SSE寄存器,速度要快得多。
一些问题:
ADD r11, 8
代替。 我非常感谢任何评论。
我非常确定memcpy的速度更快,因为在执行下一个操作之前,它不依赖于要读取的数据。
如果你可以安排你的代码,使它像这样做:
mov rax, QWORD PTR [rdx+r11*8-64]
mov rbx, QWORD PTR [rdx+r11*8-56]
mov r10, QWORD PTR [r8+r11*8-64]
mov r12, QWORD PTR [r8+r11*8-56]
adc rax, r10
adc rbx, r12
mov QWORD PTR [rcx+r11*8-64], rax
mov QWORD PTR [rcx+r11*8-56], rbx
我不能100%确定-56的偏移量是否适合您的代码,但这个概念是“正确的”。
我也会考虑缓存命中/缓存冲突。 例如,如果您有三块数据[您似乎这么做],则确保它们不与缓存中的相同偏移对齐。 一个不好的例子是,如果你从缓存中的相同位置以缓存大小的倍数分配所有的块。 过度分配并确保你的不同数据块至少有512字节的偏移量[因此分配4K超大值,并舍入到4K边界起始地址,然后将512加到第二个缓冲区,将1024加到第三个缓冲区]
如果您的数据足够大(大于L2缓存),则可能需要使用MOVNT来获取/存储数据。 这样可以避免读入缓存 - 只有在数据量非常大的情况下,这样做才是有益的,下一次读取只会导致其他可能会发现“有用”的其他内容被踢出缓存,并且您不会获得在将它从缓存中踢出缓存之前回到它 - 因此将值存储在缓存中实际上并不会帮助...
编辑:使用SSE或类似将不会有帮助,如下所述:长整数例程可以从SSE中受益吗?
最困难的依赖是每个存储块之间的进位传播; 我会尝试首先设置一个处理这个问题的方法。
以下片段模拟进位传播,但带有不使用进位标志的“优点”。 对于三个或四个单独的线程,这可以并行化,每个线程产生一半相隔大约25000个十进制数字(或10000个字节)。 那么影响只有一个字节,字,双字等的那些载体的概率将渐近地达到零。
long long carry=0;
for (int i=0;i<N;i++) {
carry += (long long)*a++ + (long long)*b++;
*c++ = carry; carry>>=32;
}
根据我的分析,使用xmm的无附加加法需要大约550ms(1e9个字),模拟进位需要大约1020ms,而4路并行版本需要大约820ms(没有任何汇编优化)。
体系结构优化可能包括使用冗余数字系统,其中进位不必一直传播,进位评估可能几乎无限推迟。
尝试先预取数据(您可以尝试先读取更多数据块到x64寄存器,然后再进行计算),检查数据是否在内存中正确对齐,将循环代码放在标号对齐为16的位置,尝试删除SIB寻址
您也可以尝试缩短您的代码以:
mov rax, QWORD PTR [rdx+r11*8-64]
adc rax, QWORD PTR [r8+r11*8-64]
mov QWORD PTR [rcx+r11*8-64], rax
链接地址: http://www.djcxy.com/p/28777.html