C ++比C#快多少?


245

还是现在相反?

据我所知,在某些领域C#被证明比C ++更快,但是我从来没有胆量亲自测试它。

以为你们中的任何人都可以详细解释这些差异,或者将我指向正确的位置以获取有关此信息。


7
受保护,以防止发布更多随机基准。如果您认为自己可以胜任,则需要10名代表。
罗伯特·哈维

2
无论如何,这是不是不会因为意见/争论而关闭?我还不是在StackOverflow吗?(不是暗示结局,只是出于好奇。我喜欢那些激起自以为是的论点)
Bill K

1
鉴于我们生活在一个可以将IL转换为CPP并从那里进行优化的时代,这几乎是一个有争议的问题:docs.unity3d.com/Manual/IL2CPP.html
pixelpax

一种检查超出范围的数组访问权限的语言永远不会胜过没有访问范围的语言。
Seva Alekseyev

@SevaAlekseyev执行此操作的不是语言,而是编译器。C ++快速(除了显而易见的原因)的原因之一是C ++编译器已经存在了35年(甚至更多)。没有什么可以阻止C#编译器随着时间的推移而变得更好。对于你提到的情况,请仔细阅读本stackoverflow.com/questions/16713076/...
陷阱

Answers:


342

没有严格的理由说明为什么具有JIT的基于字节码的语言(如C#或Java)不能与C ++代码一样快。但是,C ++代码在很长一段时间内过去一直明显更快,并且在许多情况下今天仍然如此。这主要是由于实施更高级的JIT优化非常复杂,而真正酷的优化才刚刚出现。

因此,在许多情况下,C ++速度更快。但这只是答案的一部分。C ++实际上更快的情况是高度优化的程序,专家程序员在其中彻底地从代码中优化了地狱。这不仅非常耗时(并因此很昂贵),而且通常会由于过度优化而导致错误。

另一方面,在以后的运行时版本(.NET CLR或Java VM)中,解释语言的代码变得更快,而无需执行任何操作。JIT编译器可以进行很多有用的优化,而这些优化在带有指针的语言中是根本不可能的。另外,有人认为垃圾回收通常应与手动内存管理一样快或更快,在许多情况下也是如此。通常,您可以使用C ++或C来实现并实现所有这些功能,但是它将变得更加复杂且容易出错。

正如Donald Knuth所说:“过早的优化是万恶之源”。如果您确实确定您的应用程序将主要由性能非常关键的算法组成,并且将成为瓶颈,并且在C ++中肯定会更快,并且您可以肯定C ++不会与其他程序冲突要求,请使用C ++。在任何其他情况下,请集中精力首先以最适合您的语言正确地实现应用程序,然后在运行速度太慢时发现性能瓶颈,然后考虑如何优化代码。在最坏的情况下,您可能需要通过外部函数接口调出C代码,因此您仍然可以用较低级的语言编写关键部分。

请记住,优化正确的程序相对容易,但是校正优化的程序要困难得多。

不可能给出实际的速度优势百分比,这在很大程度上取决于您的代码。在许多情况下,编程语言的实现甚至不是瓶颈。持怀疑态度的是http://benchmarksgame.alioth.debian.org/上的基准测试,因为这些测试代码很大程度上测试了算术代码,这很可能与您的代码根本不相似。


92
解释语言的<quote>代码在运行时的更高版本中变得更快</ quote>随着更好版本的编译器所编译的代码也将变得更快。
马丁·约克

47
实际上,至少有一个原因:JIT必须快速,并且不能花时间在C ++编译器可用的各种高级优化上。
Nemanja Trifunovic

63
“但由于过度优化,通常也会导致错误。” [迫切需要引用]。我在国家实验室工作,我们从代码中优化地狱。这通常不会导致错误代码。
托德·盖布林

35
优化一个正确的程序相对容易,但是更正一个优化的程序要困难得多。
gradbot

20
英格(Inge):不确定自己在正确的轨道上。是的,C#是用另一种语言实现的,但是JIT编译器正在生成机器代码,因此它不是解释型语言。因此,它不受其C ++实现的限制。我不确定您为什么会认为在某些内容上添加一些经理会使其更快。
Martin Probst 2013年

202

C#可能不会更快,但是它会使YOU / ME更快。这是我所做的最重要的衡量。:)


68
哈哈,拉里·沃尔(Larry Wall)在这个话题上有很好的引用。他说的是Perl,但是在涉及语言和性能的所有讨论中都可以想到:“ ..早期的计算机语言,例如Fortran和C,旨在有效利用昂贵的计算机硬件。相反,Perl旨在有效利用昂贵的计算机程序员”
Falaina

60
1.“ C#比C ++快得多” 2.“不能正确” 1.“确定可以” 2.“多少钱?” 1.“通常在3-4个月前”
Dmitry S.

2
对于真正取决于您使用的库的C ++,C#通常不会更快,.NET则在您谈论生产率时
Ion Todirel,2016年

这就是为什么您可以使用Python而不是C编写一些代码的原因相同……但是在创建了一些繁重的计算之后,您可以感受到性能的差异。
Ch3shire '17

这取决于您的习惯。我在C ++中编程比在C#中编程更快。图书馆知识是其中很大一部分,因为您不想为基本任务重新发明轮子。C / C ++的主要问题是指针管理,这在智能指针方面已得到很大解决。话虽这么说,C ++严重缺乏.NET和Java提供的扩展库,但可以大大加快开发速度。这将不会很快解决,因为那些标准男孩喜欢花时间在模板改进而不是库扩展上。
gast128

87

快了五个橙子。或更确切地说:不可能有(正确)一揽子答案。C ++是一种静态编译的语言(但随后,也进行了配置文件引导的优化),C#在JIT编译器的帮助下运行。差异如此之大,以至于甚至无法给出数量级的问题,都无法回答“更快”的问题。


177
您是否有任何证据支持您对五个橙子的要求?我的实验最多都指向2个橙子,在进行模板元编程时,芒果改进了3个。
亚历克斯(Alex)2010年

42
在酵母发酵中,他并没有要求加快速度。
克里斯,2010年

11
根据我的经验,它是5.2橙。但这取决于您使用的水果表。
Dio F

4
更新,StackOverflow本身搞砸了,并且处理注释的效率低下,从而减少了香蕉的数量(比应有的水平降低了300 根香蕉
KeksArmee

87

首先,我将不同意对此问题的部分已接受(且被充分赞成)的回答,并指出:

实际上,有很多原因导致JITted代码的运行速度比经过适当优化的C ++(或其他没有运行时开销的语言)程序要慢,其中包括:

  • 根据定义,在运行时花在JITting代码上的计算周期无法在程序执行中使用。

  • JITter中的任何热路径都将与您的代码竞争CPU中的指令和数据缓存。我们知道,就性能而言,缓存占主导地位,按照定义,像C ++这样的本地语言不具有这种类型的争用。

  • 运行时优化的时间预算必然是比的更多的限制编译时优化器(如另一个评论指出)

底线:最终,几乎可以肯定,您能够在C ++中创建比在C#中更快的实现

如此说来,到底有多快,实际上是无法量化的,因为变量太多了:任务,问题领域,硬件,实现质量以及许多其他因素。您将针对您的方案进行测试,以确定性能差异,然后确定是否值得付出额外的努力和复杂性。

这是一个非常漫长而复杂的主题,但是为了完整起见,我觉得值得一提的是C#的运行时优化器非常出色,并且能够在运行时执行某些动态优化,而这些动态优化是C ++编译时无法使用的(静态)优化器。即便如此,该优势通常仍在本机应用程序的深处,但是动态优化器是上面给出“ 几乎肯定”限定符的原因。

--

在相对性能方面,我也对在其他答案中看到的图表和讨论感到不安,因此我认为自己会很高兴,同时也为我在上面的陈述提供了支持。

这些基准测试问题的很大一部分是,您无法像编写C#一样编写C ++代码并期望获得代表性的结果(例如,在C ++中执行数千个内存分配会给您带来可怕的数字)。

相反,我写了一些更惯用的C ++代码,并与提供的C#代码@Wiory进行了比较。我对C ++代码进行的两个主要更改是:

1)使用的向量:: reserve()

2)将2d数组展平为1d,以实现更好的缓存位置(连续块)

