乘以零是一个特例吗?

时间:2014-09-17 17:49:41

标签: c++ performance visual-studio-2013 multiplication

我正在编写一个代码,其中优化是一个真正的问题,我真的需要尽快运行这段代码。

所以我有类似的事情:

a是double,x是int。 在我的具体情况下,x总是2或3

double c1[3] = {5345.4364654, 43346.6876978, 1224324.654756};
double c2[3] = {49876.642543678, 1104.57576756};
...
..
if (x>2)
   a = c1[0]*x*x*x-c1[1]*x*x+c1[2]*x;
else
   a = c2[0]*x*x+c2[1]*x;

我想知道如果删除if..else并执行类似

之类的操作会更快
double c [2][3] = { 
                      {5345.4364654, 43346.6876978 , 1224324.654756},
                      {0           ,49876.642543678, 1104.575767561}
                     };

a = c1[x-2][0]*x*x*x-c1[x-2][1]*x*x+c1[x-2][2]*x;

它告诉我,如果将乘以零被视为"特殊情况"那么第二个代码将运行得更快由编译器,但我不确定

如果你们有任何人有想法请帮助:) 非常感谢

5 个答案:

答案 0 :(得分:2)

我实际上是用你的第一张表格,但试着让它变得更容易。

  

a是double,x是int。在我的具体情况下,x总是0或1,但我不能将它作为布尔值(true / false),它基于int!

我们可以做很多事情,因为我们知道它只能是0或1。

  1. 我们可以摆脱整个c2的情况,因为乘以0总是会导致0。

  2. 如果x为1乘以x,则不会改变任何内容。将y乘以1始终返回y。

  3. 现在我们得到类似的东西:

    double c1[3] = {5345.4364654, 43346.6876978, 1224324.654756};
    a = (c1[0]-c1[1]+c1[2]) * x;
    

答案 1 :(得分:2)

现在乘法非常快,所以避免分支可能比将一个乘法跳过0更重要。

我将下面的等式分解为:

double c [2][3] = { 
                      {5345.4364654, 43346.6876978 , 1224324.654756},
                      {0           ,49876.642543678, 1104.575767561}
                     };

a = ((c1[x-2][0]*x-c1[x-2][1])*x+c1[x-2][2])*x;

注意,如果数组c确实是一个编译时常量(如果你通过使它成为const来帮助编译器),你可能会提示编译器预先计算x = 2和x的值= 3喜欢这样:

switch (x)
{
  case 2:
    a = ((c1[x-2][0]*x-c1[x-2][1])*x+c1[x-2][2])*x;
    break;
  case 3:
    a = ((c1[x-2][0]*x-c1[x-2][1])*x+c1[x-2][2])*x;
    break;
  default:
    // this should never happen
}

这可能会使编译器直接编译答案。

这很可能也可以起作用:

switch (x)
{
  case 2:
  case 3:
    a = ((c1[x-2][0]*x-c1[x-2][1])*x+c1[x-2][2])*x;
    break;
  default:
    // this should never happen
}

但是,如果c确实是一个编译时常量且x实际上是2或3,那么你只有两个输入和两个结果,所以你可能只计算出计算器上的两个答案如果你真的担心速度,可以在switch语句中对它们进行硬编码。

答案 2 :(得分:1)

如果x完全等于0,那么a也是0,它只是一个多项式。如果你真的想要,你可以做一个额外的案例

else if (x == 0)
    a = 0;

但是你很可能看不到速度的可观察性增加。这闻起来像是不必要的微观优化。

答案 3 :(得分:1)

避免跳转通常对性能有好处,因为每次条件跳转都会干扰CPU管道。最好做一些额外的(易于并行化的)线性计算。

答案 4 :(得分:1)

您可以提前预先计算一些值。 给定

  

a = c1 [x-2] [0] * x * x * x-c1 [x-2] [1] * x * x + c1 [x-2] [2] * x;

表达式x-2使用了3次。

如果x为2或3,则应将它们插入等式并简化:

switch (x)
{
  case 2:
    a = c1[0][0]*8 - c1[0][1] * 4 + c1[0][2]* 2;
    break;
  case 3:
    a = c1[1][0]*27 - c1[1][1] * 9 + c1[1][2] * 3;
    break;
}

虽然您可以先获得更多性能,然后再添加:

  const double term1 = c1[x-2][0]*x*x*x;
  const double term2 = c1[x-2][1]*x*x;
  const double term3 = c1[x-2][2]*x;
  a = term1 + term2 + term;

将上面的内容与一些预取相结合,可能会获得更好的性能:

  // Prefetch
  const double v0 = c1[x-2][0];
  const double v1 = c1[x-2][1];
  const double v2 = c1[x-2][2];

  // Mulitply
  const double t1 = v0 * x * x * x;
  const double t2 = v1 * x * x;
  const double t3 = v2 * x;

  // Sum
  a = t1 + t2 + t3;

最好的方法是编写代码并查看汇编语言。 也不是const的使用。 const的使用将有助于编译器执行更好的优化。