Java方法调用性能

Java方法调用性能,java,performance,Java,Performance,我正在做这段代码。当t=100000时,每个输入行中的i和j总是发生变化,它在Java8U60中的执行时间约为12秒 for (int a0 = 0; a0 < t; a0++) { String line = reader.readLine(); String[] ls = line.split(" "); int i = Integer.parseInt(ls[0]); int j = Integer.parseInt(ls[1]); int m

我正在做这段代码。当t=100000时,每个输入行中的i和j总是发生变化,它在Java8U60中的执行时间约为12秒

for (int a0 = 0; a0 < t; a0++) {
    String line = reader.readLine();
    String[] ls = line.split(" ");
    int i = Integer.parseInt(ls[0]);
    int j = Integer.parseInt(ls[1]);
    int min = width[i];
    for (int k = i + 1; k <= j; k++) {
        if (min > width[k]) {
            min = width[k];
        }
    }
    writer.write(min + "");
    writer.newLine();
}
for(int a0=0;a0
当我提取一个新方法来寻找最小值时,执行时间要快4倍(大约2.5秒)

for(int a0=0;a0

我一直认为方法调用很慢。但这个例子正好相反。Java6也演示了这一点,但在这两种情况下(17秒和10秒)的执行时间都要慢得多。有人能对此提供一些见解吗?

在不进行实际分析的情况下,
getMin
很可能是JIT编译的,因为您将它提取到一个被多次调用的方法中。如果您使用的是HotSpot JVM,则默认情况下,在执行10000个方法之后会发生这种情况


通过使用正确的标志和JVM构建,您始终可以检查应用程序使用的最终代码。查看问题/答案,了解如何操作的示例。

Java相对于语言的一个优势​​编译为C++是因为JIT(实时编译器)可以在执行代码时从字节码进行优化。此外,Java编译器本身已经准备好在构建阶段进行一些优化。 例如,这些技术允许将方法调用转换为循环中的内联代码,从而避免多态调用中重复的方法搜索开销。 使方法调用以内联方式运行意味着方法代码的运行就像它是在调用方法的地方直接编写的一样。因此,不存在要执行的方法、内存分配和新上下文变量的搜索开销。
基本上,在for循环中,当您在内存中分配新变量(例如int k)时会发生处理丢失,当您将for传递到方法中时,您最终会减少开销,因为变量已经分配给此执行

我相信java正在做一些优化/记忆。如果函数/方法是纯函数,它可以缓存函数的结果。我相信你的时间减少了,但是你的空间/记忆会增加(由于记忆),反之亦然。

这个问题没有提供一个可复制的测试用例。因此,它只专注于计算范围极小值:

git clone git@github.com:lemire/microbenchmarks.git
cd microbenchmarks
mvn clean install
java -cp target/microbenchmarks-0.0.1-jar-with-dependencies.jar me.lemire.microbenchmarks.rangequery.RangeMinimum
我的结果是(在为测试配置的服务器上,Java 8):

因此,在我的测试用例中,有一个带子循环的大循环,或者有一个包含函数调用的循环,两者之间没有显著的性能差异。请注意,基准测试多次调用函数,以便JIT编译器能够完成其工作

TL;DRJIT编译器在第二种情况下有更多机会优化内部循环,因为堆栈上的替换发生在不同的点上

我已经设法用简化的测试用例重现了问题。
不涉及I/O或字符串操作,只涉及两个具有数组访问权限的嵌套循环

public class NestedLoop {
    private static final int ARRAY_SIZE = 5000;
    private static final int ITERATIONS = 1000000;

    private int[] width = new java.util.Random(0).ints(ARRAY_SIZE).toArray();

    public long inline() {
        long sum = 0;

        for (int i = 0; i < ITERATIONS; i++) {
            int min = width[0];
            for (int k = 1; k < ARRAY_SIZE; k++) {
                if (min > width[k]) {
                    min = width[k];
                }
            }
            sum += min;
        }

        return sum;
    }

    public long methodCall() {
        long sum = 0;

        for (int i = 0; i < ITERATIONS; i++) {
            int min = getMin();
            sum += min;
        }

        return sum;
    }

    private int getMin() {
        int min = width[0];
        for (int k = 1; k < ARRAY_SIZE; k++) {
            if (min > width[k]) {
                min = width[k];
            }
        }
        return min;
    }

    public static void main(String[] args) {
        long startTime = System.nanoTime();
        long sum = new NestedLoop().inline();  // or .methodCall();
        long endTime = System.nanoTime();

        long ms = (endTime - startTime) / 1000000;
        System.out.println("sum = " + sum + ", time = " + ms + " ms");
    }
}
“内联”编译日志: “methodCall”编译日志: 这意味着JIT完成了它的工作,但生成的代码必须不同。
让我们用
-XX:+PrintAssembly
来分析它


“内联”反汇编(最热门的片段) “methodCall”分解(也是最热门的部分) 编译后的代码完全不同<代码>方法调用
优化得更好

  • 循环有8个展开的迭代
  • 内部没有数组边界检查
  • width
    字段缓存在寄存器中
相比之下,
inline
variant

  • 不进行循环展开
  • 每次从内存加载
    width
    数组
  • 对每个迭代执行数组边界检查

OSR编译的方法并不总是得到很好的优化,因为它们必须在转换点保持已解释堆栈帧的状态。这里也有同样的问题


堆栈上的替换通常发生在向后的分支上(即在循环的底部)
inline
方法有两个嵌套循环,OSR发生在内部循环中,而
methodCall
只有一个外部循环。OSR转换在外循环中更有利,因为JIT编译器有更多的自由来优化内循环。这就是你的情况。

很抱歉投了反对票,但答案是误导性的。它没有解释为什么第一段代码比较慢。HotSpot JVM可以轻松地编译带有循环的方法,即使它们不包含其他方法调用。关于10000次调用的信息也有误导性,JDK 8+中的HotSpot开始编译的时间要早得多。有关详细信息,请参阅。@apangin据我所知,HotSpot JVM JIT编译的是方法而不是代码块,因此,如果原始代码段包含在一个只调用一次的方法中的循环中,那么缺少JIT将解释为什么第一段代码速度较慢。也许我关于这方面的信息现在已经过时了,但我也看不出有什么原因导致这种差异,这就是为什么我认为JIT差异是最有可能的原因。如果一个方法内部有一个热循环,那么整个方法都将被编译并替换OSR。事实上,这两种情况下都进行了汇编。然而
m.l.m.r.RangeMinimum.embeddedmin    avgt        5  0.053 ± 0.009  ms/op
m.l.m.r.RangeMinimum.fncmin         avgt        5  0.052 ± 0.003  ms/op
public class NestedLoop {
    private static final int ARRAY_SIZE = 5000;
    private static final int ITERATIONS = 1000000;

    private int[] width = new java.util.Random(0).ints(ARRAY_SIZE).toArray();

    public long inline() {
        long sum = 0;

        for (int i = 0; i < ITERATIONS; i++) {
            int min = width[0];
            for (int k = 1; k < ARRAY_SIZE; k++) {
                if (min > width[k]) {
                    min = width[k];
                }
            }
            sum += min;
        }

        return sum;
    }

    public long methodCall() {
        long sum = 0;

        for (int i = 0; i < ITERATIONS; i++) {
            int min = getMin();
            sum += min;
        }

        return sum;
    }

    private int getMin() {
        int min = width[0];
        for (int k = 1; k < ARRAY_SIZE; k++) {
            if (min > width[k]) {
                min = width[k];
            }
        }
        return min;
    }

    public static void main(String[] args) {
        long startTime = System.nanoTime();
        long sum = new NestedLoop().inline();  // or .methodCall();
        long endTime = System.nanoTime();

        long ms = (endTime - startTime) / 1000000;
        System.out.println("sum = " + sum + ", time = " + ms + " ms");
    }
}
-XX:-TieredCompilation
-XX:CompileOnly=NestedLoop
-XX:+UnlockDiagnosticVMOptions
-XX:+PrintCompilation
-XX:+TraceNMethodInstalls
    251   46 %           NestedLoop::inline @ 21 (70 bytes)
Installing osr method (4) NestedLoop.inline()J @ 21
    271   46             NestedLoop::getMin (41 bytes)
Installing method (4) NestedLoop.getMin()I 
    274   47 %           NestedLoop::getMin @ 9 (41 bytes)
Installing osr method (4) NestedLoop.getMin()I @ 9
    314   48 %           NestedLoop::methodCall @ 4 (30 bytes)
Installing osr method (4) NestedLoop.methodCall()J @ 4
0x0000000002df4dd0: inc    %ebp               ; OopMap{r11=Derived_oop_rbx rbx=Oop off=114}
                                              ;*goto
                                              ; - NestedLoop::inline@53 (line 12)

0x0000000002df4dd2: test   %eax,-0x1d64dd8(%rip)        # 0x0000000001090000
                                              ;*iload
                                              ; - NestedLoop::inline@21 (line 12)
                                              ;   {poll}
0x0000000002df4dd8: cmp    $0x1388,%ebp
0x0000000002df4dde: jge    0x0000000002df4dfd  ;*if_icmpge
                                              ; - NestedLoop::inline@26 (line 12)

0x0000000002df4de0: test   %rbx,%rbx
0x0000000002df4de3: je     0x0000000002df4e4c
0x0000000002df4de5: mov    (%r11),%r10d       ;*getfield width
                                              ; - NestedLoop::inline@32 (line 13)

0x0000000002df4de8: mov    0xc(%r10),%r9d     ; implicit exception
0x0000000002df4dec: cmp    %r9d,%ebp
0x0000000002df4def: jae    0x0000000002df4e59
0x0000000002df4df1: mov    0x10(%r10,%rbp,4),%r8d  ;*iaload
                                              ; - NestedLoop::inline@37 (line 13)

0x0000000002df4df6: cmp    %r8d,%r13d
0x0000000002df4df9: jg     0x0000000002df4dc6  ;*if_icmple
                                              ; - NestedLoop::inline@38 (line 13)

0x0000000002df4dfb: jmp    0x0000000002df4dd0
0x0000000002da2af0: add    $0x8,%edx          ;*iinc
                                              ; - NestedLoop::getMin@33 (line 36)
                                              ; - NestedLoop::methodCall@11 (line 27)

0x0000000002da2af3: cmp    $0x1381,%edx
0x0000000002da2af9: jge    0x0000000002da2b70  ;*iload_1
                                              ; - NestedLoop::getMin@16 (line 37)
                                              ; - NestedLoop::methodCall@11 (line 27)

0x0000000002da2afb: mov    0x10(%r9,%rdx,4),%r11d  ;*iaload
                                              ; - NestedLoop::getMin@22 (line 37)
                                              ; - NestedLoop::methodCall@11 (line 27)

0x0000000002da2b00: cmp    %r11d,%ecx
0x0000000002da2b03: jg     0x0000000002da2b6b  ;*iinc
                                              ; - NestedLoop::getMin@33 (line 36)
                                              ; - NestedLoop::methodCall@11 (line 27)

0x0000000002da2b05: mov    0x14(%r9,%rdx,4),%r11d  ;*iaload
                                              ; - NestedLoop::getMin@22 (line 37)
                                              ; - NestedLoop::methodCall@11 (line 27)

0x0000000002da2b0a: cmp    %r11d,%ecx
0x0000000002da2b0d: jg     0x0000000002da2b5c  ;*iinc
                                              ; - NestedLoop::getMin@33 (line 36)
                                              ; - NestedLoop::methodCall@11 (line 27)

0x0000000002da2b0f: mov    0x18(%r9,%rdx,4),%r11d  ;*iaload
                                              ; - NestedLoop::getMin@22 (line 37)
                                              ; - NestedLoop::methodCall@11 (line 27)

0x0000000002da2b14: cmp    %r11d,%ecx
0x0000000002da2b17: jg     0x0000000002da2b4d  ;*iinc
                                              ; - NestedLoop::getMin@33 (line 36)
                                              ; - NestedLoop::methodCall@11 (line 27)

0x0000000002da2b19: mov    0x1c(%r9,%rdx,4),%r11d  ;*iaload
                                              ; - NestedLoop::getMin@22 (line 37)
                                              ; - NestedLoop::methodCall@11 (line 27)

0x0000000002da2b1e: cmp    %r11d,%ecx
0x0000000002da2b21: jg     0x0000000002da2b66  ;*iinc
                                              ; - NestedLoop::getMin@33 (line 36)
                                              ; - NestedLoop::methodCall@11 (line 27)

0x0000000002da2b23: mov    0x20(%r9,%rdx,4),%r11d  ;*iaload
                                              ; - NestedLoop::getMin@22 (line 37)
                                              ; - NestedLoop::methodCall@11 (line 27)

0x0000000002da2b28: cmp    %r11d,%ecx
0x0000000002da2b2b: jg     0x0000000002da2b61  ;*iinc
                                              ; - NestedLoop::getMin@33 (line 36)
                                              ; - NestedLoop::methodCall@11 (line 27)

0x0000000002da2b2d: mov    0x24(%r9,%rdx,4),%r11d  ;*iaload
                                              ; - NestedLoop::getMin@22 (line 37)
                                              ; - NestedLoop::methodCall@11 (line 27)

0x0000000002da2b32: cmp    %r11d,%ecx
0x0000000002da2b35: jg     0x0000000002da2b52  ;*iinc
                                              ; - NestedLoop::getMin@33 (line 36)
                                              ; - NestedLoop::methodCall@11 (line 27)

0x0000000002da2b37: mov    0x28(%r9,%rdx,4),%r11d  ;*iaload
                                              ; - NestedLoop::getMin@22 (line 37)
                                              ; - NestedLoop::methodCall@11 (line 27)

0x0000000002da2b3c: cmp    %r11d,%ecx
0x0000000002da2b3f: jg     0x0000000002da2b57  ;*iinc
                                              ; - NestedLoop::getMin@33 (line 36)
                                              ; - NestedLoop::methodCall@11 (line 27)

0x0000000002da2b41: mov    0x2c(%r9,%rdx,4),%r11d  ;*iaload
                                              ; - NestedLoop::getMin@22 (line 37)
                                              ; - NestedLoop::methodCall@11 (line 27)

0x0000000002da2b46: cmp    %r11d,%ecx
0x0000000002da2b49: jg     0x0000000002da2ae6  ;*if_icmple
                                              ; - NestedLoop::getMin@23 (line 37)
                                              ; - NestedLoop::methodCall@11 (line 27)

0x0000000002da2b4b: jmp    0x0000000002da2af0