是否可以阻止JIT优化远离方法调用?

我们正在构建一个用于Java Byte Code程序的平均案例运行时分析的工具。 其中一部分是测量实际运行时间。 因此,我们将采用任意的,用户提供的方法,可能有也可能没有结果,可能有也可能没有副作用(例子包括Quicksort,factorial,dummy嵌套循环……)并执行它(使用reflection),测量经过的时间。 (无论我们是否正确地进行基准测试,除了这里的要点之外。)

在基准测试代码中,我们显然不对结果做任何事情(有些方法甚至没有结果)。 因此,没有人知道JIT可能会做什么,事实上我们已经观察到它似乎有时会优化整个基准测试方法。 由于基准测试方法实际上并未单独使用,因此基准测试无用。

我们怎样才能阻止JIT这样做呢? 我们不想完全关闭它,因为基准测试需要很长时间,而且我们想要对“真实”运行时进行基准测试(因此我们希望JIT在方法内部处于活动状态)。

我知道这个问题但是给定的场景太窄了; 我们不知道结果类型(如果有的话),因此不能以某种方式使用结果,JIT认为没有用处。

简单的解决方案是编写一个更实际的基准测试,它可以做一些几乎有用的事情,因此不会对其进行优化。

混淆JIT有很多技巧,但这些不太可能对你有所帮助。

以下是基准测试的示例,其中通过reflection,MethodHandle调用该方法并将其编译为空。

import java.lang.invoke.*; import java.lang.reflect.*; public class Main { public static void main(String... args) throws Throwable { for (int j = 0; j < 5; j++) { testViaReflection(); testViaMethodHandle(); testWithoutReflection(); } } private static void testViaReflection() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { Method nothing = Main.class.getDeclaredMethod("nothing"); int runs = 10000000; // triggers a warmup. long start = System.nanoTime(); Object[] args = new Object[0]; for (int i = 0; i < runs; i++) nothing.invoke(null, args); long time = System.nanoTime() - start; System.out.printf("A call to %s took an average of %.1f ns using reflection%n", nothing.getName(), 1.0 * time / runs); } private static void testViaMethodHandle() throws Throwable { MethodHandle nothing = MethodHandles.lookup().unreflect(Main.class.getDeclaredMethod("nothing")); int runs = 10000000; // triggers a warmup. long start = System.nanoTime(); for (int i = 0; i < runs; i++) { nothing.invokeExact(); } long time = System.nanoTime() - start; System.out.printf("A call to %s took an average of %.1f ns using MethodHandle%n", "nothing", 1.0 * time / runs); } private static void testWithoutReflection() { int runs = 10000000; // triggers a warmup. long start = System.nanoTime(); for (int i = 0; i < runs; i++) nothing(); long time = System.nanoTime() - start; System.out.printf("A call to %s took an average of %.1f ns without reflection%n", "nothing", 1.0 * time / runs); } public static void nothing() { // does nothing. } } 

版画

 A call to nothing took an average of 6.6 ns using reflection A call to nothing took an average of 10.7 ns using MethodHandle A call to nothing took an average of 0.4 ns without reflection A call to nothing took an average of 4.5 ns using reflection A call to nothing took an average of 9.1 ns using MethodHandle A call to nothing took an average of 0.0 ns without reflection A call to nothing took an average of 4.3 ns using reflection A call to nothing took an average of 8.8 ns using MethodHandle A call to nothing took an average of 0.0 ns without reflection A call to nothing took an average of 5.4 ns using reflection A call to nothing took an average of 13.2 ns using MethodHandle A call to nothing took an average of 0.0 ns without reflection A call to nothing took an average of 4.9 ns using reflection A call to nothing took an average of 8.7 ns using MethodHandle A call to nothing took an average of 0.0 ns without reflection 

我假设MethodHandles比reflection更快,但它不会出现。

我不相信有任何方法可以选择性地禁用JIT优化,除了一些实验性的(如逃逸分析)。

你这样说:

我们不想完全关闭它,因为基准测试需要很长时间,而且我们想要对“真正的”运行时进行基准测试。

但是你要做的就是这样。 在实际的运行时,方法调用被内联,如果它们不做任何事情,它们被优化掉。 因此,通过禁止这些优化,您将获得与实际程序中实际发生的不匹配的方法执行时间的测量。

基准测试的目的是尽可能接近实际性能,所以我不知道你会在这里获得什么。 如果您怀疑JIT会做某些事情,并且您不希望在正常使用中实际禁用它,那么您最好的选择是使用该假设来构建基准。 如果有一些方法可以编写可以强调它并在JIT下使其行为效率低下的基准测试,那么这可能也很有用,因为在分析器下运行基准测试将有助于弄清楚它何时会降低其效率。