C#(.NET 4.6.1)

private static void TestArray()
{
    const int rows = 5000;
    const int columns = 9000;
    DateTime t1 = System.DateTime.Now;
    double[][] arr = new double[rows][];
    for (int i = 0; i < rows; i++)
        arr[i] = new double[columns];
    DateTime t2 = System.DateTime.Now;

    Console.WriteLine(t2 - t1);

    t1 = System.DateTime.Now;
    for (int i = 0; i < rows; i++)
        for (int j = 0; j < columns; j++)
            arr[i][j] = i;
    t2 = System.DateTime.Now;

    Console.WriteLine(t2 - t1);
}

运行时间(发布):初始化:124ms,填充:165ms

C ++ 14(Clang v3.8 / C2)

#include <iostream>
#include <vector>

auto TestSuite::ColMajorArray()
{
    constexpr size_t ROWS = 5000;
    constexpr size_t COLS = 9000;

    auto initStart = std::chrono::steady_clock::now();

    auto arr = std::vector<double>();
    arr.reserve(ROWS * COLS);

    auto initFinish = std::chrono::steady_clock::now();
    auto initTime = std::chrono::duration_cast<std::chrono::microseconds>(initFinish - initStart);

    auto fillStart = std::chrono::steady_clock::now();

    for(auto i = 0, r = 0; r < ROWS; ++r)
    {
        for (auto c = 0; c < COLS; ++c)
        {
            arr[i++] = static_cast<double>(r * c);
        }
    }

    auto fillFinish = std::chrono::steady_clock::now();
    auto fillTime = std::chrono::duration_cast<std::chrono::milliseconds>(fillFinish - fillStart);

    return std::make_pair(initTime, fillTime);
}

运行时间(发布):初始化:398µs(是的,这是微秒),填充:152ms

总运行时间:C#:289毫秒,C ++ 152毫秒(快90%)

观察结果

  • 将C#实现更改为相同的1d数组实现会产生Init:40ms,Fill:171ms,Total:211ms(C ++的速度仍快40%)。

  • 用C ++设计和编写“快速”代码比用两种语言编写“常规”代码要困难得多。

  • 在C ++中很容易获得糟糕的性能(也许)。我们看到了无保留向量的性能。这样有很多陷阱。

  • 当您考虑运行时发生的所有事情时,C#的性能非常出色。而且该性能相对容易获得。

  • 比较C ++和C#性能的更多轶事数据:https : //benchmarksgame.alioth.debian.org/u64q/compare.php?lang=gpp&lang2=csharpcore

最重要的是,C ++使您可以更好地控制性能。您要使用指针吗?参考?堆栈内存?堆?动态多态还是通过静态多态(通过模板/ CRTP)消除了vtable的运行时开销?在C ++中,你必须......呃,使所有这些选择(及以上)自己,最好让你的解决方案最好地处理问题,你的抢断。

