【问题标题】:Assuming a is double, is 2.0*a faster than 2*a?假设 a 是双倍的,2.0*a 是否比 2*a 快?
【发布时间】:2018-07-10 19:04:52
【问题描述】:

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

double a = ..;

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

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

我使用复杂的长表达式,如果适用的话,这些“.0”会在性能或可读性方面产生影响。

【问题讨论】:

  • 是什么阻止您测量和/或查看编译器输出? (而且 Fortran 不是 C++,所以轶事似乎无关紧要)
  • 编译器转换后的常数和最终结果是一样的。

标签: c++ performance


【解决方案1】:

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

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

绝对。

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

double a = (2 / someOtherNumber) * someFloat;  

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

  1. 准确地告诉代码的读者您的意图。
  2. 避免因整数除法而导致的错误。

【讨论】:

    【解决方案2】:

    原问题:

    让我们比较一下汇编输出。

    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
    

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

    【讨论】:

    • 我的观点不是非常量表达式,而是运算符重载的可能性。
    • 啊,好吧。我是这么想的,因为你所有的例子都是在双重上运行的。当然,如果你有一个带有运算符重载的类/结构,它也很重要。
    【解决方案3】:

    没有。

    以下代码:

    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++ 编译器都是如此——相信他们,他们非常聪明。

    【讨论】:

    • 该文本中的问题是Is it still beneficial to write 2.0 rather than 2 in the modern C++?。我不同意 No 在可读性和避免意外整数除法方面是该问题的答案。
    • @FantasticMrFox 我认为这个问题与性能有关,但我同意:使用正确的文字类型更清楚。
    【解决方案4】:

    不,它并没有更快。如果编译器知道该数字将是什么,为什么要等到运行时才将整数转换为浮点数?我想如果你完全禁用优化,你可能会说服一些非常迂腐的编译器这样做,但我所知道的所有编译器都会理所当然地进行优化。

    现在,如果您使用 a*ba 浮点类型和 b 整数类型,并且两者都不是编译时文字,那么在某些可能导致显着性能损失的架构上(特别是如果您最近计算了b)。但在文字的情况下,编译器已经支持你了。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-11-16
      • 2011-01-20
      • 2018-09-19
      • 1970-01-01
      • 2020-02-15
      • 2012-09-10
      • 1970-01-01
      相关资源
      最近更新 更多