加快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寄存器,速度要快得多。

一些问题:

  • 使用“加载,加载,添加,存储”结构还是“加载,加载到内存”帮助有用? 到目前为止,我的测试没有显示出任何优势。
  • 像往常一样,上证所(2,3,4)没有帮助吗?
  • 寻址(缩放索引加基数加偏移量)影响严重吗? 我可以使用ADD r11, 8代替。
  • 循环展开呢? 我阅读展开对桑迪桥架构(Agner Fog http://www.agner.org/optimize/)不利。 它是首选还是避免?
  • (编辑)我可以使用SSE寄存器从存储器加载和存储较大的块中的单词,并有效地与通用寄存器和SSE寄存器交换单词吗?
  • 我非常感谢任何评论。


    我非常确定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

    上一篇: Speed up x64 assembler ADD loop

    下一篇: How to read and write x86 flags registers directly?