2

I'm aware that functional programming/lambdas aren't the best option on the Java world when extreme high performance is the goal. Kotlin address to that issue with the inline keyword.

When Kotlin is compiled and lambdas are inlined, it is actually creating bigger methods and JIT has a hard cap of N bytes to inline to native code. With that in mind, doesn't Kotlin's inline hurts JIT's inlining and therefore affects performance?

Also, I noticed that Kotlin adds A LOT of nullchecks to the compiled code, those checks are really small methods and definitely are inlined by JIT, but due to the amount of calls, couldn't that also be a performance issue?

So, if you are aiming to the highest possible performance, what is Kotlin's impact on JVM?

Side node: I know, I know.. "over optimization is the evil of all roots", "you shouldn't care for performance at this level".

Foreign
  • 385
  • 2
  • 20

1 Answers1

1

You can override the limit of what inlining value to use for bytecode. See the answer at What is the size of methods that JIT automatically inlines? for more details.

I’m surprised there’s lots of null checks in the bytecode though. Normally Java doesn’t do that and instead lets the CPU handle those automatically and with higher performance.

They may be elided by the JVM’s JIT though so perhaps won’t be an issue once warmed up, but if you want the higher performance you probably want to put your server through dummy data runs to ensure that the JIT is warmed up. There’s nothing worse than a warm JIT hitting a compilation event and having to go through it all again.

If you look out for the JIT compilation events and look out for “hot method” messages not bring compiled or online then you can tweak those methods individually.

Generally though, if you avoid lambda then you’ll find the JVM won’t need to do so much inlining, and worse won’t need to do as much allocating. Lambdas are pretty terrible at accidentally capturing state and causing allocations to occur. Ideally in a high throughput/low latency system you want to avoid as much allocation as possible.

AlBlue
  • 23,254
  • 14
  • 71
  • 91
  • `inline` in kotlin is a compiler thing; while actual "changing" the threshold is a VM option. How many people compiling kotlin code do you know, that also change that setting? _especially_ if that is run on android. And what do you mean by "lets the CPU handle those automatically and with higher performance"? exactly what is automatically here? And of course `javac` emits checks against NullPointers; it used to be by calls to `getClass`, which where later replaced by `Objects::requireNonNull` – Eugene Jan 24 '21 at 04:22
  • The question was about the JVM, not Dallvik which is the Android runtime which is completely different. Secondly, JavaC doesn’t insert null pointer checks — the Objects::requireNonNull wasn’t added until Java 1.7 and clearly the JavaC compiler existed before then. NullPointerExceptions are handled by the zero page being marked as non readable and a segfault handler to raise it. If you’re interested in the details, see my JavaOne presentation on “HotSpot Under the Hood” https://speakerdeck.com/alblue/javaone-2016-hotspot-under-the-hood – AlBlue Jan 24 '21 at 09:52
  • thx for the follow-up. The null checks were calls to `getClass`, before `Objects::requireNonNull` existed. And where in the presentation do you touch the segfault? Or that was just a general note from your side? – Eugene Jan 24 '21 at 12:41
  • If you had `a.getClass()` then that would still cause a `NullPointerException` if the value of `a` was `null`. In any case, under the hood, the `getClass` is a single register load from an offset into the class structure, and there's no branching if the target is null; it's just a dereference to `objectaddress+0x8` (varies due to JVM version). See https://speakerdeck.com/alblue/javaone-2016-hotspot-under-the-hood?slide=24 where no 'if' branching is done in the assembly. Segfault slide is on https://speakerdeck.com/alblue/javaone-2016-hotspot-under-the-hood?slide=35 – AlBlue Jan 24 '21 at 12:49
  • I don't get your last point. [here is the relevant bug](https://bugs.openjdk.java.net/browse/JDK-8073550). And that is exactly what I was saying via "javac emits checks", because `.getClass` _were exactly these checks_, under a disguise. When you pointed me to the presentation, I hoped I would see some details for segfault and/or protected pages. EDIT: nvm, I see it now after your edit, I'll take a look. thx – Eugene Jan 24 '21 at 12:56
  • Those are calls in some places of the Java libraries that use 'obj.getClass' as a quick way (in Java) of testing for a null pointer. They aren't written by the compiler, they're injected by a user of writing the code. It's (logically) equivalent to `if obj == null throw NullPointerException else nop`. This is not the code that checks for null, it's a case of replacing `obj.getClass() -> Objects.requireNonNull(obj)` in the Java source. You can see this in https://github.com/openjdk/jdk/commit/ee906c96d7b38f313e192156abc49831934e9262 for example. – AlBlue Jan 24 '21 at 13:01
  • Look if you don't believe me, read Shiplev's post on implicit null checks here: https://shipilev.net/jvm/anatomy-quarks/25-implicit-null-checks/ -- he was the one who raised that bug, and did the commit that fixed it. – AlBlue Jan 24 '21 at 13:03
  • right. I already read that. there are still cases, where `javac` emits such byte-code. [here is one that I finally found](https://stackoverflow.com/questions/43115645/in-java-lambdas-why-is-getclass-called-on-a-captured-variable?noredirect=1&lq=1). It is not about me believing you or not, really; it's just trying to understand this. – Eugene Jan 24 '21 at 13:07
  • There are absolutely cases where javac emits such bytecode, such as when the Java programmers has put that in the source code. However the `obj.getClass()` itself is just a shorthand for the call that triggers the the null pointer exception itself, which is handled in the JVM through signals. The code for this is e.g. https://github.com/openjdk/jdk/blob/535c2927b6e0004ffa1ab5af3e19a622ab14d9f1/src/hotspot/os_cpu/linux_x86/os_linux_x86.cpp#L314 and other places. – AlBlue Jan 24 '21 at 13:17