问问自己自己是否真正想要或需要该控件,因为即使对于上述琐碎的示例,您也可以看到,尽管性能有了显着提高,但仍需要更深层次的访问投入。


16
@Quonux感谢您的评论。当然,这不是一个“真实程序”。基准测试的重点是重构此页其他地方提供的C#基准测试,以证明JITted代码比本地速度更快(事实并非如此),并且基准测试可能会误导新人。
U007D

9
@Quonux为什么要这样写?是像您这样的人使我不喜欢stackoverflow。
Markus Knappen Johansson

5
@MarkusKnappenJohansson我今天过得很糟糕;),我也只是一个人,删除了我的反对意见,但我的看法仍然适用。哦,请不要仅仅因为有些“愚蠢”的人而讨厌:)。有一个不错的。
Quonux

9
完全误判基准。在C ++版本中,您只是保留一部分内存(然后使该操作花费微秒才能执行)。在C#版本中,您将创建5000个阵列(实例化内存中的对象)。C ++比C#更快...但是相差不到40%...现在它的范围更是小于10%。您的示例说明,程序员应该坚持使用自己选择的语言(从您的个人资料中可以明显看出您是职业C ++程序员)。在C#中,您可以执行2D数组int[,]...下面是示例。
nikib3ro

3
据我所知,您的C ++示例中的代码实际上只是提前分配内存。正确的C#实现只需编写“ List <double> arrs = new List <double>(ROWS * COLS)”,即可分配以一维格式索引二维数组所需的内存(例如,您在C ++中所做的工作)。绝对没有理由分配二维数组并手动对其进行展平-预测试中的大量迭代是性能低下的原因。我认为C#的开销仍然会更多,但不会很多。
JDSweetBeat '18

62

根据我的经验(并且我在两种语言上都有很多工作),与C ++相比,C#的主要问题是高内存消耗,而且我还没有找到控制它的好方法。最终将使.NET软件变慢的是内存消耗。

另一个因素是,JIT编译器不能花太多时间进行高级优化,因为它在运行时运行,如果花费太多时间,最终用户会注意到它。另一方面,C ++编译器始终需要在编译时进行优化。这个因素远不及内存消耗恕我直言。


6
在一个工作中的项目中,我们必须挖掘大量的数据,包括同时在内存中保存许多GB,并对所有数据进行昂贵的计算-这需要对所有分配的精确控制,C ++几乎是唯一的选择。C ++ +1。另一方面,那只是一个项目,我们花费了大部分时间来编写与慢速模拟器交互的系统,并且调试可能是一场噩梦,所以我希望我们可以将程序员时间优化语言用于所有其他方面。东西。
Bogatyr

7
@IngeHenriksen:我非常了解Dispose模式,但是它根本对托管内存没有帮助。
Nemanja Trifunovic

10
@IngeHenriksen处理它仅确保已调用Dispose方法。处置永远不会释放垃圾回收的内存。Dispose方法仅用于清理非托管资源(例如文件句柄),与内存管理无关。
doug65536

1
@NemanjaTrifunovic:“ JIT编译器不能花太多时间进行高级优化”。您能否列举一些JIT无法完成的优化,因为它们会花费太长时间?
乔恩·哈罗普

5
@ user3800527:即使添加RAM始终是可行的(并且不是-想象Microsoft向每个MS Office用户添加RAM)也无法解决问题。内存是分层的,与C ++相比,C#程序将有更多的缓存未命中。
Nemanja Trifunovic

35

当可以在编译时预先确定多态决策时,就会发生C ++仍占上风(并且将持续数年)的一种特殊情况。

通常,封装和延迟决策是一件好事,因为它使代码更具动态性,更易于适应不断变化的需求并且更易于用作框架。这就是为什么用C#进行面向对象编程的生产率很高,并且可以用术语“泛化”将其概括的原因。不幸的是,这种特殊的归纳是在运行时付出代价的。

通常,此成本不算什么,但是在某些应用程序中,虚拟方法调用和对象创建的开销可能有所不同(尤其是因为虚拟方法阻止了其他优化,例如方法调用内联)。这是C ++的巨大优势,因为您可以使用模板来实现另一种类型的泛化,这对运行时没有影响,但不一定比OOP的多态性小。实际上,仅使用模板技术和编译时分辨率就可以对构成OOP的所有机制进行建模。

在这种情况下(当然,它们通常仅限于特殊问题领域),C ++胜过C#和类似语言。


6
实际上,Java VM(可能还有.NET)会尽力避免动态分配。基本上,如果有一种避免多态的方法,则可以确定您的VM可以做到。
Martin Probst

3
+1我总是很难向我的C#同事解释这一点,他们对C ++知之甚少,因此他们无法理解其重要性。您已经很好地解释了它。
罗曼·斯塔科夫

9
@crtracy:您在没有高性能计算应用程序的情况下下注。考虑天气预报,生物信息学和数值模拟。C ++在这些领域的性能领先优势不会缩小,因为在相同的高度抽象水平上,没有其他代码可以达到可比的性能。
Konrad Rudolph

5
@乔恩·苹果和橘子。您的特定主张是“在元编程的上下文中,C#比C ++快几个数量级”,而不是“使用预编译代码比解释后的代码快几个数量级”。在我们讨论时,您声称运行时代码生成比编译时代码生成“更通用”,这显然也是错误的-它们都有优点和缺点。编译时代码生成使用类型系统提供静态类型安全性-运行时代码生成无法做到这一点(它可以提供强大的类型安全性,但不能提供静态类型安全性)。
Konrad Rudolph

5
@ user3800527我认为您错过了此答案的重点。当然,您可以通过破坏封装并使用低级结构来解决此问题,您可以使用(大多数)任何语言编写汇编。使C ++(几乎)独特且独特地适合于高性能编程的原因是,您可以构建高级抽象,而无需花费运行时间。因此,您无需使用C ++编写类似于汇编的代码即可获得出色的性能:编写正确的代码sort(arr, generic_comparer)将与C ++中的手写循环一样高效。它永远不会在C#中出现。
Konrad Rudolph

20

C ++(或C)可以使您对数据结构进行细粒度的控制。如果您想旋转一下,可以选择该选项。使用Java / .NET库的内部数据结构的大型托管Java或.NET应用程序(OWB,Visual Studio 2005)随身携带了行李。我见过OWB设计者会议使用了超过400 MB的RAM和BIDS进行多维数据集或ETL设计,也使用了100 MB的内存。

在可预测的工作负载(例如大多数基准多次重复一个过程)上,JIT可以为您提供经过充分优化的代码,以至于没有实际差异。

IMO在大型应用程序上的区别不在于JIT,而是代码本身使用的数据结构。如果应用程序占用大量内存,则缓存使用率将降低。现代CPU上的高速缓存未命中代价非常高。C或C ++真正取得成功的地方是,您可以优化数据结构的使用,以更好地与CPU缓存配合使用。


19

对于图形,标准的C#图形类比通过C / C ++访问的GDI慢得多。我知道这与语言本身无关,与整个.NET平台无关,但是Graphics是作为GDI替代品提供给开发人员的,它的性能太差了,我什至都不敢做图形用它。

我们有一个简单的基准,用于查看图形库的速度,这就是在窗口中绘制随机线。C ++ / GDI仍然可以轻松处理10000行,而C#/ Graphics难以实时执行1000行。


5
你的回答让我很感兴趣。您是否使用不安全的代码和锁定位测试了相同的基准,并自己绘制了随机线?现在,这将是一件有趣的事情。
Pedery 2012年

2
@Pedery不,我没有。仅以最基本的方式使用GDI和.NET.Graphics。“自己画随机线”是什么意思?
QBziZ 2012年

1
然后,您可能应该考虑对此进行测试,以获取有关C#速度的更现实的指标。这是该技术的很好概述:bobpowell.net/lockingbits.htm
Pedery 2012年

6
那不是我们想要做的,我们自己将单独的像素放在帧缓冲区中。如果您必须自己实现一切,那么使用API​​ /平台进行编码有什么意义呢?对我来说,这不是一个论点。我们永远不需要在GDI的帧缓冲区中放置单独的像素来绘制线条,并且我们也不打算在.NET中这样做。在我看来,我们确实使用了现实的指标,而.NET却很慢。
QBziZ

1
好吧,我只是略微了解什么是斑点检测,但是仅说明一个时间并不能证明任何事情。您是否用C ++编写过一个?在JavaScript中?并将其与C#中的那些进行比较?除此之外,我不认为斑点检测正在使用许多图形基元。如果有错,请纠正我,但我想这是对像素执行运算的统计算法。
QBziZ

13

垃圾回收是Java#不能用于实时系统的主要原因。

  1. GC何时会发生?

  2. 这需要多长时间?

这是不确定的。


5
我不是Java的忠实拥护者,但是没有什么可以说Java无法使用实时友好的GC。
Zan Lynx '04

5
如果您愿意看的话,有很多实时GC实现。(GC是一个充满研究论文的领域)
Arafangion,2010年

FWIW,理查德·琼斯(Richard Jones)刚刚出版了他的垃圾收集书的更新版本,其中包括最先进的实时GC设计。
乔恩·哈罗普

11
这是胡说八道,Windows(和Linux)不是实时操作系统。您的C ++代码也可以随时换出18 ms的插槽。
汉克·霍尔特曼

2
@HenkHolterman是的,但是您总是可以在程序集中编写一个引导程序,将其绑定到应用程序的内核引导程序中,然后直接针对硬件(在RT btw中)执行C ++应用程序。您无法在C#中做到这一点,我所做的任何努力都只能模仿C#中的预编译程序集并使用大量的C代码,这使得使用C#毫无意义。读所有这些都挺有趣的,因为如果没有.NET框架,C#实际上是无用的。
zackery.fix

11

我们必须确定C#在性能上是否可与C ++相提并论,为此我编写了一些测试程序(两种语言均使用Visual Studio 2005)。事实证明,没有垃圾回收并且仅考虑语言(而不是框架),C#的性能基本上与C ++相同。C#中的内存分配比C ++中的内存分配要快得多,并且当数据大小增加到超出缓存行边界时,C#在确定性方面略有优势。但是,所有这些最终都需要付费,并且由于垃圾回收,C#的性能不确定,从而带来巨大的成本。


1
在C ++中,可以选择使用不同的分配方法,因此根据C#中内存的分配方式(AOT?),可以在C ++中以相同的方式(但更快)完成分配。
zackery.fix

5
@ zackery.fix .NET在堆分配中具有有趣的优势,因为它只需要移动一个指针即可分配新对象。这仅由于压缩垃圾收集器才可行。当然,您可以在C ++中执行相同的操作,但是C ++不会这样做。有趣的是,您如何使用相同的参数说“ C#可以但不能,所以很垃圾”和“ C ++不能,但是它可以,所以很棒” :)
Luaan

9

通常,这取决于应用程序。在某些情况下,C#的运行速度可能微不足道,而在其他情况下,C ++的运行速度则提高了5或10倍,尤其是在可以轻松进行SIMD操作的情况下。


生成的代码为VM最好的情况下将运行时编译(如匹配一个正则表达式在运行时读取),因为静态编译香草C ++程序只能用解释,因为他们没有内置的JIT编译器。
乔恩·哈罗普

未来的发展趋势:.NET自2014年左右开始就支持SIMD和朋友,尽管它并未得到广泛使用。
a安

9

我知道这不是您要的内容,但是C#的编写速度通常比C ++ 快,这在商业环境中是一大好处。


2
我会说大多数情况下速度更快:)
陷阱

8

C / C ++在大型数组或数组(任意大小)上存在大量循环/迭代的程序中,其性能要好得多。这就是为什么图形在C / C ++中通常要快得多的原因,因为繁重的数组操作几乎是所有图形操作的基础。众所周知,由于所有安全检查,.NET的数组索引操作速度都很慢,对于多维数组尤其如此(而且,是的,矩形C#数组甚至比锯齿状的C#数组还要慢)。

如果您直接使用指针并避免使用Boost std::vector和其他高级容器以及inline所有可能的小函数,则C / C ++的优势最为明显。尽可能使用老式数组。是的,您将需要更多的代码行来完成与Java或C#相同的操作,因为避免了高级容器。如果您需要一个动态大小的数组,则只需要记住将其new T[]与相应的delete[]语句配对即可(或使用std::unique_ptr)—额外速度的代价是您必须更仔细地编码。但是作为交换,您可以摆脱托管内存/垃圾收集器的开销,这很容易占Java和.NET中大量面向对象程序以及大量托管对象的执行时间的20%或更多。内存阵列索引成本。在某些特定情况下,C ++应用程序还可以从一些漂亮的编译器开关中受益。

我是C,C ++,Java和C#的专业程序员。最近,我极少有机会用后三种语言实现完全相同的算法程序。该程序有很多数学运算和多维数组运算。我在所有3种语言中都对此进行了优化。结果是我通常在较不严格的比较中所看到的典型结果:Java比C#快1.3倍(大多数JVM比CLR更优化),而C ++原始指针版本比C#快2.1倍。请注意,C#程序仅使用安全代码-我认为您最好在使用unsafe关键字之前用C ++对其进行编码。

以免有人认为我反对C#,我最后会说C#可能是我最喜欢的语言。到目前为止,这是我遇到的最合乎逻辑,最直观,最快速的开发语言。我使用C#进行所有原型设计。C#语言比Java具有许多小的,微妙的优势(是的,我知道Microsoft可以通过较晚进入游戏并可以复制Java来解决Java的许多缺点)。敬酒Java Calendar类的人吗?如果Microsoft花费大量精力优化CLR和.NET JITter,则C#可能会认真接管。令我感到惊讶的是,他们还没有这样做-他们在C#语言中做了很多事情,为什么不跟进大量的编译器优化呢?也许我们都乞求。


3
“您只需要记住将您的手机new T[]与相应的手机配对delete[] –不,您不需要。还有std::unique_ptr要为你做的。
2015年

假设您在图形中编写了一些内容,为什么要用c#编写安全代码,您是否考虑过使用不安全代码并再次进行比较?
user3800527'6

7

>从我所听到的...

您的困难似乎在于确定您所听到的内容是否可信,并且当您尝试评估此站点上的答复时,只会再次遇到该困难。

您将如何确定人们在这里所说的话是否比您最初听到的话可信或更可信?

一种方法是索取证据

当有人声称“在某些领域C#证明比C ++更快”时,问他们为什么,请他们向您展示测量结果,请他们向您展示程序。有时他们只会犯错。有时您会发现他们只是表达一种观点,而不是分享他们可以证明是真实的东西。

人们通常会在信息和意见中混淆人们所声称的内容,而您必须尝试找出哪一个。例如,从此论坛中的答复:

  • “访问http://shootout.alioth.debian.org/进行基准测试 极大的怀疑,使用,因为这些测试代码很大程度上是在测试算术代码,而这些算术代码很可能与您的代码根本不相似。”

    问问自己是否真的了解“这些经过大量测试的算术代码”的含义,然后问问自己作者是否真的向您表明他的主张是正确的。

  • “这是一个相当无用的测试,因为它实际上取决于各个程序的优化程度;我设法将其中一些程序的速度提高了4-6倍甚至更多,这清楚地表明,未优化程序之间的比较相当愚蠢。”

    问问自己,作者是否真的向您表明他已经设法“将其中一些速度提高了4-6倍或更多”-这很容易做到!


我完全同意您的意见,这就是我在这个论坛上提问的原因...毕竟答案必须在某个地方,不是吗?:)
陷阱

1
是。答案是“取决于。”。
user49117

6

对于“令人尴尬的并行”问题,当在C ++上使用Intel TBB和OpenMP时,与使用C#和TPL进行的类似(纯数学)问题相比,我发现性能提高了大约10倍。SIMD是C#不能竞争的领域,但我也觉得TPL有相当大的开销。

就是说,我只将C ++用于对性能至关重要的任务,因为我知道我将能够使用多线程并快速获得结果。对于其他所有内容,C#(偶尔还有F#)都很好。


5

没有真正确定的答案,这是一个非常模糊的问题。

例如; 我宁愿玩用C ++创建的3D游戏,也不愿玩C#,因为它的性能肯定要好得多。(而且我知道XNA等,但是它与真实事物几乎没有关系)。

另一方面,如前所述;您应该以一种可以快速完成所需工作的语言进行开发,然后在必要时进行优化。


