使用powx,2代替x * x和x double有什么好处吗?

问题描述

| 使用此代码有什么好处
double x;
double square = pow(x,2);
而不是这个?
double x;
double square = x*x;
我更喜欢x * x并查看我的实现(Microsoft),在pow中我没有发现任何优势,因为对于特定的方形情况,x * x比pow更简单。 在某些情况下战俘是优越的吗?     

解决方法

        FWIW,在MacOS X 10.6上带有gcc-4.2和
-O3
编译器标志,
x = x * x;
y = pow(y,2);
产生相同的汇编代码:
#include <cmath>

void test(double& x,double& y) {
        x = x * x;
        y = pow(y,2);
}
组装至:
    pushq   %rbp
    movq    %rsp,%rbp
    movsd   (%rdi),%xmm0
    mulsd   %xmm0,%xmm0
    movsd   %xmm0,(%rdi)
    movsd   (%rsi),(%rsi)
    leave
    ret
因此,只要您使用的是体面的编译器,就对您的应用程序编写更有意义的代码,但请注意,
pow(x,2)
永远不会比普通乘法更好。     ,        如果您的意思是x²,std :: pow更具表现力,如果您的意思是xx则xx更具表达力,尤其是在您只是编码例如科学论文,读者应该能够理解您的实现与论文。对于x * x /x²来说,差别可能很小,但是我认为,如果通常使用命名函数,它会提高代码的可扩展性和可读性。 在现代编译器上,例如g ++ 4.x,即使不是编译器内置的,也将内联std :: pow(x,2),并将其强度降低为x * x。如果默认情况下不是这样,并且您不关心IEEE浮点型一致性,请查看编译器手册中的快速数学开关(g ++ == -ffast-math)。 旁注:提到过包括math.h会增加程序大小。我的回答是:   在C ++中,您是
#include <cmath>
,而不是math.h。同样,如果您的编译器不是老旧的,它只会通过您使用的程序来增加程序的大小(一般情况下),并且如果您对std :: pow的使用只是内联到相应的x87指令和现代g ++将用x * x降低x²的强度,则没有相关的尺寸增加。另外,程序的大小永远都不能决定代码的表现力。 cmath与math.h相比的另一个优点是,使用cmath时,每种浮点类型都将获得std :: pow重载,而使用math.h时,您将在全局命名空间中获得pow,powf等,因此cmath可提高适应性代码,尤其是在编写模板时。 一般规则:优先考虑可表达且清晰的代码,而不要考虑可疑的性能和二进制大小的合理代码。 另请参阅Knuth:   “我们应该忘记效率低下,大约有97%的时间是这样:过早的优化是万恶之源” 和杰克逊:   程序优化的第一条规则:不要这样做。程序优化的第二条规则(仅适用于专家!):暂时不要做。     ,        ѭ9不仅更清晰,而且肯定至少和ѭ10一样快。     ,这个问题触及了关于科学编程的大多数C和C ++实现的关键弱点之一。从Fortran切换到C大约20年,后来又切换到C ++之后,这仍然是一个痛苦的地方之一,有时使我怀疑是否进行切换是一件好事。 简而言之,这个问题是: 实施
pow
的最简单方法是
Type pow(Type x; Type y) {return exp(y*log(x));}
大多数C和C ++编译器都采取了简单的方法。 有些人可能会“做正确的事”,但只能在高优化级别上进行。 与
x*x
相比,使用
pow(x,2)
的简便方法在计算上非常昂贵,并且会失去精度。 与旨在科学编程的语言相比: 你不写ѭ15。这些语言具有内置的幂运算符。 C和C ++一直坚决拒绝实现幂运算符,这使许多科学程序员的血液都沸腾了。对于一些顽固的Fortran程序员而言,仅此一个原因就永远不要切换到C。 对于所有小的整数幂,要求Fortran(和其他语言)“做正确的事”,其中small是-12到12之间的任何整数。(如果编译器不能做到,则编译器不兼容)正确的事情。)此外,还要求他们关闭优化功能。 许多Fortran编译器还知道如何在不求助于简单方法的情况下提取一些有理根源。 依靠高优化级别来“做正确的事”是一个问题。我曾在多家禁止在安全关键软件中使用优化的组织工作过。在这里损失1000万美元,在这里损失1亿美元之后,内存可能会很长(数十年之久),这都是由于某些优化编译器中的错误所致。 恕我直言,不要在C或C ++中使用ѭ10。我并不孤单。确实使用ѭ10的程序员通常会在代码审查期间花费大量时间。     ,        在C ++ 11中,在一种情况下,使用
x * x
优于
std::pow(x,2)
,并且需要在constexpr中使用它:
constexpr double  mySqr( double x )
{
      return x * x ;
}
如我们所见,std :: pow未标记为constexpr,因此在constexpr函数中不可用。 否则,从性能角度来看,将以下代码放入Godbolt中可显示以下功能:
#include <cmath>

double  mySqr( double x )
{
      return x * x ;
}

double  mySqr2( double x )
{
      return std::pow( x,2.0 );
}
生成相同的程序集:
mySqr(double):
    mulsd   %xmm0,%xmm0    # x,D.4289
    ret
mySqr2(double):
    mulsd   %xmm0,D.4292
    ret
我们应该从任何现代编译器中获得相似的结果。 值得一提的是,目前gcc认为pow是constexpr,在此也进行了介绍,但这是不合格的扩展,不应依赖它,并且可能会在later23ѭ的更高版本中发生更改。     ,        
x * x
将始终编译为简单的乘法。
pow(x,2)
可能会(但绝对不能保证)被优化到相同的水平。如果未对它进行优化,则可能使用了缓慢的通用上电数学例程。因此,如果您关心性能,则应始终偏爱
x * x
。     ,        恕我直言: 代码可读性 代码健壮性-更容易更改为
pow(x,6)
,也许实现了针对特定处理器的某种浮点机制,等等。 性能-如果有一种更智能,更快速的方法来计算此结果(使用汇编器或某种特殊技巧),pow会做到这一点。你不会.. :) 干杯     ,        我可能会选择
std::pow(x,2)
,因为它可以使我的代码重构更加容易。一旦优化了代码,它就不会有任何区别。 现在,这两种方法并不相同。这是我的测试代码:
#include<cmath>

double square_explicit(double x) {
  asm(\"### Square Explicit\");
  return x * x;
}

double square_library(double x) {
  asm(\"### Square Library\");  
  return std::pow(x,2);
}
asm(\"text\");
调用只是将注释写入到程序集输出中,这是我使用(OS X 10.7.4上的GCC 4.8.1)生成的:
g++ example.cpp -c -S -std=c++11 -O[0,1,2,or 3]
您不需要
-std=c++11
,我总是会用它。 第一:调试时(零优化),生成的程序集是不同的;这是相关的部分:
# 4 \"square.cpp\" 1
    ### Square Explicit
# 0 \"\" 2
    movq    -8(%rbp),%rax
    movd    %rax,%xmm1
    mulsd   -8(%rbp),%xmm1
    movd    %xmm1,%xmm0
    popq    %rbp
LCFI2:
    ret
LFE236:
    .section __TEXT,__textcoal_nt,coalesced,pure_instructions
    .globl __ZSt3powIdiEN9__gnu_cxx11__promote_2IT_T0_NS0_9__promoteIS2_XsrSt12__is_integerIS2_E7__valueEE6__typeENS4_IS3_XsrS5_IS3_E7__valueEE6__typeEE6__typeES2_S3_
    .weak_definition __ZSt3powIdiEN9__gnu_cxx11__promote_2IT_T0_NS0_9__promoteIS2_XsrSt12__is_integerIS2_E7__valueEE6__typeENS4_IS3_XsrS5_IS3_E7__valueEE6__typeEE6__typeES2_S3_
__ZSt3powIdiEN9__gnu_cxx11__promote_2IT_T0_NS0_9__promoteIS2_XsrSt12__is_integerIS2_E7__valueEE6__typeENS4_IS3_XsrS5_IS3_E7__valueEE6__typeEE6__typeES2_S3_:
LFB238:
    pushq   %rbp
LCFI3:
    movq    %rsp,%rbp
LCFI4:
    subq    $16,%rsp
    movsd   %xmm0,-8(%rbp)
    movl    %edi,-12(%rbp)
    cvtsi2sd    -12(%rbp),%xmm2
    movd    %xmm2,%rax
    movq    -8(%rbp),%rdx
    movd    %rax,%xmm1
    movd    %rdx,%xmm0
    call    _pow
    movd    %xmm0,%xmm0
    leave
LCFI5:
    ret
LFE238:
    .text
    .globl __Z14square_libraryd
__Z14square_libraryd:
LFB237:
    pushq   %rbp
LCFI6:
    movq    %rsp,%rbp
LCFI7:
    subq    $16,-8(%rbp)
# 9 \"square.cpp\" 1
    ### Square Library
# 0 \"\" 2
    movq    -8(%rbp),%rax
    movl    $2,%edi
    movd    %rax,%xmm0
    call    __ZSt3powIdiEN9__gnu_cxx11__promote_2IT_T0_NS0_9__promoteIS2_XsrSt12__is_integerIS2_E7__valueEE6__typeENS4_IS3_XsrS5_IS3_E7__valueEE6__typeEE6__typeES2_S3_
    movd    %xmm0,%xmm0
    leave
LCFI8:
    ret
但是,当您生成优化的代码时(即使在GCC的最低优化级别,也就是
-O1
),代码都是相同的:
# 4 \"square.cpp\" 1
    ### Square Explicit
# 0 \"\" 2
    mulsd   %xmm0,%xmm0
    ret
LFE236:
    .globl __Z14square_libraryd
__Z14square_libraryd:
LFB237:
# 9 \"square.cpp\" 1
    ### Square Library
# 0 \"\" 2
    mulsd   %xmm0,%xmm0
    ret
因此,除非您关心未优化的代码的速度,否则它实际上没有任何区别。 就像我说的:在我看来,“ 28”可以更清楚地传达您的意图,但这只是优先考虑的问题,而不是表现。 对于更复杂的表达式,优化似乎也适用。举个例子:
double explicit_harder(double x) {
  asm(\"### Explicit,harder\");
  return x * x - std::sin(x) * std::sin(x) / (1 - std::tan(x) * std::tan(x));
}

double implicit_harder(double x) {
  asm(\"### Library,harder\");
  return std::pow(x,2) - std::pow(std::sin(x),2) / (1 - std::pow(std::tan(x),2));
}
同样,使用
-O1
(最低的优化),程序集再次相同:
# 14 \"square.cpp\" 1
    ### Explicit,harder
# 0 \"\" 2
    call    _sin
    movd    %xmm0,%rbp
    movd    %rbx,%xmm0
    call    _tan
    movd    %rbx,%xmm3
    mulsd   %xmm3,%xmm3
    movd    %rbp,%xmm1
    mulsd   %xmm1,%xmm1
    mulsd   %xmm0,%xmm0
    movsd   LC0(%rip),%xmm2
    subsd   %xmm0,%xmm2
    divsd   %xmm2,%xmm1
    subsd   %xmm1,%xmm3
    movapd  %xmm3,%xmm0
    addq    $8,%rsp
LCFI3:
    popq    %rbx
LCFI4:
    popq    %rbp
LCFI5:
    ret
LFE239:
    .globl __Z15implicit_harderd
__Z15implicit_harderd:
LFB240:
    pushq   %rbp
LCFI6:
    pushq   %rbx
LCFI7:
    subq    $8,%rsp
LCFI8:
    movd    %xmm0,%rbx
# 19 \"square.cpp\" 1
    ### Library,%rsp
LCFI9:
    popq    %rbx
LCFI10:
    popq    %rbp
LCFI11:
    ret
最后:ѭ18方法不需要
include
42ѭ,这将使您的编译在其他所有条件相同的情况下都稍快一些。