Performance 我应该用乘法还是除法?

Performance 我应该用乘法还是除法?,performance,programming-languages,Performance,Programming Languages,这里有一个愚蠢有趣的问题: 假设我们必须执行一个简单的操作,其中需要变量值的一半。通常有两种方法: y = x / 2.0; // or... y = x * 0.5; 假设我们使用的是该语言提供的标准运算符,那么哪一个具有更好的性能 我猜乘法通常更好,所以我试着在编写代码时坚持这一点,但我想确认一下这一点 虽然我个人对Python的答案感兴趣,但是也可以发布其他语言的答案!如果您愿意,也可以发布其他更有趣的方法(如使用位移位运算符)。我一直认为乘法更有效。如果您使用整数或非浮点类型,请不要忘

这里有一个愚蠢有趣的问题:

假设我们必须执行一个简单的操作,其中需要变量值的一半。通常有两种方法:

y = x / 2.0;
// or...
y = x * 0.5;
假设我们使用的是该语言提供的标准运算符,那么哪一个具有更好的性能

我猜乘法通常更好,所以我试着在编写代码时坚持这一点,但我想确认一下这一点


虽然我个人对Python的答案感兴趣,但是也可以发布其他语言的答案!如果您愿意,也可以发布其他更有趣的方法(如使用位移位运算符)。

我一直认为乘法更有效。

如果您使用整数或非浮点类型,请不要忘记位移位运算符:>

inty=10;
y=y>>1;
Console.WriteLine(“值减半:+y”);

y=y乘法通常更快——当然永远不会慢。
但是,如果它不是速度关键型的,那么就编写最清晰的代码。

我认为这太挑剔了,所以最好是做任何能让代码更具可读性的事情。除非你进行数千次,甚至数百万次的手术,否则我怀疑任何人都不会注意到其中的差别


如果你真的必须做出选择,基准测试是唯一的选择。找出哪些函数给您带来了问题,然后找出函数中出现问题的地方,并修复这些部分。然而,我仍然怀疑一个单一的数学运算(即使是一个多次重复的运算)是否会成为任何瓶颈的原因。

我在某个地方读到,乘法在C/C++中更有效;不知道解释语言-由于所有其他开销,差异可能可以忽略不计


除非它成为一个问题,否则坚持什么更易于维护/可读-我讨厌人们告诉我这一点,但这是真的。

好吧,如果我们假设一个加法/子轨操作花费1,那么乘法花费5,除法花费大约20。

乘法更快,除法更准确。如果您的数字不是2的幂,您将失去一些精度:

y = x / 3.0;
y = x * 0.333333;  // how many 3's should there be, and how will the compiler round?
即使你让编译器精确地计算出倒数常量,答案仍然是不同的

x = 100.0;
x / 3.0 == x * (1.0/3.0)  // is false in the test I just performed

速度问题只可能在C/C++或JIT语言中起作用,甚至只有在操作处于瓶颈的循环中时才会起作用。

做任何需要的事情。首先考虑你的读者,不要担心性能,直到你确定你有性能问题


让编译器来为您实现性能。

浮点除法(通常)特别慢,所以虽然浮点乘法也相对慢,但它可能比浮点除法快


但我更倾向于回答“这并不重要”,除非分析表明除法和乘法相比有点瓶颈。不过,我猜乘法与除法的选择不会对应用程序的性能产生太大影响。

我建议一般使用乘法,因为您不必花费大量时间来确保除数不是0。当然,如果除数是一个常数,则这不适用。

Python:

time python -c 'for i in xrange(int(1e8)): t=12341234234.234 / 2.0'
real    0m26.676s
user    0m25.154s
sys     0m0.076s

time python -c 'for i in xrange(int(1e8)): t=12341234234.234 * 0.5'
real    0m17.932s
user    0m16.481s
sys     0m0.048s
乘法快33%

卢阿:

=>没有真正的区别

卢阿吉特:

time luajit -O -e 'for i=1,1e8 do t=12341234234.234 / 2.0 end'
real    0m1.921s
user    0m1.668s
sys     0m0.004s

time luajit -O -e 'for i=1,1e8 do t=12341234234.234 * 0.5 end'
real    0m1.843s
user    0m1.676s
sys     0m0.000s
=>只快了5%


结论:在Python中,乘法比除法更快,但随着使用更高级的VM或JIT接近CPU,优势就消失了。很有可能未来的Python虚拟机会使它变得无关紧要

编写更清楚说明您意图的内容

在你的程序运行后,找出什么是慢的,并使之更快


不要反过来做。

始终使用最清晰的内容。您所做的任何其他事情都是试图超越编译器。如果编译器是智能的,它将尽最大努力优化结果,但是没有什么能让下一个家伙不恨你的蹩脚的位移位解决方案(顺便说一句,我喜欢位操作,它很有趣。但是有趣!=可读)

