Moe*_*oeb 11 java performance micro-optimization
一个) for(int i = 100000; i > 0; i--) {}
b) for(int i = 1; i < 100001; i++) {}
答案就在这个网站上(问题3).我只是想不通为什么?来自网站:
3. a
pax*_*blo 69
当你到达最低级别(机器代码,但我将使用程序集,因为它主要是一对一映射),空循环递减到0和一个递增到50(例如)之间的差异通常是沿着线条:
ld a,50 ld a,0
loop: dec a loop: inc a
jnz loop cmp a,50
jnz loop
Run Code Online (Sandbox Code Playgroud)
这是因为当你达到零时,大多数理智CPU中的零标志由递减指令设置.当增量指令达到50时,通常不能说同样的增长指令(因为这个值没有什么特别的,不像零).因此,您需要将寄存器与50进行比较以设置零标志.
但是,问两个循环中的哪一个:
for(int i = 100000; i > 0; i--) {}
for(int i = 1; i < 100001; i++) {}
Run Code Online (Sandbox Code Playgroud)
更快(在几乎任何环境中,Java或其他)都没用,因为它们都没有做任何有用的事情.这两个循环的最快版本根本没有循环.我挑战任何人提出比这更快的版本:-)
它们只有在你开始在大括号内做一些有用的工作时才会变得有用,并且在那时,工作将决定你应该使用哪个顺序.
例如,如果需要从1到100,000计数,则应使用第二个循环.这是因为100000-i每次需要使用它时,你必须在循环内部进行评估这一事实可能会淹没倒计时(如果有的话)的优势.在汇编方面,这将是以下区别:
ld b,100000 dsw a
sub b,a
dsw b
Run Code Online (Sandbox Code Playgroud)
(dsw当然,这是臭名昭着的do something with汇编程序助记符).
因为每次迭代你只会对一次递增循环进行一次加注,并且你将每次迭代至少对一次减法进行一次击打(假设你将使用i,否则根本不需要循环) ),你应该选择更自然的版本.
如果你需要数数,请数数.如果你需要倒计时,倒计时.
Mit*_*eat 23
在许多编译器中,为循环向后发送的机器指令更有效,因为测试零(因此对寄存器进行归零)比常量值的加载立即快.
另一方面,一个好的优化编译器应该能够检查循环内部并确定向后行不会导致任何副作用......
顺便说一下,在我看来,这是一个糟糕的面试问题.除非你在谈论一个运行了数百万次的循环,并且你已经确定重新创建正向循环值(n-i)的许多实例并没有超过轻微的增益,否则任何性能增益都将是最小的.
与往常一样,如果没有性能基准测试并且以更难理解代码为代价,请不要进行微优化.
cle*_*tus 17
这些问题在很大程度上是一种无关紧要的分心,有些人会对此痴迷.把它称为微优化崇拜或任何你喜欢的但是循环上升或下降更快?真的吗?您可以使用适合您所做的任何事情.您不会编写代码来保存两个时钟周期或其他任何时钟周期.
让编译器做它的目的,让你明确意图(编译器和阅读器).另一个常见的Java悲观化是:
public final static String BLAH = new StringBuilder().append("This is ").append(3).append(' text").toString();
Run Code Online (Sandbox Code Playgroud)
因为过多的连接确实会导致内存碎片,但是对于常量,编译器可以(并且会)优化它:
public final static String BLAH = "This is a " + 3 + " test";
Run Code Online (Sandbox Code Playgroud)
它不会优化第一个,第二个更容易阅读.
(a>b)?a:bvs Math.max(a,b)?怎么样?我知道我宁愿阅读第二篇,所以我并不在意第一次不会产生函数调用开销.
有在此列表中像知道一个一对夫妇有用的东西finally块不要求System.exit()是潜在有用的.知道将float除以0.0不会抛出异常是有用的.
但是,除非它真的很重要,否则不要再费力地猜测编译器(我打赌你99.99%的时间没有).
Pet*_*rey 13
一个更好的问题是;
哪个更容易理解/合作?
这比性能上的名义差异重要得多.就个人而言,我想指出,绩效不应成为确定差异的标准.如果他们不喜欢我挑战他们的假设,我不会因为没有得到这份工作而感到不快.;)
sta*_*lue 10
在现代Java实现上,这不是真的.总结数字高达10亿作为基准:
Java(TM) SE Runtime Environment 1.6.0_05-b13 Java HotSpot(TM) Server VM 10.0-b19 up 1000000000: 1817ms 1.817ns/iteration (sum 499999999500000000) up 1000000000: 1786ms 1.786ns/iteration (sum 499999999500000000) up 1000000000: 1778ms 1.778ns/iteration (sum 499999999500000000) up 1000000000: 1769ms 1.769ns/iteration (sum 499999999500000000) up 1000000000: 1769ms 1.769ns/iteration (sum 499999999500000000) up 1000000000: 1766ms 1.766ns/iteration (sum 499999999500000000) up 1000000000: 1776ms 1.776ns/iteration (sum 499999999500000000) up 1000000000: 1768ms 1.768ns/iteration (sum 499999999500000000) up 1000000000: 1771ms 1.771ns/iteration (sum 499999999500000000) up 1000000000: 1768ms 1.768ns/iteration (sum 499999999500000000) down 1000000000: 1847ms 1.847ns/iteration (sum 499999999500000000) down 1000000000: 1842ms 1.842ns/iteration (sum 499999999500000000) down 1000000000: 1838ms 1.838ns/iteration (sum 499999999500000000) down 1000000000: 1832ms 1.832ns/iteration (sum 499999999500000000) down 1000000000: 1842ms 1.842ns/iteration (sum 499999999500000000) down 1000000000: 1838ms 1.838ns/iteration (sum 499999999500000000) down 1000000000: 1838ms 1.838ns/iteration (sum 499999999500000000) down 1000000000: 1847ms 1.847ns/iteration (sum 499999999500000000) down 1000000000: 1839ms 1.839ns/iteration (sum 499999999500000000) down 1000000000: 1838ms 1.838ns/iteration (sum 499999999500000000)
请注意,时间差异很脆弱,环路附近的小变化可以使它们转过来.
编辑: 基准循环是
long sum = 0;
for (int i = 0; i < limit; i++)
{
sum += i;
}
Run Code Online (Sandbox Code Playgroud)
和
long sum = 0;
for (int i = limit - 1; i >= 0; i--)
{
sum += i;
}
Run Code Online (Sandbox Code Playgroud)
使用int类型的和大约快三倍,但总和溢出.使用BigInteger,它慢了50多倍:
BigInteger up 1000000000: 105943ms 105.943ns/iteration (sum 499999999500000000)
Run Code Online (Sandbox Code Playgroud)
通常,实际代码将向上运行更快.这有几个原因:
所以快乐地做正确的事通常会更快.不必要的微优化是邪恶的.自编程6502汇编程序以来,我没有故意编写反向循环.
实际上只有两种方法可以回答这个问题.
要告诉你它真的,无所谓,你浪费你的时间甚至想知道.
告诉您,唯一的方法是在您关心的实际生产硬件,操作系统和JRE安装上运行可靠的基准测试.
所以,我让你成为一个可运行的基准测试,你可以用来试试这个:
http://code.google.com/p/caliper/source/browse/trunk/test/examples/LoopingBackwardsBenchmark.java
这个Caliper框架尚未准备好迎接黄金时段,因此可能并不完全明白该如何处理,但如果你真的非常关心,你可以搞清楚.以下是我在linux盒子上给出的结果:
max benchmark ns
2 Forwards 4
2 Backwards 3
20 Forwards 9
20 Backwards 20
2000 Forwards 1007
2000 Backwards 1011
20000000 Forwards 9757363
20000000 Backwards 10303707
Run Code Online (Sandbox Code Playgroud)
向后循环看起来像对任何人的胜利?
| 归档时间: |
|
| 查看次数: |
4174 次 |
| 最近记录: |