4
您能举几个例子吗?用C#编写的游戏发现的很慢
Karl

1
甚至安装随附的示例应用程序也感觉很慢。
David The Man

9
垃圾收集器在用C#进行游戏中是巨大的责任,因为它可以随时踢,从而导致重大停顿。显式的内存管理最终使游戏开发变得更加容易。
未来主义者

3
大多数现代游戏均受GPU限制。对于此类游戏,逻辑(在CPU上执行)是否慢10%无关紧要,它们仍然受GPU(而非CPU)限制。垃圾收集器是一个实际的问题,如果对内存分配的调整不当,则会导致随机的短暂冻结。
Michael Entin

2
@postfuturist:在PC上不是这样;垃圾收集器的进出非常出色,我从未遇到过任何问题。然而,在Xbox 360和Zune / Windows的7-电话,垃圾收集器是不是几乎一样聪明如在PC上; 我从来没有参加过任何书面的,但谁才有人告诉我,垃圾收集器是一个巨大的问题。
BlueRaja-Danny Pflughoeft 2011年

5

.NET语言可以与C ++代码一样快,甚至更快,但是C ++代码将具有更恒定的吞吐量,因为.NET运行时必须暂停GC,即使暂停非常巧妙。

因此,即使您对运行时GC非常小心,如果您有一些必须始终快速运行且没有任何暂停的代码,.NET 有时会引入延迟。


6
-1:这实际上是一个神话。首先,惯用的C ++的延迟实际上很糟糕,并且通常比.NET差得多,这是因为当大型数据结构超出范围时RAII会导致大量析构函数,而现代GC却是递增的,而.NET甚至是并发的。其次,您实际上可以通过不分配来完全删除.NET上的GC暂停。
乔恩·哈罗普

2
如果这样做,则必须放弃使用BCL,因为大多数方法都会创建临时对象。
Florian Doyon

5
的确如此,直到.net 4才使GC成为增量式。我们有一个大型的C#应用​​,每次GC都会暂停几秒钟。对于性能至关重要的应用程序,这是杀手.。
贾斯汀

5
倾向于推动硬件的程序倾向于使用C ++是有原因的。需要时,您可以进行更精细的控制。在启动系统时,性能才是关键,否则使用C#或Java可以节省时间。
VoronoiPotato 2012年

4
如果您无法管理缓存行为,则无法击败优化的c ++代码。从L1到主内存的高速缓存未命中可能会使您的操作速度降低100倍。
DAG

4

从理论上讲,对于长时间运行的服务器类型应用程序,JIT编译的语言比本地编译的语言快得多。由于通常将JIT编译语言首先编译为相当低级的中间语言,因此无论如何,您都可以在编译时立即进行许多高级优化。最大的优势在于,随着JIT可以获取越来越多的有关应用程序使用方式的数据,JIT可以继续动态地重新编译部分代码。它可以安排最常见的代码路径,以使分支预测尽可能频繁地成功。它可以重新排列通常一起调用的单独代码块,以将它们都保留在缓存中。它可以花费更多的精力来优化内部循环。

我怀疑这是通过.NET或任何JRE完成的,但是在我上大学时就已经对其进行了研究,因此可以认为这些事情很快就会进入现实世界并不无道理。 。


4