过早优化是万恶之源。永远记住优化的三条规则

  • 不要优化
  • 如果你是专家,请参见规则1
  • 如果您是专家并且能够证明需要,请使用以下程序:

    • 将其编码为未优化
    • 确定“足够快”的速度——注意哪个用户需求/故事需要该度量
    • 写一个速度测试
    • 测试现有代码——如果足够快,就完成了
    • 重新编码它
    • 测试优化代码。如果它不符合标准,扔掉它,保留原来的
    • 如果它满足测试要求,则保留原始代码作为注释

  • 此外,在不需要内部循环时删除它们,或者在数组上选择链表进行插入排序,这些都不是优化,仅仅是编程。

    当你在汇编或C语言中编程时,这就成了一个更大的问题。我认为大多数现代语言都在为我进行优化。

    小心“猜测乘法通常更好,所以我在编写代码时尽量坚持这一点。”

    在这个具体问题的背景下,这里的“更好”意味着“更快”。这不是很有用

    考虑速度可能是一个严重的错误。在计算的特定代数形式中有着深刻的错误含义

    看。看

    虽然有些浮点值是精确的,但大多数浮点值是近似值;它们是一些理想值加上一些误差。每个操作都适用于理想值和误差值

    最大的问题来自试图操纵两个几乎相等的数字。最右边的位(错误位
    time lua -e 'for i=1,1e8 do t=12341234234.234 / 2.0 end'
    real    0m7.956s
    user    0m7.332s
    sys     0m0.032s
    
    time lua -e 'for i=1,1e8 do t=12341234234.234 * 0.5 end'
    real    0m7.997s
    user    0m7.516s
    sys     0m0.036s
    
    time luajit -O -e 'for i=1,1e8 do t=12341234234.234 / 2.0 end'
    real    0m1.921s
    user    0m1.668s
    sys     0m0.004s
    
    time luajit -O -e 'for i=1,1e8 do t=12341234234.234 * 0.5 end'
    real    0m1.843s
    user    0m1.676s
    sys     0m0.000s
    
    >>> for i in range(7):
    ...     a=1/(10.0**i)
    ...     b=(1/10.0)**i
    ...     print i, a, b, a-b
    ... 
    0 1.0 1.0 0.0
    1 0.1 0.1 0.0
    2 0.01 0.01 -1.73472347598e-18
    3 0.001 0.001 -2.16840434497e-19
    4 0.0001 0.0001 -1.35525271561e-20
    5 1e-05 1e-05 -1.69406589451e-21
    6 1e-06 1e-06 -4.23516473627e-22
    
    y = x * (1.0 / 2.0);
    
    int main() {
    
        volatile int a;
        volatile int b;
    
        asm("## 5/2\n");
        a = 5;
        a = a / 2;
    
        asm("## 5*0.5");
        b = 5;
        b = b * 0.5;
    
        asm("## done");
    
        return a + b;
    
    }
    
    movl    $5, -4(%ebp)
    movl    -4(%ebp), %eax
    movl    %eax, %edx
    shrl    $31, %edx
    addl    %edx, %eax
    sarl    %eax
    movl    %eax, -4(%ebp)
    
    movl    $5, -8(%ebp)
    movl    -8(%ebp), %eax
    pushl   %eax
    fildl   (%esp)
    leal    4(%esp), %esp
    fmuls   LC0
    fnstcw  -10(%ebp)
    movzwl  -10(%ebp), %eax
    orw $3072, %ax
    movw    %ax, -12(%ebp)
    fldcw   -12(%ebp)
    fistpl  -16(%ebp)
    fldcw   -10(%ebp)
    movl    -16(%ebp), %eax
    movl    %eax, -8(%ebp)
    
    flds    LC0
    fstl    -8(%ebp)
    fldl    -8(%ebp)
    flds    LC1
    fmul    %st, %st(1)
    fxch    %st(1)
    fstpl   -8(%ebp)
    fxch    %st(1)
    
    fstpl   -16(%ebp)
    fldl    -16(%ebp)
    fmulp   %st, %st(1)
    fstpl   -16(%ebp)
    
    ## 5/2
    
    ## 5*0.5
    ## done
    
    movl    $5, %eax
    leave
    ret
    
    1*1e-6F;
    
    1/1e6F;
    
    public void Mutiplication()
    {
        float a = 1.0f;
    
        for(int i=0; i<1000000; i++)
        {
            a *= 0.5f;
        }
    }
    public void Division()
    {
        float a = 1.0f;
    
        for(int i=0; i<1000000; i++)
        {
            a /= 2.0f;
        }
    }
    
    Multiplications():   time/call: 1524.375 ms
    Division():          time/call: 1220.003 ms
    
    double half(double x) => x / 2.0;
    
    double half(double x) => x * 0.5;
    
    double quantize(double x)
    {
        if (half(x) > threshold))
            return 1;
        else
            return -1;
    }