还是说现在情况反过来了?
据我所知,有些领域 C# 比 C++ 更快,但我从未有勇气自己测试。
希望您能详细解释这些差异或指导我获取相关信息。
还是说现在情况反过来了?
据我所知,有些领域 C# 比 C++ 更快,但我从未有勇气自己测试。
希望您能详细解释这些差异或指导我获取相关信息。
像C#或Java这样的基于字节码的语言,如果有JIT编译器,理论上它们可以和C++一样快。但是长期以来,C++代码在许多情况下仍然明显更快,这主要是因为更高级别的JIT优化实现比较困难,而且真正酷炫的优化刚刚才开始问世。
因此,在许多情况下,C++确实更快。但这只是答案的一部分。C++代码实际上更快的情况是高度优化的程序,由专业程序员彻底地优化了代码。这不仅非常耗时(因此昂贵),而且通常会由于过度优化而导致错误。
另一方面,解释型语言中的代码在运行时的后续版本(.NET CLR或Java VM)中会变得更快,而不需要任何操作。 JIT编译器可以进行许多有用的优化,而在具有指针的语言中这些优化是不可能的。此外,一些人认为垃圾回收应该通常与手动内存管理一样快或更快,并且在许多情况下确实如此。您通常可以在C ++或C中实现并实现所有这些,但这将更加复杂且容易出错。
正如Donald Knuth所说,“过早的优化是万恶之源”。如果您确实确定您的应用程序将主要由非常性能关键的算术组成,并且它将成为瓶颈,而且它肯定会比C++快,那么您可以选择C ++。否则,请首先使用最适合您的语言正确地实现您的应用程序,然后在运行速度过慢时找出性能瓶颈,然后考虑如何优化代码。在最坏的情况下,您可能需要通过外部函数接口调用C代码,以便仍然可以使用低级别语言编写关键部分。
请记住,优化正确的程序相对容易,但纠正优化过的程序要困难得多。
给出实际速度优势的百分比是不可能的,这在很大程度上取决于您的代码。在许多情况下,编程语言的实现甚至都不是瓶颈。对于在http://benchmarksgame.alioth.debian.org/进行的基准测试要持高度怀疑态度,因为这些测试主要测试算术代码,这与您的代码大概率完全不同。
我先要反对此问题被接受的(且得到大量赞同的)答案,声明:
JIT编译代码比经过适当优化的C++(或其他没有运行时开销的语言)程序运行得慢确实有很多原因,包括:
在运行时用于JIT编译代码的计算周期定义上不可用于程序执行。
JIT编译器中的任何热点都会与CPU中指令和数据高速缓存竞争。 我们知道高速缓存是性能的主导因素,并且像C++这样的本地语言没有这种类型的竞争,这是通过设计实现的。
运行时优化器的时间预算必然比编译时优化器的时间预算更加受限制(正如另一位评论者所指出的)
底线:最终,你几乎肯定可以使用C++创建比C#更快的实现。
现在,话虽如此,有多快真的无法量化,因为有太多变量:任务、问题领域、硬件、实现质量以及许多其他因素。 您必须在您的情况下运行测试以确定性能差异,然后决定是否值得额外的努力和复杂性。
这是一个非常长且复杂的主题,但我认为值得提及的是,C#的运行时优化器非常出色,并且能够执行某些动态优化,而这些优化对于使用编译时(静态)优化器的C++来说根本不可用。 即使如此,优势仍然通常深深地掌握在本机应用程序的手中,但动态优化器是上述“几乎”限定语的原因。
--
关于相对性能,我也对其他答案中看到的数字和讨论感到不安,因此我想发表一下意见,并同时支持我上面所说的话。
这些基准测试的一个很大的问题是,您不能编写C++代码,就好像您正在编写C#代码并期望获得代表性的结果(例如,在C++中执行数千个内存分配将为您带来可怕的数字)。
相反,我编写了稍微更符合惯例的C++代码,并与@Wiory提供的C#代码进行了比较。 我对C++代码进行了两个重要的更改:
使用了vector :: reserve()
将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);
}
运行时间(发布版):初始化:124毫秒,填充:165毫秒
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微秒(是的,那是微秒),填充时间:152毫秒
观察结果:
将C#实现更改为相同的一维数组实现产生了初始化时间:40毫秒,填充时间:171毫秒,总时间:211毫秒(C++仍然比C#快近40%)。
在C ++中编写“快速”代码比在任何一种语言中编写“常规”代码要困难得多。
在C ++中很容易出现性能不佳的情况;我们在未保留向量的性能方面看到了这一点。还有许多类似的陷阱。
考虑到运行时所发生的所有事情,C#的性能相当惊人。而且这种性能相对容易获取。
更多关于C++和C#性能比较的轶闻数据:https://benchmarksgame.alioth.debian.org/u64q/compare.php?lang=gpp&lang2=csharpcore
总的来说,C++赋予你更多控制性能的权利。您想使用指针?引用?堆栈内存?堆内存?使用动态多态性还是消除虚拟表的运行时开销,通过静态多态性(通过模板/ CRTP)?在C++中,您必须......嗯,有机会自己做出所有这些选择(以及更多选择),理想情况下,使您的解决方案最好地解决所涉及的问题。
请问您是否真正需要或想要这种控制权,因为即使对于上面的简单示例,您也可以看到虽然性能有了显着提高,但访问所需投资更深入。
int[,]
...接下来是一个例子。 - nikib3ro它快了五个橙。或者说:没有(正确的)笼统答案。C++是一种静态编译语言(但是,也有基于分析数据的优化),而C#则通过JIT编译器运行。存在如此多的差异,以至于像“更快多少”的问题无法回答,甚至无法给出数量级。
根据我的经验(我曾经与这两种语言密切合作过),相对于C++,C# 的主要问题是内存消耗比较高,我没有找到一种好的方式来控制它。正是由于内存消耗会最终导致 .NET 软件变慢。
另一个因素是JIT编译器不能花太多时间进行高级优化,因为它在运行时运行,如果它需要太多时间,最终用户将会注意到。而另一方面,C++编译器有足够的时间在编译时进行优化。在我看来,这个因素比内存消耗少得多。
在特定的场景中,C++仍然占据优势(而且未来几年内将继续如此),这种情况发生在多态决策可以在编译时预先确定的情况下。
通常,封装和延迟决策是一件好事,因为它使得代码更具动态性,更易于适应不断变化的需求,并更容易用作框架。这就是为什么C#中的面向对象编程非常高效,并可归纳为“泛化”一词的原因。不幸的是,这种特定类型的泛化会带来运行时成本。
通常,这种成本不重要,但有些应用程序中虚方法调用和对象创建的开销可能会有所差异(特别是因为虚方法阻止其他优化,例如方法调用内联)。这就是C++具有巨大优势的地方,因为您可以使用模板来实现一种不同类型的泛化,这种泛化对运行时没有影响,但不一定比面向对象编程少多少多态性。实际上,构成面向对象编程的所有机制都可以仅使用模板技术和编译时解析进行建模。
在这种情况下(尽管他们通常局限于特殊问题领域),C++胜过C#和类似的语言。
sort(arr, generic_comparer)
将与C++中手写循环一样有效。但这在C#中永远不可能实现。 - Konrad RudolphC++(或C语言)可以对数据结构进行精细的控制。如果你想要位运算,这是可行的。而大型管理的Java或.NET应用程序(如OWB,Visual Studio 2005)使用Java/.NET库的内部数据结构时会带来很多负担。我曾经见过OWB设计会话使用超过400 MB的RAM,而用于立方体或ETL设计的BIDS也可能会达到几百MB。
在可预测的工作负载下(例如大多数重复处理过程的基准测试),即时编译器可以为您生成优化得足够好的代码,以至于没有实际的差异。
在我的看法中,在大型应用程序中,区别不是即时编译器,而是代码本身使用的数据结构。当应用程序需要较多的内存时,CPU缓存的利用率就会变低,导致缓存未命中。现代CPU上的缓存未命中非常昂贵。C或C++的优势在于可以优化数据结构的使用方式,以使其与CPU缓存协同工作。
对于图形方面,标准的C# Graphics类比通过C/C++访问的GDI要慢得多。 我知道这与语言本身无关,更多的是与整个.NET平台有关,但Graphics是作为GDI替代品提供给开发人员的,其性能太差了,我甚至不敢用它进行图形处理。
我们有一个简单的基准测试来查看图形库的速度,那就是在窗口中绘制随机线条。 C++/GDI仍然可以轻松绘制10000条线,而C#/Graphics在实时绘制1000条线时就会遇到困难。
C/C++在处理大型数组或对任何大小的数组进行重复循环/迭代的程序中表现更加优异。这就是为什么C/C++中的图形通常更快的原因,因为几乎所有的图形操作都基于繁重的数组操作。由于.NET中的安全检查,数组索引操作非常慢,特别是多维数组(是的,矩形形式的C#数组比锯齿形状的C#数组更慢)。
如果直接使用指针并避免使用Boost、std::vector和其他高级容器,尽可能使用老式的数组,那么C/C++的优势最为明显,每个小函数都应该使用inline。是的,相对于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(是的,我知道微软有机会通过晚入局并抄袭Java来修复Java的许多缺点)。向Java的Calendar类干杯?如果微软真正努力优化CLR和.NET JITter,C#可能会严重占领市场。我真的很惊讶他们还没有这样做-在C#语言中做了很多正确的事情,为什么不跟进强大的编译器优化呢?也许我们都应该请求一下。
我们不得不确定C#在性能上是否与C++相当,我为此编写了一些测试程序(使用Visual Studio 2005编写两种语言)。结果表明,在没有垃圾回收并且仅考虑语言(而非框架)的情况下,C#与C++基本具有相同的性能。在内存分配方面,C#比C++更快,并且在数据大小超出缓存行边界时,C#具有轻微的确定性优势。但是,所有这些最终都必须付出代价,C#由于垃圾回收而存在着非确定性性能损失的巨大成本。