正如其他人指出的那样,该测试在许多方面都有缺陷。
你没有告诉我们到底 如何 ,你做了这个测试。但是,我试图实施这样的“幼稚”测试(无冒犯):
class PrePostIncrement
{
public static void main(String args[])
{
for (int j=0; j<3; j++)
{
for (int i=0; i<5; i++)
{
long before = System.nanoTime();
runPreIncrement();
long after = System.nanoTime();
System.out.println("pre : "+(after-before)/1e6);
}
for (int i=0; i<5; i++)
{
long before = System.nanoTime();
runPostIncrement();
long after = System.nanoTime();
System.out.println("post : "+(after-before)/1e6);
}
}
}
private static void runPreIncrement()
{
final int n = Integer.MAX_VALUE;
int i = 0;
while (++i < n) {}
}
private static void runPostIncrement()
{
final int n = Integer.MAX_VALUE;
int i = 0;
while (i++ < n) {}
}
}
当使用默认设置运行它时,似乎有很小的差异。但是,当您使用该标志运行基准测试时,基准测试的 真正
缺陷就变得显而易见-server
。在我的情况下,结果就像
...
pre : 6.96E-4
pre : 6.96E-4
pre : 0.001044
pre : 3.48E-4
pre : 3.48E-4
post : 1279.734543
post : 1295.989086
post : 1284.654267
post : 1282.349093
post : 1275.204583
显然,预增量版本已被 完全优化 。原因很简单:不使用结果。循环是否执行完全无关紧要,因此JIT只需将其删除即可。
通过查看热点反汇编可以确认这一点:预增量版本产生以下代码:
[Entry Point]
[Verified Entry Point]
[Constants]
# {method} {0x0000000055060500} 'runPreIncrement' '()V' in 'PrePostIncrement'
# [sp+0x20] (sp of caller)
0x000000000286fd80: sub $0x18,%rsp
0x000000000286fd87: mov %rbp,0x10(%rsp) ;*synchronization entry
; - PrePostIncrement::runPreIncrement@-1 (line 28)
0x000000000286fd8c: add $0x10,%rsp
0x000000000286fd90: pop %rbp
0x000000000286fd91: test %eax,-0x243fd97(%rip) # 0x0000000000430000
; {poll_return}
0x000000000286fd97: retq
0x000000000286fd98: hlt
0x000000000286fd99: hlt
0x000000000286fd9a: hlt
0x000000000286fd9b: hlt
0x000000000286fd9c: hlt
0x000000000286fd9d: hlt
0x000000000286fd9e: hlt
0x000000000286fd9f: hlt
后递增版本产生以下代码:
[Entry Point]
[Verified Entry Point]
[Constants]
# {method} {0x00000000550605b8} 'runPostIncrement' '()V' in 'PrePostIncrement'
# [sp+0x20] (sp of caller)
0x000000000286d0c0: sub $0x18,%rsp
0x000000000286d0c7: mov %rbp,0x10(%rsp) ;*synchronization entry
; - PrePostIncrement::runPostIncrement@-1 (line 35)
0x000000000286d0cc: mov $0x1,%r11d
0x000000000286d0d2: jmp 0x000000000286d0e3
0x000000000286d0d4: nopl 0x0(%rax,%rax,1)
0x000000000286d0dc: data32 data32 xchg %ax,%ax
0x000000000286d0e0: inc %r11d ; OopMap{off=35}
;*goto
; - PrePostIncrement::runPostIncrement@11 (line 36)
0x000000000286d0e3: test %eax,-0x243d0e9(%rip) # 0x0000000000430000
;*goto
; - PrePostIncrement::runPostIncrement@11 (line 36)
; {poll}
0x000000000286d0e9: cmp $0x7fffffff,%r11d
0x000000000286d0f0: jl 0x000000000286d0e0 ;*if_icmpge
; - PrePostIncrement::runPostIncrement@8 (line 36)
0x000000000286d0f2: add $0x10,%rsp
0x000000000286d0f6: pop %rbp
0x000000000286d0f7: test %eax,-0x243d0fd(%rip) # 0x0000000000430000
; {poll_return}
0x000000000286d0fd: retq
0x000000000286d0fe: hlt
0x000000000286d0ff: hlt
对于我来说,还不是很清楚为什么它似乎 没有 删除后增量版本。(实际上,我认为这是一个单独的问题)。但是至少,这解释了为什么您可能会看到“数量级”差异。
编辑:有趣的是,从改变环路的上限时Integer.MAX_VALUE
到Integer.MAX_VALUE-1
,那么 这两个
版本被优化掉,并且需要“零”的时间。这种限制(仍然会出现0x7fffffff
在装配体中)以某种方式阻止了优化。据推测,这与将比较映射到(单个!)cmp
指令有关,但是我不能给出更深层的理由。JIT以神秘的方式工作…
问题内容: 为什么Google会优先使用其(私有)JSON响应? 例如,这是在Google日历中打开和关闭日历时的响应: 我认为这是为了防止人们对此进行操作,但是您真正要做的就是替换,然后进行设置。我认为评估是为了确保人们编写安全的JSON解析代码。 我也曾在其他几个地方使用过此功能,但在Google(邮件,日历,通讯录等)中使用了更多功能。奇怪的是,GoogleDocs以开头,而Google C
我想我的程序跳过了while循环,但我真的不确定到底发生了什么。该函数应该通过找到GCD,然后将分子和分母除以该数字来减少分数。 我得到分子和分母的绝对值,以确保如果分数是负数,我会在最后保持它。如果分子为0,则要求我返回(0,1)。问题是关于while循环。。。似乎它被完全跳过了。有什么建议吗?
本文向大家介绍do……while和while……do有什么区别?相关面试题,主要包含被问及do……while和while……do有什么区别?时的应答技巧和注意事项,需要的朋友参考一下 前一个循环一遍再判断,后一个判断以后 再循环。
我正在写一份简单的银行申请书。在我的程序中,我使用了while循环。如果用户输入错误,它将再次提示用户输入。 现在的问题是我无法编写任何语句。它总是显示错误(比如:unreachable语句),最终该行不会被打印出来。 我该怎么解决这个问题? [我之所以需要使用,是因为我想打印用户输入的所有信息。] 我正在从事的项目:
所以我这里有一个程序,它只是简单地模仿跳棋(但不是跳,而是一个棋子简单地“吃掉”另一个棋子)。我有一个简单的类,它实现了一个“checkers”类对象,并创建了一个名为“chips”的int数组变量。还有一个“while”语句,它在chip[0]和chips[1]整数大于0时创建了一个循环。看起来是这样的: Checkers类中的count方法如下所示: 板是8 x 8(这就是board.leng
问题内容: 我一直在使用“ if”来测试自己的版本,并且一切似乎都正常。当然,如果使用signalAll()而不是signal(),这将导致严重崩溃,但是如果一次仅通知一个线程,这怎么会出错? 他们的代码在这里 -检查put()和take()方法;在Condition的JavaDoc顶部可以看到一个更简单,更重点的实现。 下面是我实施的相关部分。 PS我知道,通常,尤其是在这样的lib类中,应该让