C# 将方法/属性标记为虚拟的性能影响是什么?
问题如标题所述:将方法/属性标记为虚拟的性能影响是什么C# 将方法/属性标记为虚拟的性能影响是什么?,c#,performance,virtual,C#,Performance,Virtual,问题如标题所述:将方法/属性标记为虚拟的性能影响是什么 注意-我假设虚拟方法在普通情况下不会重载;我通常在这里使用基类。从您的标记中,您说的是c。我只能从德尔菲的角度来回答。我想也会是这样。(我在这里期待负面反馈:)) 静态方法将在编译时链接。虚拟方法需要在运行时进行查找以决定调用哪个方法,因此开销很小。只有当方法很小且经常调用时,它才有意义。与直接调用相比,虚拟函数的性能开销非常小。在较低的级别上,基本上是通过数组查找来获取函数指针,然后通过函数指针进行调用。现代CPU甚至可以在其分支预测器中
注意-我假设虚拟方法在普通情况下不会重载;我通常在这里使用基类。从您的标记中,您说的是c。我只能从德尔菲的角度来回答。我想也会是这样。(我在这里期待负面反馈:))
静态方法将在编译时链接。虚拟方法需要在运行时进行查找以决定调用哪个方法,因此开销很小。只有当方法很小且经常调用时,它才有意义。与直接调用相比,虚拟函数的性能开销非常小。在较低的级别上,基本上是通过数组查找来获取函数指针,然后通过函数指针进行调用。现代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语言。)除了内联本身带来的加速外,内联还支持其他一些优化,例如常量折叠,因为调用方可以知道被调用方在内部是如何工作的。对于大到无论如何都不能内联的函数,性能影响可能可以忽略不计。对于可能是内联的非常小的函数,这时您需要注意虚拟函数
编辑:需要记住的另一件事是,所有程序都需要流控制,而这永远不是免费的。什么将取代你的虚拟功能?转换语句?一系列if语句?这些仍然是不可预测的分支。此外,给定一个N路分支,一系列if语句将在O(N)中找到正确的路径,而虚函数将在O(1)中找到它。switch语句可以是O(N)或O(1),这取决于它是否优化为跳转表。在桌面端,不管方法是否重载,它们都会通过方法指针表(虚拟方法表)产生额外级别的间接寻址,这意味着在方法调用之前,大约有2个额外的内存通过间接读取来比较非密封类和非final方法上的非虚方法 [一个有趣的事实是,在compact framework 1.0版上,开销更大,因为它不使用虚拟方法表,而只是通过反射来发现调用虚拟方法时要执行的正确方法。] 此外,与非虚拟方法相比,虚拟方法不太可能成为内联或其他优化(如尾部调用)的候选方法 这大致是方法调用的性能层次结构: 非虚拟方法<虚拟方法<接口方法(在类上)<委托分派
但是,除非您通过测量来证明,否则各种调度机制的这些性能影响都无关紧要(即使这样,架构含义、可读性等也可能对选择方法有很大的影响)通常,虚拟方法只需通过一个函数指针表即可到达实际方法。这意味着一次额外的解引用和一次到内存的往返 虽然成本并非绝对为零,但却是极其微小的。 如果它有助于你的程序拥有虚拟功能,那么一定要这样做
拥有一个设计良好、性能影响非常小的程序要比一个笨拙的程序好得多,只是为了避免v表。很难说清楚,因为.NET JIT编译器可能能够在某些(许多?)情况下优化开销 但如果它没有优化它,我们基本上是在讨论一个额外的间接指针 也就是说,当调用非虚拟方法时,必须