整数性能 - x32与x64 jvm的差异是30-50倍?

Xtr*_*der 14 java performance

最近我有一个非常奇怪的事情 - 一个方法在profiler下非常慢,没有明显的原因.它包含很少的操作long,但是被频繁调用 - 它的总体使用率约占总程序时间的30-40%,而其他部分看起来要"更重".

我通常在x32 JVM上运行非内存饥饿的程序,但假设我遇到了64位类型的问题,我尝试在x64 JVM上运行相同的程序 - "实时场景"中的整体性能提高了2-3倍.之后我用特定方法为操作创建了JMH基准测试,并对x32和x64 JVM的差异感到震惊 - 高达50次.

我会"接受"x32 JVM(较小的字号)大约慢2倍,但我没有30-50次可能来自的线索.你能解释一下这个巨大的差异吗?


对评论的回复:

  • 我重写了测试代码以"返回某些东西"并避免"死代码消除" - 它似乎没有改变'x32'的任何内容,但是'x64'上的某些方法明显变慢了.
  • 两个测试都在"客户端"下运行.在'-server'下运行没有明显的效果.

所以似乎我的问题的答案是

  • "测试代码"是错误的:由于"没有返回值",它允许JVM执行"死代码消除"或任何其他优化,并且看起来"x32 JVM"执行的此类优化比"x64 JVM"更少 - 这导致了x32和x64之间存在显着的"错误"差异
  • '正确的测试代码'上的性能差异高达2x-5x倍 - 这似乎是合理的

以下是结果(注意:? 10??特殊字符不是在Windows上打印的 - 它是低于0.001 s/op用科学记数法写成的10e- ??)

x32 1.8.0_152

Benchmark                Mode  Score Units    Score (with 'return')
IntVsLong.cycleInt       avgt  0.035  s/op    0.034   (?x slower vs. x64)
IntVsLong.cycleLong      avgt  0.106  s/op    0.099   (3x slower vs. x64) 
IntVsLong.divDoubleInt   avgt  0.462  s/op    0.459
IntVsLong.divDoubleLong  avgt  1.658  s/op    1.724   (2x slower vs. x64)
IntVsLong.divInt         avgt  0.335  s/op    0.373
IntVsLong.divLong        avgt  1.380  s/op    1.399
IntVsLong.l2i            avgt  0.101  s/op    0.197   (3x slower vs. x64)  
IntVsLong.mulInt         avgt  0.067  s/op    0.068
IntVsLong.mulLong        avgt  0.278  s/op    0.337   (5x slower vs. x64)
IntVsLong.subInt         avgt  0.067  s/op    0.067   (?x slower vs. x64)
IntVsLong.subLong        avgt  0.243  s/op    0.300   (4x slower vs. x64)

x64 1.8.0_152

Benchmark                Mode  Score Units    Score (with 'return')
IntVsLong.cycleInt       avgt ? 10??  s/op   ? 10??
IntVsLong.cycleLong      avgt  0.035  s/op    0.034
IntVsLong.divDoubleInt   avgt  0.045  s/op    0.788 (was dead)
IntVsLong.divDoubleLong  avgt  0.033  s/op    0.787 (was dead)
IntVsLong.divInt         avgt ? 10??  s/op    0.302 (was dead)
IntVsLong.divLong        avgt  0.046  s/op    1.098 (was dead)
IntVsLong.l2i            avgt  0.037  s/op    0.067
IntVsLong.mulInt         avgt ? 10??  s/op    0.052 (was dead)
IntVsLong.mulLong        avgt  0.040  s/op    0.067
IntVsLong.subInt         avgt ? 10??  s/op   ? 10??
IntVsLong.subLong        avgt  0.075  s/op    0.082
Run Code Online (Sandbox Code Playgroud)

这是(固定)基准代码

import org.openjdk.jmh.annotations.Benchmark;

public class IntVsLong {

    public static int N_REPEAT_I  = 100_000_000;
    public static long N_REPEAT_L = 100_000_000;

    public static int CONST_I = 3;
    public static long CONST_L = 3;
    public static double CONST_D = 3;

    @Benchmark
    public void cycleInt() throws InterruptedException {
        for( int i = 0; i < N_REPEAT_I; i++ ) {
        }
    }

    @Benchmark
    public void cycleLong() throws InterruptedException {
        for( long i = 0; i < N_REPEAT_L; i++ ) {
        }
    }

    @Benchmark
    public int divInt() throws InterruptedException {
        int r = 0;
        for( int i = 0; i < N_REPEAT_I; i++ ) {
            r += i / CONST_I;
        }
        return r;
    }

    @Benchmark
    public long divLong() throws InterruptedException {
        long r = 0;
        for( long i = 0; i < N_REPEAT_L; i++ ) {
            r += i / CONST_L;
        }
        return r;
    }

    @Benchmark
    public double divDoubleInt() throws InterruptedException {
        double r = 0;
        for( int i = 1; i < N_REPEAT_L; i++ ) {
            r += CONST_D / i;
        }
        return r;
    }

    @Benchmark
    public double divDoubleLong() throws InterruptedException {
        double r = 0;
        for( long i = 1; i < N_REPEAT_L; i++ ) {
            r += CONST_D / i;
        }
        return r;
    }

    @Benchmark
    public int mulInt() throws InterruptedException {
        int r = 0;
        for( int i = 0; i < N_REPEAT_I; i++ ) {
            r += i * CONST_I;
        }
        return r;
    }

    @Benchmark
    public long mulLong() throws InterruptedException {
        long r = 0;
        for( long i = 0; i < N_REPEAT_L; i++ ) {
            r += i * CONST_L;
        }
        return r;
    }

    @Benchmark
    public int subInt() throws InterruptedException {
        int r = 0;
        for( int i = 0; i < N_REPEAT_I; i++ ) {
            r += i - r;
        }
        return r;
    }

    @Benchmark
    public long subLong() throws InterruptedException {
        long r = 0;
        for( long i = 0; i < N_REPEAT_L; i++ ) {
            r += i - r;
        }
        return r;
    }

    @Benchmark
    public long l2i() throws InterruptedException {
        int r = 0;
        for( long i = 0; i < N_REPEAT_L; i++ ) {
            r += (int)i;
        }
        return r;
    }

}
Run Code Online (Sandbox Code Playgroud)

Dub*_*bas 1

有很多变量需要检查。

如果我们只关注使用 64 位的处理器,您可以在同一步骤中对 CPU 寄存器进行更多操作,因为它使用每个寄存器的 8 个八位字节而不是 4 个八位字节。这提高了操作性能和内存分配。此外,某些 CPU 仅启用仅在 64 位模式下运行的高级功能

如果您使用相同的 CPU 来执行测试,则需要考虑到要执行 32 位指令,CPU 需要在虚拟模式或保护模式下运行,这些模式运行速度比真正的 32 位 CPU 慢。此外,某些指令集扩展可能无法使用 32 位模式(如 SSE-SIMD 或 AVX)启用,但可以提高某些操作速度。

如果您使用的是 Windows 10 等现代操作系统,您还需要考虑到该操作系统使用 WOW64(x86 模拟器)运行 32 位应用程序

帮助文档: