传递接口和类重新加载之间的性能差异 [英] Performance difference between passing interface and class reloaded

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

问题描述

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


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


是否使用类或接口,多态调用导致CPU上的管道刷新,因为CPU无法提前看到跳转的目标,这具有很高的成本。当调用站点在运行时被称为多态( oligo 意思是'少')时,性能会急剧增加,因为一个好的JVM专门处理这些情况。对于单形情况,JVM可以直接跳转到单个目标方法,甚至可以内联它。对于二态情况,它实现 of(); ,如同(非有效语法): if(o.getClass()== A.class )A :: f(o)其他B :: 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).

解决方案

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:

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

登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