需要大量内存访问的应用程序,例如 在非托管环境(C ++)中编写图像操作通常比托管(C#)更好。使用指针算法优化的内部循环在C ++中更容易控制。在C#中,您可能需要求助于不安全的代码才能获得接近相同的性能。


4

我已经vector在C ++和C#等效语言List以及简单的2d数组中进行了测试。

我正在使用Visual C#/ C ++ 2010 Express版本。这两个项目都是简单的控制台应用程序,我已经在标准(无自定义设置)发布和调试模式下对其进行了测试。C#列表在我的PC上运行更快,在C#中数组初始化也更快,数学运算也更慢。

我正在使用Intel Core2Duo P8600@2.4GHz,C#-.NET 4.0。

我知道矢量实现与C#列表不同,但是我只想测试用于存储对象(并能够使用索引访问器)的集合。

当然,您需要清除内存(对于的每次使用都可以说new),但是我想保持代码简单。

C ++矢量测试

static void TestVector()
{
    clock_t start,finish;
    start=clock();
    vector<vector<double>> myList=vector<vector<double>>();
    int i=0;
    for( i=0; i<500; i++)
    {
        myList.push_back(vector<double>());
        for(int j=0;j<50000;j++)
            myList[i].push_back(j+i);
    }
    finish=clock();
    cout<<(finish-start)<<endl;
    cout<<(double(finish - start)/CLOCKS_PER_SEC);
}

C#清单测试:

private static void TestVector()
{

    DateTime t1 = System.DateTime.Now;
    List<List<double>> myList = new List<List<double>>();
    int i = 0;
    for (i = 0; i < 500; i++)
    {
        myList.Add(new List<double>());
        for (int j = 0; j < 50000; j++)
            myList[i].Add(j *i);
    }
    DateTime t2 = System.DateTime.Now;
    Console.WriteLine(t2 - t1);
}

C ++-数组:

static void TestArray()
{
    cout << "Normal array test:" << endl;
    const int rows = 5000;
    const int columns = 9000;
    clock_t start, finish;

    start = clock();
    double** arr = new double*[rows];
    for (int i = 0; i < rows; i++)
        arr[i] = new double[columns];
    finish = clock();

    cout << (finish - start) << endl;

    start = clock();
    for (int i = 0; i < rows; i++)
        for (int j = 0; j < columns; j++)
            arr[i][j] = i * j;
    finish = clock();

    cout << (finish - start) << endl;
}

C#-数组:

private static void TestArray()
{
    const int rows = 5000;
    const int columns = 9000;
    DateTime t1 = System.DateTime.Now;
    double[][] arr = new double[rows][];
    for (int i = 0; i < rows; i++)
        arr[i] = new double[columns];
    DateTime t2 = System.DateTime.Now;

    Console.WriteLine(t2 - t1);

    t1 = System.DateTime.Now;
    for (int i = 0; i < rows; i++)
        for (int j = 0; j < columns; j++)
            arr[i][j] = i * j;
    t2 = System.DateTime.Now;

    Console.WriteLine(t2 - t1);

}

时间:(发布/调试)

C ++

  • 600/606毫秒数组初始化,
  • 200/270毫秒数组填充,
  • 1sec / 13sec向量初始化并填充。

(是的,这是13秒,在调试模式下,列表/向量总是有问题。)

C#:

  • 20/20 ms数组初始化,
  • 403/440毫秒数组填充,
  • 710/742 ms列出初始化并填充。

1
我很想在std :: list中看到索引访问器。无论如何,列表,释放模式需要37秒。无需调试即可发布:3秒列表,0,3秒向量。可能取消引用问题或某事。范例:nopaste.pl/12fb
Wiory 2011年

2
为了进行更精确的测量,您不应该使用System.DateTime.Now,而应该使用Stopwatch类。
山姆

4
在C ++中获得如此慢的向量填充时间的部分原因是您使用的是push_back。这已经在许多帖子中显示出比使用at方法或运算符[]慢。为了使用这些方法之一,您需要使用调整大小或保留方法。另外,对于c ++向量而言,初始化要花很长时间的原因是,您要强制使用复制或赋值运算符(在这种情况下不确定哪个)来初始化c ++向量。对于c ++中的数组,有一种算法使用2个新调用而不是5001,并且迭代速度也更快。
Zachary Kraus 2014年

5
我认为您没有以适当的方式进行c ++。乍一看,发现了很多问题。例如,vector <vector <double >> myList = vector <vector <double >>()
DAG

2
哇。不确定通过比较List和可调整大小的数组可以得出什么结论,但是如果您要使用这样的向量,则需要了解reserve(),我的朋友,reserve()。
U007D'9

3

这要看情况。如果将字节码转换为机器码(而不仅仅是JIT)(我是说如果您执行该程序),并且如果您的程序使用了很多分配/取消分配,则它可能会更快,因为GC算法仅需要一次通过(理论上)通过整个内存一次,但是正常的malloc / realloc / free C / C ++调用会导致每个调用产生开销(调用开销,数据结构开销,缓存未命中;))。

因此,从理论上讲是可能的(对于其他GC语言也是如此)。

我真的没有看到无法针对大多数应用程序使用C#元编程的极端缺点,因为大多数程序员无论如何都不会使用它。

另一个很大的优势是,像LINQ “扩展” 一样,SQL为编译器提供了优化对数据库的调用的机会(换句话说,编译器可以将整个LINQ编译为一个“ blob”二进制文件,其中被调用函数可以内联或插入)。供您优化使用,但我在这里推测)。


1
任何合适的C ++开发人员都不会遇到您所描述的问题。只有决定在程序上添加类并称为C ++的不良C程序员才会遇到这些问题。
清晰的

1
为了神的爱,这是8岁的生日,OMFGz
Quonux

随时提供更好的最新答案
Quonux

2

我猜想有一些用C#编写的应用程序运行得很快,还有更多用C ++编写的应用程序运行得很快(好一点的C ++……也要使用UNIX ...)
-问题的确实是-那是什么东西,用户开发人员抱怨...
恕我直言,在C#的情况下,我们拥有非常舒适的UI,非常好的库层次结构以及CLI的整个接口系统。对于C ++,我们有模板,ATL,COM,MFC以及已经编写并正在运行的代码(如OpenGL,DirectX等)的全部代码。开发人员抱怨在C#情况下,GC调用的次数增加了(意味着程序运行很快,并且一秒钟-砰!它卡住了。
用C#编写代码非常简单快捷(不要忘记这也会增加出错的机会。对于C ++,开发人员抱怨内存泄漏,-表示崩溃,DLL之间的调用以及“ DLL地狱”-问题)支持和替换库的更新……
我认为您在编程语言中拥有的技能越多,软件的质量(和速度)就越高。


2

我要这样说:编写更快代码的程序员,是那些更了解当前计算机运行速度的原因的程序员,顺便说一句,他们也是使用允许精确的低级和确定性工具的适当工具的程序员。优化技术。由于这些原因,这些人就是使用C / C ++而不是C#的人。我会尽可能地陈述这一事实。


考虑到他正在处理的数据量,Notch编码了我的世界,速度相当快。而且,他几乎在较短的时间内用单手代码编写了代码,这在C ++中实际上是不可能的。我确实同意优化技术-如果您有10倍的开发时间花在代码上,使您的代码运行速度提高一倍,那可能是值得的。
比尔K

2

如果我没记错的话,C#模板将在运行时确定。这必须比C ++的编译时模板慢。

当您接受其他许多人提到的所有其他编译时优化时,以及缺乏安全性的确意味着更快的速度...

就原始速度和最小内存消耗而言,我想说C ++是显而易见的选择。但是,这也可以转化为更多的时间来开发代码,并确保您不会泄漏内存或引起任何空指针异常。

判决:

  • C#:更快的开发,更慢的运行

  • C ++:缓慢的开发,更快的运行。


1

这实际上取决于您要在代码中尝试完成的工作。我听说VB.NET,C#和托管C ++之间的性能差异只是都市传说中的东西。但是,我发现,至少在字符串比较中,托管C ++击败了C#,后者反过来又击败了VB.NET。

我从来没有对语言之间的算法复杂性做过详尽的比较。我还只是在每种语言中使用默认设置。在VB.NET中,我使用设置来要求声明变量等。这是我用于托管C ++的代码:(如您所见,此代码非常简单)。我在.NET 4.6.2中以其他语言在Visual Studio 2013中运行相同的程序。

#include "stdafx.h"

using namespace System;
using namespace System::Diagnostics;

bool EqualMe(String^ first, String^ second)
{
    return first->Equals(second);
}
int main(array<String ^> ^args)
{
    Stopwatch^ sw = gcnew Stopwatch();
    sw->Start();
    for (int i = 0; i < 100000; i++)
    {
        EqualMe(L"one", L"two");
    }
    sw->Stop();
    Console::WriteLine(sw->ElapsedTicks);
    return 0;
}

1

在性能方面,C#和C ++之间存在一些主要区别:

  • C#是基于GC /堆的。由于内存访问的非本地性,分配和GC本身是开销
  • 多年来,C ++优化器已经变得非常出色。JIT编译器无法达到相同的级别,因为它们的编译时间有限并且看不到全局范围

除此之外,程序员的能力也起着作用。我看到了糟糕的C ++代码,这些类在各处都按值作为参数传递了类。如果您不知道自己在做什么,实际上会使C ++的性能变差。


0

>毕竟答案必须在某个地方,不是吗?:)

嗯不

正如一些答复所指出的那样,问题的指定方式不够明确,无法以答案的方式邀请问题。仅采取一种方式:

然后哪个程序?哪台机器?哪个操作系统?哪个数据集?


我完全同意。我想知道为什么人们在问一个一般性问题时会期望得到准确的答案(63.5%)。我认为对这种问题没有普遍的答案。
叫我史蒂夫

@callmesteve:我知道你的意思,但是你的最后一句话听起来像是钉在黑板上的程序员。
Wouter van Nifterick

1
这似乎没有回答问题,而是作为注释或or语来阅读。
塔斯州,2013年

-13

受此启发,我对大多数程序中所需的60%的普通指令进行了快速测试。

这是C#代码:

for (int i=0; i<1000; i++)
{
    StreamReader str = new StreamReader("file.csv");
    StreamWriter stw = new StreamWriter("examp.csv");
    string strL = "";
    while((strL = str.ReadLine()) != null)
    {
        ArrayList al = new ArrayList();
        string[] strline = strL.Split(',');
        al.AddRange(strline);
        foreach(string str1 in strline)
        {
            stw.Write(str1 + ",");
        }
        stw.Write("\n");
    }
    str.Close();
    stw.Close();
}

字符串数组和arraylist专门用于包含这些指令。

这是c ++代码:

for (int i = 0; i<1000; i++)
{
    std::fstream file("file.csv", ios::in);
    if (!file.is_open())
    {
        std::cout << "File not found!\n";
        return 1;
    }

    ofstream myfile;
    myfile.open ("example.txt");
    std::string csvLine;

    while (std::getline(file, csvLine))
    {
        std::istringstream csvStream(csvLine);
        std::vector csvColumn;
        std::string csvElement;

        while( std::getline(csvStream, csvElement, ‘,’) )
        {
            csvColumn.push_back(csvElement);
        }

        for (std::vector::iterator j = csvColumn.begin(); j != csvColumn.end(); ++j)
        {
            myfile << *j << ", ";
        }

        csvColumn.clear();
        csvElement.clear();
        csvLine.clear();
        myfile << "\n";
    }
    myfile.close();
    file.close();
}

我使用的输入文件大小为40 KB。

结果是-

  • C ++代码运行了9秒钟。
  • C#代码:4秒!!!

哦,但这是在Linux上...在Mono上运行C# ...在g ++ C ++。

好的,这就是我在Windows上获得的– Visual Studio 2003

  • C#代码运行了9秒钟。
  • C ++代码–可怕的370秒!!!

7
您在此处使用了不同的数据结构和库代码,尽管“ 370秒”确实表明有些可怕–您不是在调试器中运行它吗?我怀疑您正在使用的CSV库的性能比您正在使用的语言的性能更有趣。我会质疑在这种情况下矢量的使用,以及您使用了哪些优化。此外,众所周知,对于至少某些常见的实现方式,iostream(尤其是“ myfile << * j <<“,”;“)比其他写入文件的方法要慢得多。
Arafangion,2010年

6
最后,您将在C ++版本中完成更多工作。(为什么要清除csvColumn,csvElement和csvLines?)
Arafangion,2010年

2
while循环的每次迭代都将破坏和重构std :: istream和std :: vector和std :: string。while主体在每次迭代时都超出范围,而while范围内的所有那些变量都将在每次迭代时进行破坏和构造。
doug65536

1
从阅读c ++代码的外观来看,您正在尝试从一个文件复制到另一个文件。不必使用文件流,字符串,向量和字符串流之间的复杂交互,您只需将输入文件流复制到输出文件流即可。这样可以节省大量时间和内存。
Zachary Kraus 2014年

2
进行速度测试,测试内存中的内容不要进入磁盘IO,无休止地在最新的SSD上进行测试,以及专用于性能应用程序的测试。由于计算机不断写入磁盘,即使您不触摸键盘也是如此。
user3800527'6
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.