Key*_*mer 1 c# optimization virtual abstract sealed
我想知道以下代码是如何优化的.特别涉及虚拟和直接呼叫.我评论过我认为一切都是优化的,但这些只是猜测.
public abstract class Super
{
public abstract void Foo();
public void FooUser()
{
Foo();
}
}
public class Child1 : Super
{
public override void Foo()
{
//doSomething
}
}
public class SealedChild : Super
{
public override void Foo()
{
//doSomething
}
}
class Program
{
void main()
{
Child1 child1 = new Child1();
child1.Foo(); //Virtual call?
child1.FooUser(); //Direct call and then a virtual call.
SealedChild sealedChild = new SealedChild();
sealedChild.Foo(); //Direct call?
sealedChild.FooUser();
/* Two options: either a direct call & then a virtual call
* Or: direct call with a parameter that has a function pointer to Foo, and then a direct call to foo.
*/
Super super = child1;
super.Foo(); //Virtual call.
super.FooUser(); //Virtual call then direct call.
}
}
Run Code Online (Sandbox Code Playgroud)
编译器根本不进行任何类型的优化.它总是生成IL'callvirt'指令(调用虚拟).从理论上讲,运行时可以删除调用的虚拟部分,但我见过并尝试过的每个基准测试都表明情况并非如此.考虑到即使是完全静态的C++编译器也无法在琐碎的情况下执行此操作,JIT似乎不太可能将其拉下来.即使他们能够使其发挥作用,也有大量更常见的性能陷阱,他们可以花时间来代替.
哦,Eric Gunnerson撰写的这篇博文解释了为什么C#总能生成callvirt.
如果您在密封类上有虚方法,并且对象引用的类型是密封类,则可以避免虚拟调用.以下面的例子为例.没有实际的原因需要虚拟调用GetName,因为我们知道没有父类的子类,因此没有进一步的虚拟调度.
sealed class Parent : Child {
public override string GetName() { return "foo"; }
}
public void Test() {
var p = new Parent();
var name = p.GetName();
}
Run Code Online (Sandbox Code Playgroud)
编译器可以选择注意这一点并输出调用IL指令而不是callvirt.但是,C#和VB.Net编译器都选择不执行此优化.两者都会发出callvirt.
JIT也可以自由地进行这样的优化.它也选择不这样做.
但这并不意味着你不应该密封你的课程.除非你真的打算让别人继承,否则应该密封课程.否则,您将打开自己未能准确降低成本的方案.
此外,没有什么能阻止编译器和JIT在以后实现这一点.
| 归档时间: |
|
| 查看次数: |
1295 次 |
| 最近记录: |