是否可以阻止JIT优化远离方法调用? [英] Can JIT be prevented from optimising away method calls?

查看:126
本文介绍了是否可以阻止JIT优化远离方法调用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

We are building a tool for average case runtime analysis of Java Byte Code programs. One part of this is measuring real runtimes. So we would take an arbitrary, user provided method that may or may not have a result and may or may not have side effects (examples include Quicksort, factorial, dummy nested loops, ...) and execute it (using reflection), measuring the elapsed time. (Whether or not we benchmark properly at all is besides the point here.)

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

In the benchmarking code, we obviously don't do anything with the result (and some methods won't even have results). Therefore, there is no telling what the JIT may do, and we have in fact observed that it seems to optimise the whole benchmarked method call away on occasion. As the benchmarked methods are not used in isolation in reality, this renders the benchmark useless.

我们如何防止JIT这样做?我们不想完全关闭它,因为基准测试需要很长时间,而且我们想要对真实运行时进行基准测试(因此我们希望JIT在内部方法中运行)。

How can we prevent JIT from doing that? We don't want to turn it off completely because then benchmarking takes ages, and we want to benchmark "real" runtimes anyway (so we want JIT to be active inside the method).

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

I am aware of this question but the given scenario is too narrow; we do not know the result type (if there is one) and can therefore not use the result in some fashion the JIT does not see as useless.

推荐答案

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

The simple solution is to write a more realistic benchmark which does something almost useful so it will not be optimised away.

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

There are a number of trick to confuse the JIT, but these are unlikely to help you.

这是一个基准测试示例,通过反射,MethodHandle调用该方法并编译为空。

Here is example of a benchmark where the method is called via reflection, MethodHandle and compiled to nothing.

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比反射快,但它不会出现。

I had assumed MethodHandles to be faster than reflection but it doesn't appear so.

这篇关于是否可以阻止JIT优化远离方法调用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