invokeinterface on sealed interfaces
mark.yagnatinsky at barclays.com
mark.yagnatinsky at barclays.com
Sat May 22 05:39:00 UTC 2021
I'm usually just a lurker on this list, and there are plenty of real experts here who can give you a more detailed answer, but let me try anyway.
This will be a two-part answer. In part 1, I will try to answer your question.
In part 2 I'll try to convince you that it's the wrong question: the secret to fast method calls lies elsewhere.
Actually, let me give a sneak preview of part 2, and then I'll begin part 1, and eventually return to part 2.
The sneak preview is just 3 lines:
Ask not how Java can make interface calls as fast as virtual calls, for virtual calls are too slow.
Nor ask how to make virtual calls as fast as static calls, for even static calls are too slow.
Instead, ask how to make interface calls as fast as no calls at all.
Okay, that's it for the sneak preview. Time for part 1.
An old but probably still accurate description of invoking through an interface is here:
I don't see anything about discarding tables if another class is found. (Maybe I missed it; I only skimmed it superficially.)
I haven't thought about it too much, but it seems sealing doesn't help much here.
What makes virtual calls so cheap is that each class has only one base class, but may implement many interfaces.
Sealed doesn't change this. (Again, I haven't thought too much; there may be a really neat trick that can make good use of sealed-ness.)
This concludes part 1 of my answer. Now we begin part 2.
There are two kinds of methods: big/expensive, and fast/cheap.
For example, if a method merely increments an integer field, it is fast and cheap.
If it makes an SQL query to a database halfway around the world, it is slow.
If a method is slow, then method invocation overhead doesn't matter: it's counting a few nanoseconds when many milliseconds are at stake.
On the other hand, for cheap methods, it matters a lot. And here, even a virtual call is too slow, let alone an interface call.
In fact, even a static call (using invokestatic or invokespecial) is still too slow!
And calling cheap methods through an interface is common in Java! For instance, suppose you have an ArrayList, and you want to loop through it using a for-each loop.
This involves getting the iterator from the list, and then calling next() and hasNext() and bunch of times.
Now obviously, Iterator is an interface that will never be sealed: that would defeat its point. But we still want this loop to run fast.
And note that we have a classic example of two very cheap methods: next() is basically "i++" and hasNext() is basically "i < n".
Call overhead should matter a lot here! Luckily, there's a trick that works quite well.
The JVM eventually notices "I've called next() on line 123 a few hundred times already, and every time it turned out to be a method on ArrayList.Itr".
It then makes a bet with itself: I bet that line 123 will keep acting the same way in the future, so I'm going to fire up the JIT compiler, and have it emit code like this:
If(iterator.getClass() == ArrayList.Iter.class) call the method directly
Else do the usual slow thing
If the class is what we expected, we pay only the price of a static call, not a virtual call.
But this is still too much. But now the solution is easy: since we have a static call, we can just inline it!
And once we do, that brings in more benefits! It allows the JIT compiler to treat the loop body as one unified whole.
Whereas before, it would have to worry "who knows what this mysterious hasNext() method might do? Maybe it does I/O.
Maybe it starts new threads. Maybe it writes to global variables. I'd better be careful to make sure the code I generate works no matter what crazy stuff this method does."
Whereas as after hasNext() is inlined directly into the loop, life is simpler: "this is just a simple test with no side-effects at all. I can optimize this aggressively with no fear."
And that's why the price of virtual calls doesn't matter too much: Java tries to make sure that you rarely have to pay for them if it matters.
From: liangchenblue at gmail.com <liangchenblue at gmail.com>
Sent: Friday, May 21, 2021 9:50 PM
To: jdk-dev at openjdk.java.net
Subject: invokeinterface on sealed interfaces
I have heard of a myth against using interfaces, namely on how invokeinterface is slower than invokevirtual since concrete class methods to call, with known indices in the method table, can be found more easily than interface methods, which always require a lookup by name.
Another myth I've heard is that JVM may decide to construct a temporary table for interface dispatches as well, but the table would be discarded once another class is discovered in the interface hierarchy and the performance would slow back down.
I wonder if these two claims are right; unfortunately, I am no good at the VM internals and can't verify them. But if they are true, I wonder if invokeinterface calls on sealed interface methods would enjoy a performance boost and have its performance brought closer to that of invokevirtual calls. It seems that the sealed class design not only provides clearer apis, but also allow the VM to better optimize code.
This message is for information purposes only, it is not a recommendation, advice, offer or solicitation to buy or sell a product or service nor an official confirmation of any transaction. It is directed at persons who are professionals and is not intended for retail customer use. Intended for recipient only. This message is subject to the terms at: www.barclays.com/emaildisclaimer.
For important disclosures, please see: www.barclays.com/salesandtradingdisclaimer regarding market commentary from Barclays Sales and/or Trading, who are active market participants; https://www.investmentbank.barclays.com/disclosures/barclays-global-markets-disclosures.html regarding our standard terms for the Investment Bank of Barclays where we trade with you in principal-to-principal wholesale markets transactions; and in respect of Barclays Research, including disclosures relating to specific issuers, please see http://publicresearch.barclays.com.
If you are incorporated or operating in Australia, please see https://www.home.barclays/disclosures/importantapacdisclosures.html for important disclosure.
How we use personal information see our privacy notice https://www.investmentbank.barclays.com/disclosures/personalinformationuse.html
More information about the jdk-dev