可选的 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 集成。
与其说是一个问题,不如说是一个观察。
我有点惊讶 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 集成。