Search code examples
javajvmjit

JVM non-inlined methods optimizations


I basically know of some things that a JVM can do after it inlines a method, like scalar replacement, escape analysis or lock elision etc (I admit I dont know all of them). But what if a method is too big to be inlined? Are there any optimizations that a JVM can do to those methods? I am thinking loop unrolling would be one...

Anyone knowing the subject might shed some light?


Solution

  • Inlining is a kind of uber-optimization that widens the context for many other optimizations: common subexpression elimination, constant propagation, scalar replacement etc. A non-inlined method is a black box - JVM does not know whether such method modifies object fields, throw exceptions, which registers it spoils and so on.

    Inlining facilitates other optimizations, but this does not mean other optimizations cannot work without inlining. JIT compilation unit is a method, and JVM can apply nearly all optimizations to a big non-inlined method within its scope. Imagine that you created a very big method by inlining all callees manually in the source code. So, whether the inlining is done manually or automatically, the result control flow / data flow graph will be roughtly the same, so JIT will be able to handle both cases.

    In particular, Escape Analysis will perfectly work inside a big method; allocations and locks can be still eliminated if they don't escape this method.

    Let me demonstrate this with the following JMH benchmark. Run it with -prof gc to make sure no objects are allocated in both inlined and non-inlined cases.

    @State(Scope.Benchmark)
    public class Inline {
        double x = 111;
        double y = 222;
    
        @Benchmark
        public double inline() {
            return doInline(x, y);
    
        }
    
        @Benchmark
        public double noinline() {
            return dontInline(x, y);
        }
    
        @CompilerControl(CompilerControl.Mode.INLINE)
        private double doInline(double a, double b) {
            return new Vector2D(a, b).norm();
        }
    
        @CompilerControl(CompilerControl.Mode.DONT_INLINE)
        private double dontInline(double a, double b) {
            return new Vector2D(a, b).norm();
        }
    
        static class Vector2D {
            private final double x;
            private final double y;
    
            public Vector2D(double x, double y) {
                this.x = x;
                this.y = y;
            }
    
            public double norm() {
                return Math.sqrt(x * x + y * y);
            }
        }
    }
    

    An obvious requirement for scalar replacement to happen in HotSpot is that object constructor and all its called methods are inlined, but the caller itself does not need to be inlined.