可选的 JIT 编译

JIT compilation of Optional

与其说是一个问题,不如说是一个观察。

我有点惊讶 JIT 编译器没有内联可选 class 的使用,因为自 Java 8 以来它似乎是该语言中被频繁使用的部分。期望以下两种测试方法执行等效:

    import static java.util.Optional.*;

    public static class TestClass {

        int i;

        public void test1(Integer x) {
            i = ofNullable(x).orElse(-1);
        }

        public void test2(Integer x) {
            if (x == null)
                i = -1;
            else
                i = x;
        }
    }

相反,test1 总是为非空值分配一个 Optional,因此比 test2 慢 20 倍。看起来这段代码应该很容易被 JIT 编译器优化。

我在 Java 8 和 Java 11 上对此进行了测试。有人知道 Java 的新版本是否在优化方面做得更好吗?总的来说,我喜欢在 if/else 语句上使用 ofNullable 的简洁性,但由于 GC 繁重,我不能在关键代码路径中使用它们。

编辑:这是我使用的基准测试代码:

@BenchmarkOptions(benchmarkRounds = 100, warmupRounds = 20)
public class BenchmarkTest {
    @Rule
    public TestRule benchmarkRun = new BenchmarkRule();

    public static Integer[] ARRAY;

    /** Prepare random numbers for tests. */
    @BeforeClass
    public static void beforeClass() {
        ARRAY = new Integer[10000000];
        for (int i = 0; i < 10000000; i++)
            ARRAY[i] = i % 2 == 0 ? null : i;
    }

    @Test
    public void test1() throws Exception {
        TestClass x = new TestClass();
        for (int a = 0; a < ARRAY.length; a++) {
            x.test1(ARRAY[a]);
        }
    }

    @Test
    public void test2() throws Exception {
        TestClass x = new TestClass();
        for (int a = 0; a < ARRAY.length; a++) {
            x.test2(ARRAY[a]);
        }
    }

如果你想使用像Optional提供的更具可读性的方式,又想控制JIT。您可以编写一个 JIT 可以轻松内联的简单 class。

package util;


public class OptionalInteger {

    public interface NullableInt {
        public int orElse(int defaultValue);
    }

    public static NullableInt ofNullable(Integer integer) {
        return new NullableInt() {

            @Override
            public int orElse(int elseValue) {
                return integer == null ? elseValue : integer;
            }
        };
    }
}

您的客户端代码看起来几乎一样。

import static util.OptionalInteger.ofNullable;

public class TestClass {

    int i;

    public void test1(Integer x) {
        i = ofNullable(x).orElse(-1);
    }

}

但 JIT 很容易识别它可以被内联。


这里是测试代码

class Main {
    public static void main(String args[]) {
        List<Integer> integers = new ArrayList<>();

        Random random = new Random();

        for (int i = 0; i < 100000000; i++) {
            Integer inte = random.nextBoolean() ? random.nextInt() : null;
            integers.add(inte);
        }

        TestClass testClass = new TestClass();

        warmUpJIT(integers, testClass);

        execWithMeasurement(testClass, integers);
    }

    private static void warmUpJIT(List<Integer> integers, TestClass testClass) {
        exec(testClass, integers);
        exec(testClass, integers);
        exec(testClass, integers);
        exec(testClass, integers);
    }

    private static void execWithMeasurement(TestClass testClass, List<Integer> integers) {
        long start = System.currentTimeMillis();

        int result = exec(testClass, integers);

        long end = System.currentTimeMillis();

        String msg = MessageFormat.format("Result {0} - took {1} ms", result, (end - start));
        System.out.println(msg);
    }

    private static int exec(TestClass testClass, List<Integer> integers) {
        int result = 0;

        for (Integer integer : integers) {
            testClass.test1(integer);
            result += testClass.i;
        }

        return result;
    }
}

我使用了 JITclipse,这是我前一段时间写的 JITWatch 的 eclipse 集成。