托管代码可以像unmamanged一样快地执行计算吗?

时间:2011-06-01 22:54:36

标签: c# c++ performance unmanaged managed

我最近对不同的国际象棋引擎感兴趣。该领域有许多开放和封闭的资源项目。他们都是(大部分都是)用C / C ++编写的。这是一个显而易见的事情 - 你有一个计算密集型的任务,你使用C / C ++,所以你同时获得了可移植性和速度。这似乎是一个没有脑子的人。

但是,我想质疑这个想法。当.NET首次出现时,有很多人说.NET的想法不会起作用,因为.NET程序注定要超慢。实际上,这并没有发生。有人在VM,JIT等方面做得很好,现在我们在大多数任务中都有不错的表现。但不是所有的。微软从未承诺.NET适用于所有任务,并承认某些任务仍然需要C / C ++。

回到计算繁重任务的问题 - 有没有办法编写.NET程序,以便它不会使用相同的算法执行比非托管代码更糟糕的计算?我对“持续”的速度损失感到满意,但是比这更糟糕的事情将成为一个问题。

你怎么看?我们能否接近托管代码中计算的非托管代码,或者非托管代码是唯一可行的答案?如果我们可以,怎么样?如果我们不能为什么?

更新:这里有很多好的反馈。我将接受最多投票的答案。

7 个答案:

答案 0 :(得分:6)

“可以”吗?当然是。即使没有不安全/无法验证的代码,经过充分优化的.NET代码也可以胜过本机代码。例如,Jon Harrop博士在这个主题上的答案:F# performance in scientific computing

是的吗?通常,不,除非你以强烈的方式以避免分配。

答案 1 :(得分:2)

简单的答案是否定的。对于下面的一些评论者:大多数时候它会变慢,而不是总是如此。 计算密集型应用程序,其中每毫秒计数仍将使用非托管语言(如C,C ++)编写。收集时GC会减慢很多。

例如,没有人在C#或XNA中编写3D引擎。有一些,但没有什么接近CryEngine或虚幻。

答案 2 :(得分:2)

.NET不是超级超级慢 - 但它也不是与母语相同的领域。速度差异是您可以轻松应对的商业应用程序,它更喜欢安全性和更短的开发周期。如果你没有在CPU上使用每个循环,那么你使用的数量并不重要,事实是许多甚至大多数应用程序根本不需要那种性能。但是,当需要这种性能时,.NET将不会提供它。

更重要的是,它不够可控。在C ++中,您可以销毁每个资源,管理每个分配。当你真的不想这样做时,这是一个很大的负担 - 但是当你需要增加微调每个分配的性能时,它是不可能的。

要考虑的另一件事是编译器。我的意思是,JIT可以访问有关程序和目标CPU的更多信息。但是, 每次必须从头开始重新编译,并且在比C ++编译器更远的时间限制下这样做,本身就限制了它的能力。 CLR语义,如每次对每个对象的堆分配,也从根本上限制了它的性能。托管的GC分配速度很快,但它没有堆栈分配,更重要的是,没有分配。

编辑:当然,.NET附带了与大多数本地语言不同的内存控制范例,这意味着对于特别适合垃圾收集的应用程序,.NET代码可能比本机代码运行得更快。但是,这与托管代码与本机代码无关,只是为正确的作业选择正确的算法,并不意味着从本机代码中使用的等效GC算法不会更快。

答案 3 :(得分:1)

简短的回答是的,有足够的工作来回答。

有一些用托管C#.NET编写的高频交易应用程序。很少有其他应用程序像交易引擎那样接近时间关键性。总体概念是您开发的软件非常高效,您的应用程序将不需要垃圾收集器为非第0代对象调用自身。如果垃圾收集器在任何时候启动,你就会有大量(在计算时间上)延迟数十或数百毫秒,这是不可接受的。

答案 4 :(得分:0)

您可以使用unsafe和指针获取“原始”内存访问权限,这可以显着提高您的速度,但代价是对您的内容负有更多责任(请记住pin your objects)。那时,你只是换字节。

垃圾收集压力可能是一个有趣的事情,但也有一些策略(对象池)。

答案 5 :(得分:0)

这似乎是一个过于宽泛的问题。

有一些无偿的提示:是的,您可以使用unsafeunchecked,Structs数组,最重要的是C ++ / CLI。

C ++的内联,编译时模板扩展(以及同上优化)等永远不会匹配。

但最重要的是:它取决于问题。无论如何 computations。 Mono有很好的扩展使用SIMD指令,在Win32上你必须本地才能获得这些指令。 Interop是作弊。

根据我的经验,移植玩具项目(例如解析器和国际象棋引擎)将导致至少一个数量级的速度差异,无论你做多少优化.NET方面的东西。我认为这主要与堆管理和服务例程(System.String,System.IO)有关。

.NET中可能会有很大的陷阱(过度使用Linq,lambdas,不小心依赖Enum.HasFlag执行像按位操作......)等等YMMV并谨慎选择你的武器

答案 6 :(得分:0)

通常,托管代码与编译代码相比至少会有一些速度损失,与代码大小成比例。当VM首次JIT编译代码时,这种损失就会出现。假设JIT编译器和普通编译器一样好,那么代码将执行相同的操作。

但是,根据它的编写方式,甚至可能JIT编译器的性能优于普通编译器。 JIT编译器比普通编译器知道更多关于目标平台的东西 - 它知道什么代码是“热”的,它可以缓存已证实的纯函数的结果,它知道目标平台支持哪些指令集扩展等,而取决于编译器,以及您(以及您的应用程序)允许的特殊程度,编译器可能无法进行优化。

老实说,这完全取决于您的申请。特别是像国际象棋引擎这样的算法,JIT编译语言中的代码可能会遵循预期的语义和常规模式,可能比C / C ++中的等效语言运行得更快。

写点东西并测试一下!