本文介绍了传递接口和重载类之间的性能差异的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有一个类,并且在该表中的已知偏移量处找到指向所需方法实现的指针,因此跳转到目标非常简单.然而,虽然一个类只能扩展一个超类,但一个类可以实现任意数量的接口,因此通过接口调用方法更加复杂.对于接口调用,它必须首先查找类的接口列表以找到所需的接口,然后才能在该接口的表中查找方法实现.

当使用两个以上的实现时,性能开始出现分歧.

无论是使用类还是接口,多态调用都会导致CPU上的管道flush,因为CPU无法提前看到跳转的目标,这样成本很高.当调用站点在运行时已知为寡态(oligo 表示很少")时,性能会急剧提高,因为好的 JVM 会专门处理这些情况.对于单态情况,JVM 可以直接跳转到单个目标方法,甚至内联它.对于二态情况,它实现了 of(); 就像通过(无效语法): if (o.getClass() == A.class) A::f(o) elseB::f(o);.

实际上,我不确定为什么在您的基准测试中,二态情况似乎与单态情况一样快——CPU 的分支预测器不应该在随机数据上有一半的时间出错吗?也许还有其他微妙之处在起作用...

另见:

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.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).

解决方案

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.

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.

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:

这篇关于传递接口和重载类之间的性能差异的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

06-27 19:34