Java:为什么计算比赋值(int)更快?

Java:为什么计算比赋值(int)更快?,java,performance,variable-assignment,Java,Performance,Variable Assignment,同一函数的以下两个版本(基本上尝试通过暴力恢复密码)的性能不同: 第1版: …与版本1相比: i = length -1; 然而,情况恰恰相反:版本1的速度快了3%以上! 有人知道为什么吗?这是因为编译器进行了一些优化吗 谢谢你们的回答。 只需补充一点,我们的目标实际上不是优化这段代码(这段代码已经相当优化),而是从编译器/CPU/体系结构的角度来理解,是什么可以解释这种性能差异。 您的回答非常有帮助,再次感谢 键在微基准测试中很难检查这一点,因为在不读取生成的机器代码的情况下,您无法确定代码

同一函数的以下两个版本(基本上尝试通过暴力恢复密码)的性能不同:

第1版: …与版本1相比:

i = length -1;
然而,情况恰恰相反:版本1的速度快了3%以上! 有人知道为什么吗?这是因为编译器进行了一些优化吗

谢谢你们的回答。 只需补充一点,我们的目标实际上不是优化这段代码(这段代码已经相当优化),而是从编译器/CPU/体系结构的角度来理解,是什么可以解释这种性能差异。 您的回答非常有帮助,再次感谢


在微基准测试中很难检查这一点,因为在不读取生成的机器代码的情况下,您无法确定代码是如何优化的,即使这样,CPU将来也可以做很多技巧来优化它,例如,它将RISC式指令中的x86代码转换为实际执行

一次计算只需一个周期,CPU一次最多可以执行三次。对一级缓存的访问需要4个周期,而对于二级、三级、主存的访问需要11、40-75、200个周期


在许多情况下,存储值以避免简单计算实际上速度较慢。顺便说一句,使用除法和模是非常昂贵的,在微调优代码时缓存此值是值得的。

正确答案应该可以通过解汇编程序(我的意思是.class->.java转换器)检索到, 但我的猜测是,编译器可能已经决定完全去掉iref,并决定存储
length-1
一个辅助寄存器。 我更喜欢C++,但我先尝试一下:

const int refi = length - 1;
在for循环内部。你也应该使用

indexes[ refi ] = 1;

首先,你不能仅仅通过运行你的程序来比较性能

此外,现代CPU上的减法运算平均只需三分之一个时钟周期。在3GHz CPU上,这是0.1纳秒。没有任何东西告诉您减法实际上是在编译器修改代码时发生的

因此:

  • 你应该试着检查一下房间
  • 如果您真正关心性能,请创建适当的微基准

  • 比较代码的运行时间不会给出准确的或隔离的结果

    首先,这不是比较这种表现的方式。这里需要进行运行时分析。这两种代码具有相同的循环结构,运行时间相同。运行代码时,可能会有不同的运行时间。但是,它们在缓存命中率、I/O时间、线程和进程调度方面存在很大差异。代码总是在一个精确的时间内完成,这是不存在隔离的

    但是,代码中仍然存在差异,为了理解差异,您应该查看CPU体系结构。我基本上可以按照x86架构来解释

    幕后发生了什么?

    i = refi;
    
    CPU将
    refi
    i
    从ram带到其寄存器。如果内存中的值不在缓存中,则有2个访问ram的权限。而
    i
    的值将写入ram。但是,根据线程和进程调度,它总是需要不同的时间。此外,如果值在虚拟内存中,则需要更长的时间

    i = length -1;
    
    CPU还可以从ram或缓存访问
    i
    length
    。访问的次数相同。此外,这里还有一个减法,这意味着额外的CPU周期。这就是为什么你认为这一个需要更长的时间来完成。这是意料之中的,但我上面提到的问题解释了为什么这需要更长的时间

    i = length -1;
    
    求和

    正如我所解释的,这不是比较性能的方法。我认为,这些代码之间没有真正的区别。CPU内部和编译器中都有很多优化。如果反编译
    .class
    文件,可以看到优化的代码

    我的建议是最好尽量减少BigO运行时分析。如果你找到更好的算法,这是优化代码的最好方法。如果您的代码中仍然存在瓶颈,您可以尝试微基准测试

    另请参见


    您可能需要确保您的基准测试是正确的。对java进行基准测试并不总是容易的,3%也不是很多。java中的等价物是
    final int refi=length-1
    ;然后,@Parakram再也没有建议他查看类文件,他建议使用类文件反编译器来查看编译器从原始源文件优化了什么。@JurgenCamilleri反编译器可以提供一些但有限的信息。有些字节码在Java中没有等效的表示形式,例如,反编译器无法显示
    开关
    块是否编译为跳转表,而不是一系列isub+ifeq与偏移量*值,等等。
    i = refi;
    
    i = length -1;