十年或二十年前,这是值得写的数字代码,以避免使用倍增和分裂,并使用加法和减法的替代。一个很好的例子是使用 正向差异 评估的多项式的曲线,而不是计算多项式。

这仍然是这种情况下,或者有的现代计算机结构的先进到哪里*,/不再许多倍的速度比+,-?

具体来说,我很感兴趣,汇编C/C++上运行的代码现代化的典型x86芯片具有广泛板上浮点硬件、一个不小的微试图做FP在软件。我意识到管道和其他建筑增强功能排除特定的循环计数,但我仍然想获得一个有用的直觉。

有帮助吗?

解决方案

这也取决于指令混合。您的处理器将有多个计算单元随时待命,如果所有的人都被填充了所有的时间,你会得到最大的吞吐量。因此,执行MUL的的循环是一样快执行循环或添加 - 但如果表达式变得更加复杂,同样不持有

例如,借此循环:

for(int j=0;j<NUMITER;j++) {
  for(int i=1;i<NUMEL;i++) {
    bla += 2.1 + arr1[i] + arr2[i] + arr3[i] + arr4[i] ;
  }
}

有NUMITER = 10 ^ 7,NUMEL = 10 ^ 2,初始化为小的正数(NaN的是慢得多)两个阵列,这需要使用一个64位的PROC双打6.0秒。如果我与替换环

bla += 2.1 * arr1[i] + arr2[i] + arr3[i] * arr4[i] ;

只需1.7秒......所以,因为我们“过火”的补充,该MULS基本上免费的;在增加的减少帮助。它得到的更加混乱:

bla += 2.1 + arr1[i] * arr2[i] + arr3[i] * arr4[i] ;

- 相同MUL /添加分布,但现在的常数加入,而不是相乘 - 需要3.7秒。您的处理器很可能优化以更高效地执行典型数值计算;所以点积状MULS的金额和比例的款项是因为它得到一样好;添加常量是几乎没有通用的,所以这是更慢...

bla += someval + arr1[i] * arr2[i] + arr3[i] * arr4[i] ; /*someval == 2.1*/

再次花费1.7秒。

bla += someval + arr1[i] + arr2[i] + arr3[i] + arr4[i] ; /*someval == 2.1*/

(同初始循环,但无需昂贵的恒定添加:3.4秒)的

bla += someval * arr1[i] * arr2[i] * arr3[i] * arr4[i] ; /*someval == 2.1*/

(大多MULS,但是一个另外1.9秒)的

所以,基本上;很难说这是更快,但如果你希望避免的瓶颈,更重要的是要有一个清醒的搭配,避免NaN或INF,避免增加常量。不管你做什么,一定要进行测试,并且测试不同的编译器设置,因为经常微小变化可能只是赚取差价。

一些更多的情况下:

bla *= someval; // someval very near 1.0; takes 2.1 seconds
bla *= arr1[i] ;// arr1[i] all very near 1.0; takes 66(!) seconds
bla += someval + arr1[i] * arr2[i] + arr3[i] * arr4[i] ; // 1.6 seconds
bla += someval + arr1[i] * arr2[i] + arr3[i] * arr4[i] ; //32-bit mode, 2.2 seconds
bla += someval + arr1[i] * arr2[i] + arr3[i] * arr4[i] ; //32-bit mode, floats 2.2 seconds
bla += someval * arr1[i]* arr2[i];// 0.9 in x64, 1.6 in x86
bla += someval * arr1[i];// 0.55 in x64, 0.8 in x86
bla += arr1[i] * arr2[i];// 0.8 in x64, 0.8 in x86, 0.95 in CLR+x64, 0.8 in CLR+x86

其他提示

在理论的信息是在这里:

英特尔®64和32架构优化参考手册,附录C指令的延迟和吞吐量

每个处理器,他们名单,延迟FMUL是非常接近,FADD或FDIV.在一些较旧的处理器,FDIV是2至3时较慢,虽然在较新的处理器,这是相同的,因为FMUL.

注意事项:

  1. 该文件联我实际上说你可以不依靠这些数字在现实生活中,由于处理器将做什么,它想使事情更快,如果它是正确的。

  2. 有一个很好的机会,编译器将决定使用一个许多较新的指示设置的,有一个浮点乘/鸿沟。

  3. 这是一个复杂的文件只是读通过编译器的作家和我可能会得到它是错误的。就像我不清楚为什么FDIV延迟的数量完全是失踪的一些Cpu。

要回答这个问题的最好办法就是实际写你需要做的处理的基准/轮廓。实证应在理论被用来当过可能的。特别是当它容易获得。

如果你已经知道你需要做数学的不同实现,你可以写数学的几个不同的代码transfermations,看看你的表现峰。这将允许处理器/编译器产生不同的执行流,以填补处理器管线,给你一个具体的答案来回答。

如果你在具体的DIV / MUL / ADD / SUB型指令的性能,你可以甚至在某些联汇编抛专门控制时所执行的这些指令的变体的兴趣。但是,你需要确保你保持multilple执行单元忙来获得性能的系统能够一个好主意。

也做这样的事情会让您只需在其上运行同一程序来比较处理器的多种变体的性能,而且还可以让你在主板的差异因素。

编辑:

的基本+架构 - 是相同的。因此,他们在逻辑上采取相同的时间来计算。 *在另一方面,需要多个层,典型地构造出“全加器”来完成一个单一的操作。这garentees说,虽然*可以颁发给流水线每个周期将有比加/减电路较高的延迟。甲FP /操作是使用其迭代地朝着随时间的正确答案收敛的近似方法典型地实现。这些类型的近似值的经由乘法典型地实施。因此,对于浮点一般可以假设,因为这是不切实际的“展开”乘法除法将需要更长的时间(这已经是它的自我和大量电路)到乘法器电路众多的管道。仍然给定系统的性能最好通过试验测定的。

我无法找到一个明确的参考,但大量的实验告诉我,浮动乘法现在差不多是相同的速度,加减,而分工不(但不是“很多次”慢,要么)。你可以让你只运行自己的实验愿望的直觉 - 记得提前产生的随机数(百万人),阅读您开始计时前,并使用CPU的性能计数器(没有其他进程运行,如就像你可以阻止他们)进行精确测量!

的* / VS +的速度差 - 取决于你的处理器体系结构。特别是在一般的x86速度差已变得不那么有现代的处理器。 *应接近+,当有疑问:只是实验。如果你有大量的FP操作的一个很艰难的问题,也可以考虑使用你的GPU(的GeForce,...),它可以作为一个向量处理器。

有可能是乘法和加法之间的时间相差无几。在另一方面师是因为它的递归性质仍然显著慢然后乘法。 现代x86架构的SSE指令应该做浮点运算时,而不是使用fpu.Though好的C被视为/ C ++编译器应该给你使用的SSE而不是FPU的选择。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top