当前位置: 首页 > 编程语言 > C#

VirtualCallSpeedInC#vs.C++Share

时间:2023-04-11 02:34:01 C#

C#vs.C++VirtualCallSpeed我好像记得在某处看过,C#中virtualcalls的开销没有C+贵+这么高。这是真的?如果是这样-为什么?C#虚拟调用必须检查“this”是否为空,而C++虚拟调用则不需要。所以我通常不明白为什么C#虚拟调用会更快。在特殊情况下,C#编译器(或JIT编译器)可能能够比C++编译器更好地内联虚拟调用,因为C#编译器可以访问更好的类型信息。在C++中,调用方法指令有时会更慢,因为C#JIT可能能够使用更快的指令,并且只处理小的偏移量,因为它比C++编译器更了解运行时内存布局和处理器模型。然而,我们充其量只会讨论一些处理器指令。在现代超标量处理器上,“空检查”指令很可能与“调用方法”同时运行,因此不需要时间。如果在循环中调用make,则所有处理器指令可能已经在1级缓存中。但数据不太可能是缓存,如今从主存读取数据值的成本与从1级缓存运行100条指令的成本相同。因此,不幸的是,在实践中,虚拟通话的成本甚至可以在极少数地方进行衡量。C#代码使用更多指令这一事实当然会减少缓存中可以容纳的代码量,其效果是不可预测的。(如果C++类使用多个内在函数,成本更高,因为“this”指针必须打补丁。同样,C#中的接口添加了另一层重定向。)对于JIT-compiled语言(我不知道CLR做或不做,Sun的JVM做),将只有两个或三个实现的虚拟调用转换为一个类型和直接或内联的测试序列是常见的优化调用。这样做的好处是,现代流水线CPU可以使用分支预测和预取来进行直接调用,但间接调用(在高级语言中用函数指针表示)经常会导致流水线停顿。在极限情况下,只有一个虚拟调用的实现,并且调用主体足够小,虚拟调用减少为纯内联代码。这种技术用于自动语言运行时,JVM就是从中发展而来的。大多数C++编译器不执行执行此优化所需的全程序分析,但像LLVM这样的项目正在考虑像这样的全程序优化。原始问题:我似乎记得在某处读到过C#中虚拟调用的成本不如C++高。请注意这一点。换句话说,这个问题可以改写:我似乎记得在某处读过,在C#中,虚拟调用和非虚拟调用同样慢,而在C++中,虚拟调用比非虚拟调用慢......因此,在任何情况下都不会提问者声称C#比C++快。可能是无用的转移,但这激发了我对使用/clr:pure的C++的好奇心,而不使用C++/CLI扩展。编译器生成的IL由JIT转换为本机代码,即使它是纯C++。所以在这里我们可以看到如果在与C#相同的平台上运行,标准C++实现会做什么。使用非虚拟方法:structPlain{voidBar(){System::Console::WriteLine("hi");}};这段代码:Plain*p=newPlain();p->条形图();…导致使用指定的方法名称发出调用操作码,将隐式this参数传递给Bar。callvoid::Plain.Bar(valuetypePlain*)与继承层次比较:structBase{virtualvoidBar()=0;};structDerived:Base{voidBar(){System::Console::WriteLine("hi");}};如果我们这样做:Base*b=newDerived();b->条形图();这会发出calli操作码,它会跳转到计算出的地址——所以在调用之前有很多IL。我们可以通过将其重新打开到C#来查看发生了什么:**(*((int*)b))(b);换句话说,将b的地址转换为指向int的指针(刚好与相同的指针大小相同)并获取该位置的值,即vtable的地址,并取出第一项。vtable是要跳转到的地址,取消引用它并调用它,将隐式this参数传递给它。我们可以调整虚拟示例以使用C++/CLI扩展:refstructBase{virtualvoidBar()=0;};refstructDerived:Base{virtualvoidBar()override{System::Console::WriteLine("hi");}};基础^b=gcnew衍生();b->条形图();这会生成一个callvirt操作码,就像在C#中一样:对于标准的C++类层次结构,C++编译器生成的代码包含用于遍历vtable的硬编码逻辑,而对于ref类,它将由JIT来确定最佳实现。我猜这个假设是基于JIT编译器的,这意味着C#可能在实际使用之前将虚拟调用转换为简单的方法调用。但这主要是理论上的,我不会打赌!C++中虚调用的代价是通过指针(vtbl)调用函数的代价。我怀疑C#可以更快地做到这一点,并且仍然能够在运行时确定对象类型......编辑:正如PeteKirkham指出的那样,一个好的JIT可能能够内联C#调用,避免管道停顿;大多数C++连编译器都做不到的事情。另一方面,IanRingrose提到了对缓存使用的影响。除此之外,JIT本身正在运行,并且(严格地个人)我不会打扰,除非在实际工作负载下对目标机器进行分析证明比其他机器更快。充其量只是微优化。不确定完整的框架,但在CompactFramework中,它比较慢,因为CF没有虚拟调用表,尽管它确实缓存结果。这意味着CF中的虚拟调用在第一次调用时会比较慢,因为它必须进行手动查找。如果应用程序内存不足,则每次调用它时可能会很慢,因为缓存查找可能会被调整。在C#中,可以通过分析代码将虚函数转换为非虚函数。实际上,它不会经常改变。C#扁平化vtable并内联祖先调用,因此您无需链接继承层次结构来修复任何问题。这可能不是您问题的答案,但是虽然.NETJIT会像大家之前所说的那样优化虚拟调用,但VisualStudio2005和2008中的配置文件引导优化通过插入对最可能的目标函数的直接调用来实现虚拟调用大概是内联调用,所以权重可能是相同的。以上就是《C#学习教程:C#和C++中虚拟调用速度》的全部内容。如果对你有用,需要进一步了解C#学习教程,希望大家多多关注。本文收集自网络,并不代表侵权,如有侵权,请点击右边联系管理员删除。如需转载请注明出处: