2

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?

Eugene
  • 117,005
  • 15
  • 201
  • 306
  • “on those methods”? A method “too big to be inlined” still can get optimized. The more interesting part is the *caller* of that method, or the interaction between caller and callee. – Holger Sep 20 '18 at 15:02
  • @Holger I understand it can, it's not like it runs in the interpreted mode forever... – Eugene Sep 20 '18 at 15:15
  • I don't quite understand the question. JIT compilation unit is a method. JVM can apply nearly all optimizations to a big non-inlined method, though the scope of the optimizations will be limited by the scope of this method. – apangin Sep 20 '18 at 23:51
  • E.g. escape analysis will perfectly work inside this big method; allocations and locks can be still eliminated if they don't escape this method. – apangin Sep 20 '18 at 23:54
  • @apangin I thought that escape analysis is only done if a method is inlined, isn't it so? I only tried once looking into `scalar replacement` and that would not happen unless a method was inlined, thus I asumed other optimizations were only happening if inlining would happen – Eugene Sep 21 '18 at 04:54
  • @Eugene It isn't. What you are saying is true for constructors: for obvious reasons an object allocation cannot be eliminated unless its constructor is inlined, but otherwise inlining does not matter. – apangin Sep 21 '18 at 11:04
  • @apangin would you mind expanding that into an answer? Having said that would it be fair to ask if you know what optimizations are possible *only* because of inlining? – Eugene Sep 21 '18 at 11:09

2 Answers2

4

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.

apangin
  • 92,924
  • 10
  • 193
  • 247
  • Are you aware of any particular case when disabled inlining actually makes performance worse? – Sergey Tsypanov Oct 12 '21 at 10:40
  • @SergeyTsypanov Disabled inlining almost always makes performance worse... not sure I understand the question. – apangin Oct 12 '21 at 11:44
  • let me rephrase. I was looking into your answer to my question here https://stackoverflow.com/questions/63397711/linuxperfasmprofiler-shows-java-code-corresponding-assembly-hot-spot-for-java-8#comment112109002_63397711 and it came to my mind, that there could be a case when we disable inlining and as a result the code is not optimized as it is normally. And as a result of missing/weakened optimizations we now have the profile different from normal, i.e. hotspots in the profile collected with `-XX:MaxInlineLevel=0` might be different from those which are highlighted in "normal" profile – Sergey Tsypanov Oct 12 '21 at 12:54
  • @SergeyTsypanov Yes, this is absolutely possible. – apangin Oct 12 '21 at 16:34
1

Range check elimination would be an example of optimisation that doesn't require inlining. Take a look at Performance Techniques section of OpenJDK wiki for more examples, a long list of optimisations is listed in PerformanceTacticIndex.

Karol Dowbecki
  • 43,645
  • 9
  • 78
  • 111