Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Performance difference between passing interface and class reloaded

There's a consensus that using interfaces is better than using classes. I surely agree: a library method accepting ArrayList instead of List would be a crap.

There's also a consensus that the performance is always the same. Here my benchmark begs to differ. results There are 1 to 4 implementations of both an interface and an abstract class. When more than two implementations get used, the performance starts to diverge. I'm looking for an explanation for this behavior (and also for the origin of the false consensus).

like image 456
maaartinus Avatar asked Feb 06 '14 16:02

maaartinus


1 Answers

There's a consensus that using interfaces is better than using classes.

This is overly simplistic. Both interfaces and abstract classes have advantages over each other.

The answer you link to suggests declaring variables as java.util.List, rather than java.util.ArrayList where possible. It's correct that using List gives you more flexibility to choose a different implementation class later, and thus is a good thing to do when you don't need ArrayList-specific methods (e.g., .trimToCapacity()). However, this advice has nothing to do with interfaces or classes in general, and would be just as true if java.util.List were an abstract class.

There's also a consensus that the performance is always the same.

The popular advice is that one should not worry about performance differences between classes and interfaces, and should choose between them based on good programming principles instead. This is good advice to prevent programmers fretting about unimportant performance differences; however it is sometimes misunderstood to imply that there is no difference, which is not true. There is a small difference: classes are faster.

With the method call through a class, there is a vtable at a fixed offset in the class, and the pointer to the desired method implementation is found at a known offset within that table, so the jump to the target is quite simple. However, while a class can extend only one superclass, a class can implement any number of interfaces, so method calls through an interface are more complicated. For interface calls, it has to look up the class's list of interfaces first to find the interface that's wanted, before it can look up the method implementation in that interface's table.

When more than two implementations get used, the performance starts to diverge.

Whether classes or interfaces are used, the polymorphic call causes a pipeline flush on the CPU, because the CPU can't see the target of the jump in advance, and this has a high cost. When the call site is known at run time to be oligomorphic (oligo meaning 'few'), performance increases sharply because a good JVM handles these cases specially. For the monomorphic case the JVM can jump directly to the single target method, or even inline it. For the dimorphic case it implements o.f(); as if by (not valid syntax): if (o.getClass() == A.class) A::f(o) else B::f(o);.

Actually I'm not sure why the dimorphic case seems to be as fast in your benchmark as the monomorphic case – shouldn't the CPU's branch predictor get it wrong half the time on random data? Perhaps there's other subtleties at work there...

See also:

  • https://wiki.openjdk.java.net/display/HotSpot/VirtualCalls
  • https://wiki.openjdk.java.net/display/HotSpot/InterfaceCalls
like image 64
Boann Avatar answered Oct 30 '22 07:10

Boann