【问题标题】:Multiplying is faster than branching乘法比分支快
【发布时间】:2013-07-02 23:02:15
【问题描述】:

为了了解 if 语句与选择性乘法,我尝试了下面的代码,发现将结果乘以 0 而不是 failed-if-statement(false) 并乘以 1 而不是 pass-if-statement (true),如果只有 3-4 个双精度乘法,if 语句会更慢,而仅计算总是更快。

问题:虽然这种乘法即使在 cpu 上也更快,但它在 GPU(opencl/cuda) 上的表现如何?我的投票是绝对加速。单精度乘法的精度损失如何?我知道不可能总是有 1.00000,乘以 0.999999。 假设我不介意第 5 位的 sp 精度损失。

这更适合整数,但至少对浮点数有意义吗? 如果浮点数/半数比双精度数更快/更快,那么这将更快。

结果:

 no if: 0.058515741 seconds
 if(){}: 0.073415743 seconds

谁能重现类似的结果? if(){} 是第二个测试,所以 JIT 不可能作弊?

代码:

 public static void main(String[] args)
{
       boolean[]ifBool=new boolean[10000000];
       byte[]ifThen=new byte[10000000];
       double []data=new double[10000000];
       double []data1=new double[10000000];
       double []data2=new double[10000000];

       for(int i=0;i<ifThen.length;i++)
       {
          ifThen[i]=(byte)(0.43+Math.random()); //1 =yes result add, 0= no result add 
          ifBool[i]=(ifThen[i]==1?true:false);
          data[i]=Math.random();
          data1[i]=Math.random();
          data2[i]=Math.random();
      }

         long ref=0,end=0;
         ref=System.nanoTime();
         for(int i=0;i<data.length;i++)
         {
                // multiplying by zero means no change in data
                // multiplying by one means a change in data
            double check=(double)ifThen[i]; // some precision error 0.99999 ?
            data2[i]+=(data[i]*data1[i])*check; // double checked to be sure
            data[i]+=(data2[i]*data1[i])*check; // about adding the result
            data1[i]+=(data[i]*data2[i])*check; // or not adding
                                       //(adding the result or adding a zero)

         }
         end=System.nanoTime();
         System.out.println("no if: "+(end-ref)/1000000000.0+" seconds");

         ref=System.nanoTime();
         for(int i=0;i<data.length;i++)
         {
            if(ifBool[i]) // conventional approach, easy to read
            {
               data2[i]+=data[i]*data1[i];
               data[i]+=data2[i]*data1[i];
               data1[i]+=data[i]*data2[i];
            }
         }
         end=System.nanoTime();
         System.out.println("if(){}: "+(end-ref)/1000000000.0+" seconds");
}

CPU 是 FX8150 @ 4GHz

【问题讨论】:

  • 在流水线 cpu 上,一个分支可能非常昂贵,而且多重往往是高度优化的。所以我对此并不感到惊讶。
  • 任何编译器/解释器都可以自动执行整数乘法(并在之后添加)?
  • 对此有几个 cmets: 1. 您应该增加迭代次数/时间以使其成为有意义的测试(也许在您的循环中添加一个外部循环以多次循环遍历数据) 2.您正在更改测试 1 中的数据值,然后在测试 2 中使用新值。理想情况下,您应该为两个测试使用完全相同的数据(尽管我不认为它会对测试产生很大影响)。跨度>
  • 照你说的做,结果一样。即使交换循环位置也不会改变结果。重复循环也给出了。
  • 像这样的 Java 微基准测试非常很难正确执行。我建议您阅读this SO question and the accepted answer 并重新考虑您的工作方式。

标签: java optimization cpu gpgpu branch-prediction


【解决方案1】:

无法重现您的结果(仅限 CPU)。

原代码: 否 如果:0.11589088 秒。 if(){}:0.115732277 秒。

逆序: if(){}:0.1154809 秒。 否 如果:0.115531714 秒。

多次运行产生不同的结果,但 if/no_if 块实际上是相同的。

您需要更详细的基准来得出一些有意义的结论。使用热身、稳定的随机种子、多次调用的平均值。

我也可能(几乎)对微管理 Java 代码毫无用处。它仅适用于特定硬件和特定 VM 版本。 这些天来,VM 代码优化是如此先进,你不会相信它可以做什么。确保执行的代码与你的字节码有很大的不同。

【讨论】:

    猜你喜欢
    • 2011-06-30
    • 1970-01-01
    • 1970-01-01
    • 2012-02-10
    • 2017-08-15
    • 2013-07-26
    • 1970-01-01
    • 2011-04-30
    • 1970-01-01
    相关资源
    最近更新 更多