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;
}