当前位置:  开发笔记 > 编程语言 > 正文

将方法/属性标记为虚拟的性能影响是什么?

如何解决《将方法/属性标记为虚拟的性能影响是什么?》经验,为你挑选了3个好方法。

问题如标题所述:将方法/属性标记为虚拟的性能影响是什么?

注意 - 我假设虚拟方法在常见情况下不会过载; 我通常会在这里使用基类.



1> dsimcha..:

与直接调用相比,虚拟功能仅具有非常小的性能开销.在较低级别,您基本上是在查看数组查找以获取函数指针,然后通过函数指针进行调用.现代CPU甚至可以在其分支预测器中合理地预测间接函数调用,因此它们通常不会太严重地损害现代CPU流水线.在汇编级别,虚函数调用转换为类似以下内容,其中I是任意立即值.

MOV EAX, [EBP + I] ; Move pointer to class instance into register
MOV EBX, [EAX] ;  Move vtbl pointer into register.
CALL [EBX + I]  ;   Call function

比.以下为直接函数调用:

CALL I  ;  Call function directly

真正的开销是因为大多数情况下虚拟函数无法内联.(它们可以是JIT语言,如果VM意识到它们无论如何总是会转到同一个地址.)除了你通过内联获得的加速,内联还可以实现其他几种优化,例如常量折叠,因为调用者可以知道被调用者是怎样的内部工作.对于任何大到不能内联的函数,性能损失可能微不足道.对于可能内联的非常小的函数,当您需要注意虚函数时.

编辑:要记住的另一件事是所有程序都需要流量控制,这绝不是免费的.什么会取代你的虚拟功能?转换声明?一系列if语句?这些仍然是可能无法预测的分支.此外,给定N路分支,一系列if语句将在O(N)中找到正确的路径,而虚函数将在O(1)中找到它.switch语句可以是O(N)或O(1),这取决于它是否针对跳转表进行了优化.


我在一段时间内看到的信息量最大的帖子之一,我手边都不知道,但仍然很容易理解.

2> Jon Limjap..:

Rico Mariani在他的Performance Tidbits博客中概述了有关性能的问题,他说:

虚方法: 直接调用时,您是否使用虚方法?很多时候,人们使用虚拟方法来实现未来的可扩展性.可扩展性是一件好事,但它确实付出了代价 - 确保您的完整可扩展性故事得到解决,并且您对虚拟功能的使用实际上将使您到达您需要的位置.例如,有时人们会考虑调用站点问题,但是不考虑如何创建"扩展"对象.后来他们意识到(大部分)虚拟功能根本没有帮助,他们需要一个完全不同的模型来将"扩展"对象引入系统.

密封:密封可以是将类的多态性限制为仅需要多态性的站点的方法.如果您将完全控制类型,那么密封对于性能来说是一件好事,因为它可以实现直接调用和内联.

基本上反对虚方法的论点是它不允许代码成为内联的候选者,而不是直接调用.

在MSDN文章" 提高.NET应用程序性能和可伸缩性"中,进一步阐述了这一点:

考虑虚拟会员的权衡

使用虚拟成员提供可扩展性.如果您不需要扩展类设计,请避免使用虚拟成员,因为由于虚拟表查找而调用它们的成本更高,并且它们会使某些运行时性能优化失败.例如,编译器无法内联虚拟成员.此外,当您允许子类型化时,您实际上向消费者提供了一份非常复杂的合同,并且当您将来尝试升级您的课程时,您不可避免地会遇到版本问题.

然而,对上述内容的批评来自TDD/BDD阵营(他们希望方法默认为虚拟),认为性能影响无论如何都可以忽略不计,特别是当我们可以访问速度更快的机器时.



3> abelenky..:

通常,虚拟方法只需通过一个函数表指针即可到达实际方法.这意味着一次额外的解除引用和一次往返内存.

虽然成本并非绝对零,但它非常小.如果它可以帮助您的程序拥有虚拟功能,那么一定要做到这一点.

为了避免使用v-table,最好是拥有一个精心设计的程序,只需要很小的微小的性能,而不是一个笨拙的程序.


虚函数调用的最大成本不是来自vtable的指针加载,而是由错误预测的分支导致的管道清除(并且vjump通常是错误预测的).这可以和管道本身一样长.对于非常频繁调用的函数,它会累加起来.
该问题中的大多数答案都是错误的,尤其是对于有序处理器,因为间接分支通常会被错误地预测。
推荐阅读
云聪京初瑞子_617
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有