【问题标题】:Do processors actually calculate multiplication by a zero or one? Why?处理器实际上计算乘以零还是一?为什么?
【发布时间】:2013-07-06 09:30:47
【问题描述】:

简短版

在下面一行:

aData[i] = aData[i] + ( aOn * sin( i ) );

如果aOn01,处理器是实际执行乘法运算,还是有条件地计算结果(0 对应0,其他值对应1)?

长版

我正在研究算法性能一致性,其中部分涉及到Branch Prediction 的效果。

假设是这段代码:

for ( i = 0; i < iNumSamples; i++ )
    aData[i] = aData[i] + ( aOn * sin( i ) );

将提供比此代码更稳定的性能(分支预测可能会破坏性能):

for ( i = 0; i < iNumSamples; i++ )
{
    if ( aOn )
        aData[i] = aData[i] + sin( i );
}

aOn01,它可以在另一个线程执行循环期间切换。

实际的条件计算(上例中的+ sin( i ))涉及到更多的处理,并且if条件必须在循环内(有多种条件,不仅仅是上例中的一个;另外,更改为@987654335 @ 应该立即生效,而不是每个循环)。

忽略性能一致性,两个选项之间的性能权衡在于执行 if 语句和乘法的时间。

无论如何,很容易发现,如果处理器不会对 10 等值执行实际乘法,则第一个选项可能是双赢的解决方案(无分支预测,性能更好)。

【问题讨论】:

  • 编译器很可能会将这些优化掉。
  • 我检查过优化和不优化,这对01 和任何其他数字的乘数之间的相对性能没有影响(尽管优化确实在所有情况下都会略微提高性能) .那么优化究竟与处理器是否进行乘法相关?
  • @iamnotmaynard 除非 aOn 是常量,否则编译器无法优化它,但 OP 没有给出任何指示。

标签: c++ c performance algorithm processors


【解决方案1】:

处理器与0s 和1s 执行正则乘法。

原因是,如果处理器在每次计算之前检查01,则条件的引入将花费更多的周期。虽然您将获得 01 乘数的性能,但您将失去任何其他值的性能(更有可能)。

一个简单的程序可以证明这一点:

#include <iostream>
#include "cycle.h"
#include "time.h"

void Loop( float aCoefficient )
{
    float iSum = 0.0f;

    clock_t iStart, iEnd;

    iStart = clock();
    for ( int i = 0; i < 100000000; i++ )
    {
        iSum += aCoefficient * rand();
    }
    iEnd = clock();
    printf("Coefficient: %f: %li clock ticks\n", aCoefficient, iEnd - iStart );
}

int main(int argc, const char * argv[])
{
    Loop( 0.0f );
    Loop( 1.0f );
    Loop( 0.25f );

    return 0;
}

输出是:

Coefficient: 0.000000: 1380620 clock ticks
Coefficient: 1.000000: 1375345 clock ticks
Coefficient: 0.250000: 1374483 clock ticks 

【讨论】:

  • 您使用了什么优化级别,什么编译器,您是否尝试过打印结果 iSum - 更重要的是,可能使用的内容比 rand() 少一些,在它本身做了一堆相当复杂的数学运算并且可能隐藏了结果(并且由于编译器必须调用rand(),因为rand() 有副作用 - 它会修改内部状态),无论如何它都无法优化它.
  • 好点。无论优化级别如何,相对性能保持不变(从无到-Os);编译器是 LLVM C++;没有rand() 需要打印iSum 以防止优化器完全跳过循环;尝试不使用 rand() 的变化,例如用 sin( i ) 替换它仍然会为所有 3 个乘数产生相似的结果。
  • 我认为可能会出现编译器将 0.0 或 1.0 预先计算为“不要相乘”的情况,但同时,只有在编译器可以看到该值的情况下才有可能的系数作为常数。并且根据处理器的类型 [以及运行期间值的可预测性 - 换句话说,它的变化频率],使用 if 与普通乘法相比可能更好,也可能不会更好。 ...继续...
  • 如果第二个线程正在修改aOn,从技术上讲,它应该受到保护,这样两个线程就不能同时更新和读取。在实践中并且除非精确检测onoff 很重要,并且处理器是x86(或选择的其他类型),否则可能不这样做。
  • 实际实现的细节涉及GUI线程(即用户)在另一个线程进行数据处理时更改aOn。循环实际上一次又一次地执行了几个小时。 onoff 的精确检测在单个循环迭代规模上并不重要(即,只在 i+1 处更新是可以的——毕竟触发更改的是高度不精确的 GUI 线程)。所以我看不出为什么需要读/更新锁。但更改必须在循环内而不是每个循环内生效。
猜你喜欢
  • 2014-12-09
  • 1970-01-01
  • 2021-03-05
  • 1970-01-01
  • 1970-01-01
  • 2012-04-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多