假设a是两倍,2.0 * a比2 * a快吗?

时间:2018-07-10 09:16:53

标签: c++ performance

很久以前,在有关古代FORTRAN的书中,我看到一种说法,即使用带有浮点变量的整数常量会比较慢,因为首先需要将常量转换为浮点形式:

double a = ..;

double b = a*2;   // 2 -> 2.0 first
double c = a*2.0; 

在现代C ++中编写2.0而不是2仍然有益吗?如果不是这样,可能应该首选“整数版本”,因为2.0更长,并且对人类读者没有任何影响。

我使用复杂的长表达式,其中这些“ .0”会影响性能或可读性(如果有的话)。

4 个答案:

答案 0 :(得分:4)

首先要涵盖其他答案,22.0不会造成性能差异,这将在编译时进行检查以创建正确的值。但是要回答这个问题:

  

在现代C ++中编写2.0而不是2仍然有益吗?

绝对。

但这不是因为性能,而是因为可读性和错误。想象一下以下操作:

double a = (2 / someOtherNumber) * someFloat;  

someOtherNumber是什么类型?因为如果它是integer类型,则由于整数除法会遇到麻烦。 2.02.0f具有明显的优势:

  1. 告诉代码的读者您想要的内容。
  2. 避免您不希望出现的整数除法错误。

答案 1 :(得分:3)

原始问题:

让我们比较程序集的输出。

double foo(double a)
{
        return a * 2;
}

double bar(double a)
{
        return a * 2.0f;   
}

double baz(double a)
{
        return a * 2.0;   
}

产生

0000000000000000 <foo>: //double x int
   0:   f2 0f 58 c0             addsd  %xmm0,%xmm0          // add with itself
   4:   c3                      retq                        // return (quad)
   5:   90                      nop                         // padding
   6:   66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1) // padding
   d:   00 00 00 

0000000000000010 <bar>: //double x float
  10:   f2 0f 58 c0             addsd  %xmm0,%xmm0          // add with itself
  14:   c3                      retq                        // return (quad)
  15:   90                      nop                         // padding
  16:   66 2e 0f 1f 84 00 00    nopw   %cs:0x0(%rax,%rax,1) // padding
  1d:   00 00 00 

0000000000000020 <baz>: //double x double
  20:   f2 0f 58 c0             addsd  %xmm0,%xmm0          // add with itself
  24:   c3                      retq                        // return (quad)

如您所见,它们都是相等的,根本不执行乘法。

即使在进行实数乘法(a*5)时,它们都相等,并且性能低至

0:  f2 0f 59 05 00 00 00    mulsd  0x0(%rip),%xmm0        # 8 <foo+0x8>
7:  00 
8:  c3                      retq   

添加:

@ Goswin-Von-Brederlow说,使用非常量表达式将导致不同的汇编。让我们像上面的例子一样测试它,但是要带有以下签名。

double foo(double a, int b); //int, float, double for foo/bar/baz

这将导致输出:

0000000000000000 <foo>: //double x int
   0:   66 0f ef c9             pxor   %xmm1,%xmm1  // clear xmm1
   4:   f2 0f 2a cf             cvtsi2sd %edi,%xmm1 // convert edi (second argument) to double
   8:   f2 0f 59 c1             mulsd  %xmm1,%xmm0  // mul xmm1 with xmm0
   c:   c3                      retq                // return
   d:   0f 1f 00                nopl   (%rax)       // padding

0000000000000010 <bar>: //double x float
  10:   f3 0f 5a c9             cvtss2sd %xmm1,%xmm1 // convert float to double
  14:   f2 0f 59 c1             mulsd  %xmm1,%xmm0   // mul
  18:   c3                      retq                 // return
  19:   0f 1f 80 00 00 00 00    nopl   0x0(%rax)     // padding

0000000000000020 <baz>: //double x double
  20:   f2 0f 59 c1             mulsd  %xmm1,%xmm0   // mul directly
  24:   c3                      retq                 // return

在这里您可以看到(运行时)从类型到双精度的转换,这当然会导致(运行时)开销。

答案 2 :(得分:2)

否。

以下代码:

double f1(double a) {
    double b = a*2;
    return b;
}

double f2(double a) {
    double c = a*2.0;
    return c;
}

...使用Clang在gcc.godbolt.org上编译时,会产生以下程序集:

f1(double): # @f1(double)
  addsd xmm0, xmm0
  ret
f2(double): # @f2(double)
  addsd xmm0, xmm0
  ret

您可以看到两个函数完全相同,并且编译器甚至用加法代替了乘法。我希望这个千年以来的所有C ++编译器都可以做到这一点-相信他们,它们非常聪明。

答案 3 :(得分:0)

不,不是更快。如果编译器知道该数字是多少,为什么还要等到运行时才将其转换为浮点数呢?我想,如果您完全禁用了优化功能,那么您可能会说服某些极其笨拙的编译器来执行此操作,但是我所知道的所有编译器理所当然都会进行该优化。

现在,如果您使用a*b的浮点类型和a的整数类型来做b,并且在某些可能导致会对性能产生重大影响(特别是如果您最近才计算b的话)但是就文字而言,编译器已经为您服务。