实现接口的开销

Joa*_*nge 12 .net c# java performance interface

我的一位同事告诉我,实现接口会带来开销.这是真的?

我不关心微观优化; 我只是想知道这需要更深入的细节.

Bot*_*000 17

无法抗拒和测试它,看起来几乎没有开销.

参与者是:

Interface IFoo    defining a method
class Foo: IFoo   implements IFoo
class Bar         implements the same method as Foo, but no interface involved
Run Code Online (Sandbox Code Playgroud)

所以我定义了

Foo realfoo = new Foo();
IFoo ifoo = new Foo();
Bar bar =  new Bar();
Run Code Online (Sandbox Code Playgroud)

并调用该方法,该方法执行20个字符串连接,每个变量10,000,000次.

realfoo:   723 Milliseconds
ifoo:      732 Milliseconds
bar:       728 Milliseconds
Run Code Online (Sandbox Code Playgroud)

如果该方法什么都不做,那么实际的调用会更加突出.

  realfoo: 48 Milliseconds
  ifoo: 62 Milliseconds
  bar: 49 Milliseconds
Run Code Online (Sandbox Code Playgroud)

  • 应该注意,在接口中声明的方法是自动"虚拟"的.这意味着JIT编译器永远不会内联这样的方法调用.因此,如果使用单行方法实现接口,即使尝试使用"[MethodImpl(MethodImplOptions.AggressiveInlining)]"(C#),也总是会支付调用成本. (4认同)

Nei*_*fey 12

Java的角度来看,至少在Hotspot的最新版本中,答案通常很少或没有开销.

例如,假设您有一个如下方法:

public void doSomethingWith(CharSequence cs) {
  char ch = cs.charAt(0);
  ...
}
Run Code Online (Sandbox Code Playgroud)

CharSequence当然是一个界面.所以你可能会期望这个方法需要做额外的工作,检查什么对象类型,找到方法,以及最后搜索界面等等 - 基本上你可以想象的所有恐怖故事......

但实际上,VM可能比这更聪明.如果它确实在实践中你总是传递特定类型的对象,那么它不仅可以跳过对象类型检查,而且甚至可以内联该方法.例如,如果你调用一个方法,如一系列字符串的循环以上,热点实际上可以内联调用的charAt(),以便获得字符字面变成换句话说,一对夫妇MOV instructions--的,一对接口的方法调用甚至可以变成甚至根本没有方法调用.(PS此信息基于1.6更新12的调试版本的汇编输出.)


use*_*878 9

由于在调用方法或访问属性时执行了额外的间接,接口确实会产生开销.许多用于实现多态的系统(包括接口的实现)通常使用基于运行时类型映射函数调用的虚方法表.

理论上,编译器可以将虚函数调用优化为普通函数调用或内联代码,前提是编译器能够证明正在进行调用的对象的历史记录.

在绝大多数情况下,使用虚函数调用的好处远远超过了缺点.

  • 至少在Java中,所有函数调用都是虚拟的,所以我怀疑会有任何可衡量的开销. (3认同)

Ree*_*sey 6

我不关心微观优化,只是想知道这需要更深入的细节.

有开销,但它是微优化级别开销.例如,一个接口可以在IL切换中从呼叫到callvirt进行多次调用,但这非常小.

  • 在IL中,有两个不同的调用:用于调用方法的"call"和用于调用虚函数的"callvirt".Callvirt强制在CLR中进行虚方法表查找,因此它比"调用"函数慢(这就是为什么,密封一个类可以帮助微观选择级别的性能).使用接口使接口中的方法调用使用callvirt,有时当它们在技术上不"使用"callvirt时. (3认同)

tom*_*ich 5

是的,接口会产生开销。事实上,在逻辑和处理器之间添加的每一层都会增加开销。显然,您应该在汇编中编写所有内容,因为这是唯一不会产生开销的事情。GUI 也会产生开销,所以不要使用它们。

我是在开玩笑,但重点是你必须在清晰、可理解、可维护的代码和性能之间找到自己的平衡。对于 99.999%(当然是重复)的应用程序,只要您注意避免不必要地重复执行任何更昂贵的方法,您就永远不会达到需要使某些内容变得更难以维护的地步只是为了让它跑得更快。

  • 99.999...% == 100% (( http://en.wikipedia.org/wiki/.999 )) 所以你是说没有应用程序需要为了性能而牺牲可维护性。 (4认同)
  • 早期优化是万恶之源。在确定未优化的应用程序不符合规范之前,切勿进行优化 - 然后在“优化”之前和之后进行测试,以确保它满足规范(如果不满足,请将其回滚!)最后,保留未优化的应用程序那里的代码作为注释准确地说明了它是如何失败的,这样其他人就不会假设您不知道自己在做什么并对其进行去优化以提高可读性。 (3认同)