密封类真的提供性能好处吗?
我遇到了很多优化提示,说你应该把你的课程标记为密封以获得额外的性能好处。
我跑了一些测试来检查性能差异,发现没有。 难道我做错了什么? 我是否错过了密封课程会带来更好结果的情况?
有没有人运行测试,看到了不同?
帮我学习:)
JITter有时会对密封类中的方法使用非虚拟调用,因为它们无法进一步扩展。
关于调用类型有很复杂的规则,虚拟/非虚拟的,我不知道它们,所以我不能真正为你勾画它们,但是如果你是谷歌的密封类和虚拟方法,你可能会找到关于这个主题的一些文章。
请注意,从优化级别获得的任何类型的性能优势都应视为最后的手段,在优化代码级别之前,始终优化算法级别。
这里有一个链接提到这一点:在密封关键字上进行Rambling
答案是否定的,密封课的表现并不比密封好。
这个问题归结为call
VS callvirt
IL操作码。 Call
比callvirt
快, callvirt
主要用于你不知道对象是否已被子类化的情况。 所以人们认为,如果你封闭一个班级,所有的操作代码将从calvirts
变为calls
并且会更快。
不幸的是, callvirt
做其他一些有用的事情,例如检查空引用。 这意味着即使一个类是封闭的,引用可能仍然是空的,因此需要一个callvirt
。 你可以解决这个问题(不需要封闭课程),但是它变得有点毫无意义。
结构使用call
因为它们不能被子类化,也不能为空。
看到这个问题的更多信息:
呼叫和callvirt
据我所知,没有性能优势的保证。 但是在密封方法的某些特定条件下 , 有机会降低性能损失 。 (密封的课程使所有的方法被密封。)
但这取决于编译器的实现和执行环境。
细节
许多现代CPU使用长流水线结构来提高性能。 由于CPU速度比内存快得多,因此CPU必须从内存中预取代码以加速流水线。 如果代码在适当的时候没有准备好,管道将空闲。
动态调度有一个很大的障碍,它扰乱了这种“预取”优化。 你可以理解这只是一个条件分支。
// Value of `v` is unknown,
// and can be resolved only at runtime.
// CPU cannot know code to prefetch,
// so just prefetch one of a() or b().
// This is *speculative execution*.
int v = random();
if (v==1) a();
else b();
在这种情况下,CPU不能预取下一个要执行的代码,因为在条件解决之前下一个代码位置是未知的。 所以这会导致管道闲置的危险。 经常性闲置的性能损失是巨大的。
类似的事情发生在方法覆盖的情况下。 编译器可能会确定正确的方法覆盖当前方法调用,但有时不可能。 在这种情况下,只能在运行时确定适当的方法。 这也是动态分派的一种情况,动态类型语言的主要原因通常比静态类型语言慢。
有些CPU(包括最近英特尔的x86芯片)甚至使用称为投机执行的技术来利用管道。 只需预取一条执行路径即可。 但这种技术的命中率并不高。 投机失败导致管道失速,这也造成巨大的性能损失。 (这完全是由CPU实现的,一些移动CPU被称为不节能的这种优化)
基本上,C#是一种静态编译的语言。 但不总是。 我不知道确切的情况,这完全取决于编译器的实现。 如果方法被标记为sealed
某些编译器可以通过防止重写方法来消除动态分派的可能性。 愚蠢的编译器可能不会。 这是sealed
的性能优势。
这个答案(为什么处理一个有序数组比一个未排序的数组更快?)正在更好地描述分支预测。
链接地址: http://www.djcxy.com/p/5379.html