优化x64汇编器MUL循环
我正在写数学代码,它需要快速乘以大数。 它分解为整数数组与单个整数的乘法运算。 在C ++中,它看起来像这样(在unsigned的):
void muladd(unsigned* r, const unsigned* a, unsigned len, unsigned b) {
unsigned __int64 of = 0; // overflow
unsigned i = 0; // loop variable
while (i < len) {
of += (unsigned __int64)a[i] * b + r[i];
r[i] = (unsigned)of;
of >>= 32;
++i;
}
r[i] = (unsigned)of; // save overflow
}
我手动展开了这个循环,将其转换为64位并在.asm编译器输出上工作以进一步优化它。 主.asm循环现在看起来像这样:
mov rax, rdi ; rdi = b
mul QWORD PTR [rbx+r10*8-64] ; rdx:rax = a[i] * b; r10 = i
mov rsi, QWORD PTR [r14+r10*8-64] ; r14 = r; rsi = r[i]
add rax, rsi
adc rdx, 0
add rax, r11 ; r11 = of (low part)
adc rdx, 0
mov QWORD PTR [r14+r10*8-64], rax ; save result
mov r11, rdx
; this repeats itself 8 times with different offsets
当我对此进行基准测试时,发现我的Core2 Quad上的每乘法平均需要大约6.3个周期。
我的问题是:我能以某种方式加快速度吗? 不幸的是,我看不到任何方法来避免其中的一个增加,并且乘法总是需要RDX:RAX,所以我需要移动数据并且不能排序“并行乘法”。
任何想法的人?
更新:经过一些更多的测试后,我设法将速度提高到每个64位MUL约5.4个周期(包括所有添加,移动和循环开销)。 我想这是关于Core2的最佳选择,因为Core2没有非常快速的MUL指令:吞吐量为3,延迟为6(7)个周期。 吞吐量为1,等待时间为3(4)个周期,桑迪桥将会更好。
关于GMP的少得多:我从他们的源代码中得到了这个数字,在我看来这是一个理论数字。 但可以肯定的是,这是一个计算AMD K9 CPU的数字。 从我读过的内容来看,AMD收集的MUL单元比(旧的)英特尔芯片更快。
我曾经写过一个看起来很像这样的循环,对很多数据进行最少量的处理,结果是循环受到内存速度的限制。
我会尝试预取[i]和r [i]
如果使用gcc,则在汇编器中使用函数__builtin_prefetch()或PREFETCHT0指令
http://gcc.gnu.org/onlinedocs/gcc-3.3.6/gcc/Other-Builtins.html
当这项工作结果可以是戏剧性的。 只要循环长达一千次左右,我就会预取[i + 64]和r [i + 64]作为起点,看看它在CPU上的差异。 您可能需要尝试更大的预取距离。
看起来你的例程可以从SSE中受益。 PMULLD和PADDD看起来像是相关的说明。 不知道为什么你的编译器不能从中产生SSE。
我只想指出,循环计数是毫无用处的,因为您的指令将被转换为微码,该微码将根据cpu正在执行的所有内容按顺序执行或暂停执行。 如果你有一个快速的例程,你可以尝试去除一个理论循环,除非你知道你的例程总是完全独立运行。
链接地址: http://www.djcxy.com/p/65005.html上一篇: Optimizing x64 assembler MUL loop
下一篇: NSPredicate for property of object in NSArray of NSArray