栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

为什么“ while(i ++ <n){}”比“ while(++ i <n){}”显着慢

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

为什么“ while(i ++ <n){}”比“ while(++ i <n){}”显着慢

正如其他人指出的那样,该测试在许多方面都有缺陷。

你没有告诉我们到底 如何 ,你做了这个测试。但是,我试图实施这样的“幼稚”测试(无冒犯):

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-4pre  : 6.96E-4pre  : 0.001044pre  : 3.48E-4pre  : 3.48E-4post : 1279.734543post : 1295.989086post : 1284.654267post : 1282.349093post : 1275.204583

显然,预增量版本已被 完全优化 。原因很简单:不使用结果。循环是否执行完全无关紧要,因此JIT只需将其删除即可。

通过查看热点反汇编可以确认这一点:预增量版本产生以下代码:

[Entry Point][Verified Entry Point][Constants]  # {method} {0x0000000055060500} &apos;runPreIncrement&apos; &apos;()V&apos; in &apos;PrePostIncrement&apos;  #[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} &apos;runPostIncrement&apos; &apos;()V&apos; in &apos;PrePostIncrement&apos;  #[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以神秘的方式工作…



转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/495916.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号