当前位置:  开发笔记 > 编程语言 > 正文

C#vs C - 性能差异很大

如何解决《C#vsC-性能差异很大》经验,为你挑选了10个好方法。

我发现C anc C#中类似代码之间存在巨大的性能差异.

C代码是:

#include 
#include 
#include 

main()
{
    int i;
    double root;

    clock_t start = clock();
    for (i = 0 ; i <= 100000000; i++){
        root = sqrt(i);
    }
    printf("Time elapsed: %f\n", ((double)clock() - start) / CLOCKS_PER_SEC);   

}

而C#(控制台应用程序)是:

using System;
using System.Collections.Generic;
using System.Text;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            DateTime startTime = DateTime.Now;
            double root;
            for (int i = 0; i <= 100000000; i++)
            {
                root = Math.Sqrt(i);
            }
            TimeSpan runTime = DateTime.Now - startTime;
            Console.WriteLine("Time elapsed: " + Convert.ToString(runTime.TotalMilliseconds/1000));
        }
    }
}

使用上面的代码,C#在0.328125秒(发布版本)中完成,C运行需要11.14秒.

使用mingw将c编译为可执行的Windows.

我一直假设C/C++比C#.net更快或至少可以与C#.net相媲美.究竟是什么导致C运行速度慢了30多倍?

编辑:看起来C#优化器似乎正在删除root,因为它没有被使用.我将根分配更改为root + =并在结尾处打印出总计.我还使用cl.exe编译了C,其中/ O2标志设置为最大速度.

现在的结果是:C#2.61秒为3.75秒

C仍然需要更长的时间,但这是可以接受的



1> jalf..:

您必须比较调试版本.我只是编译了你的C代码,然后得到了

Time elapsed: 0.000000

如果您不启用优化,那么您所做的任何基准测试都是毫无价值的.(如果你确实启用了优化,那么循环就会被优化掉.所以你的基准测试代码也是有缺陷的.你需要强制它运行循环,通常是通过总结结果或类似结果,并在结束时打印出来)

您所测量的似乎基本上是"哪个编译器插入最多的调试开销".结果答案是C.但这并不能告诉我们哪个程序最快.因为当您想要速度时,您可以启用优化.

顺便说一下,如果你放弃任何语言比其他人"更快"的概念,你将从长远来看为自己省去很多麻烦.C#的速度不会超过英语.

即使在一个天真的非优化编译器中,C语言中的某些东西也会很有效,还有一些东西在很大程度上依赖于编译器来优化所有东西.当然,C#或任何其他语言也是如此.

执行速度由以下因素确定:

您正在运行的平台(操作系统,硬件,系统上运行的其他软件)

编译器

你的源代码

一个好的C#编译器将产生高效的代码.坏C编译器会生成慢代码.如何生成C#代码的C编译器,然后可以通过C#编译器运行?跑的速度有多快?语言没有速度.你的代码呢.


很好的答案,但我不同意语言速度,至少在类比中:已经发现Welsch是一种比大多数语言更慢的语言,因为长元音的频率很高.此外,人们更快地记住单词(和单词列表).http://web.missouri.edu/~cowann/docs/articles/before%201993/Cowan%20et%20al%20JML%201992%20verbal%20output%20time.pdf http://en.wikipedia.org/wiki/ Vowel_length http://en.wikipedia.org/wiki/Welsh_language
++嘿伙计们,不要在这里偏离轨道.如果同一个程序在一种语言中运行速度比另一种语言快,那是因为生成了不同的汇编代码.在这个特定的例子中,99%或更多的时间将进入浮动的"i"和"sqrt",这就是测量的内容.

2> Hans Passant..:

我会保持简短,它已经标记为已回答.C#具有定义良好的浮点模型的巨大优势.这恰好与x86和x64处理器上的FPU和SSE指令集的本机操作模式相匹配.那里没有巧合.JITter将Math.Sqrt()编译为一些内联指令.

本机C/C++背负着多年的向后兼容性./ fp:precise,/ fp:fast和/ fp:严格的编译选项是最明显的.因此,它必须调用实现sqrt()的CRT函数,并检查选定的浮点选项以调整结果.那很慢.


这是C++程序员的一个奇怪的信念,他们似乎认为C#生成的机器代码与本机编译器生成的机器代码有某种不同.只有一种.无论你使用什么gcc编译器开关或你编写的内联汇编,仍然只有一条FSQRT指令.它并不总是更快,因为一个本地语言生成它,cpu并不关心.
@ user877329 - 真的吗?哇.
这就是ngen.exe预先解决的问题.我们谈的是C#,而不是Java.
不,x64抖动使用SSE.Math.Sqrt()被转换为sqrtsd机器代码指令.
虽然技术上并不是语言之间的差异,但与典型的C/C++编译器相比,.net JITter的优化程度相当有限.其中一个最大的限制是缺乏SIMD支持,使代码通常慢了4倍.不暴露许多内在函数也可能是一个很大的问题,但这在很大程度上取决于你正在做什么.
你不需要调用任何crt函数到fsqrt.只是 - 快速数学或内联asm.本机将**总是比"托管代码"快,但有时您需要告诉编译器该做什么.
但是你忘记了JIT的时间.问题是当JIT做得更好时,启动时间会增加.Java热点与我系统上未经优化的C代码相匹配.

3> Brann..:

由于您从不使用"root",因此编译器可能已删除调用以优化您的方法.

您可以尝试将平方根值累积到累加器中,在方法结束时将其打印出来,然后查看正在发生的事情.

编辑:请参阅下面的Jalf的答案


似乎问题出在另一端.在所有情况下,C#都表现得相当合理.他的C代码显然是在没有优化的情况下编译的
你们中的很多人都没有在这里讲到重点。我读过很多类似的案例,其中c#的性能优于c / c ++,并且总是反驳是采用一些专家级的优化。99%的程序员不具备使用这种优化技术的知识,仅仅是为了使其代码比c#代码运行得更快。C / C ++的用例正在缩小。

4> Richard..:

我是C++和C#开发人员.自.NET框架的第一个测试版以来,我开发了C#应用程序,并且我在开发C++应用程序方面拥有超过20年的经验.首先,C#代码永远不会比C++应用程序更快,但我不会对托管代码,工作原理,操作间层,内存管理内部,动态类型系统和垃圾收集器进行冗长的讨论.不过,让我继续说,这里列出的基准都会产生不正确的结果.

让我解释一下:我们需要考虑的第一件事是C#(.NET Framework 4)的JIT编译器.现在,JIT使用各种优化算法(比Visual Studio附带的默认C++优化器更具攻击性)为CPU生成本机代码,.NET JIT编译器使用的指令集更接近实际CPU在机器上,可以在机器代码中进行某些替换,以减少时钟周期并提高CPU流水线高速缓存中的命中率,并产生进一步的超线程优化,例如指令重新排序和与分支预测相关的改进.

这意味着除非使用正确的RELEASE构建参数(而不是DEBUG构建)编译C++应用程序,否则C++应用程序的执行速度可能比相应的基于C#或.NET的应用程序慢.在C++应用程序上指定项目属性时,请确保启用"完全优化"和"支持快速代码".如果你有64位机器,你必须指定生成x64作为目标平台,否则你的代码将通过转换子层(WOW64)执行,这将大大降低性能.

在编译器中执行正确的优化后,C++应用程序获得0.72秒,C#应用程序获得1.16秒(两者都在发布版本中).由于C#应用程序非常基础并且在堆栈中而不是在堆上分配循环中使用的内存,因此它实际上比涉及对象,繁重计算和更大数据集的实际应用程序执行得更好.因此,提供的数据是偏向于C#和.NET框架的乐观数据.即使有这种偏见,C++应用程序的完成时间也只比同等的C#应用​​程序快一半.请记住,我使用的Microsoft C++编译器没有正确的管道和超线程优化(使用WinDBG查看汇编指令).

现在,如果我们使用英特尔编译器(顺便提一下,这是在AMD /英特尔处理器上生成高性能应用程序的行业秘密),相同的代码在.54秒内执行C++可执行文件,而使用Microsoft Visual Studio 2010执行.72秒最后,C++的最终结果为.54秒,C#为1.16秒.因此,.NET JIT编译器生成的代码比C++可执行文件长214%.在.54秒内花费的大部分时间是从系统中获取时间而不是在循环内部!

统计中还缺少的是启动和清理时间,这些时间未包含在时间中.与C++应用程序相比,C#应用程序在启动和终止上花费的时间更多.这背后的原因很复杂,并且与.NET运行时代码验证例程和内存管理子系统有关,后者在程序的开头(因此,结束时)执行大量工作以优化内存分配和垃圾集电极.

在测量C++和.NET IL的性能时,重要的是查看汇编代码以确保所有计算都存在.我发现,如果不在C#中添加一些额外的代码,上面示例中的大多数代码实际上都是从二进制文件中删除的.当您使用更积极的优化器(例如英特尔C++编译器附带的优化器)时,C++也是如此.我在上面提供的结果100%正确并在装配级别进行了验证.

互联网上很多论坛的主要问题是很多新手在没有理解技术的情况下听取微软的营销宣传,并且虚假声称C#比C++更快.声称理论上,C#比C++更快,因为JIT编译器可以优化CPU的代码.这个理论的问题在于,.NET框架中存在大量管道,这会降低性能; C++应用程序中不存在的管道.此外,经验丰富的开发人员将知道用于给定平台的正确编译器,并在编译应用程序时使用适当的标志.在Linux或开源平台上,这不是问题,因为您可以分发源并创建安装脚本,使用适当的优化来编译代码.在Windows或闭源平台上,您必须分发多个可执行文件,每个可执行文件都有特定的优化.将要部署的Windows可执行文件是基于(使用自定义操作)由MSI安装程序检测到CPU上.


-1,这个咆哮充满了错误和误导性的陈述,例如显而易见的"C#代码永远不会比C++应用程序更快"
-1.你应该阅读Rico Mariani和Raymond Chen的C#vs C性能战:http://blogs.msdn.com/b/ricom/archive/2005/05/16/418051.aspx.简而言之:微软中最聪明的人之一进行了大量优化,使C版本比简单的C#版本更快.
1.微软从来没有对C#的声称更快,他们声称它的速度大约是速度的90%,开发速度更快(因此调整时间更长),并且由于内存和类型安全性而导致更多的无错误.所有这些都是正确的(我有20年的C++和10年的C#)2.在大多数情况下,启动性能毫无意义.3.还有更快的C#编译器,如LLVM(因此,苹果推出英特尔不是苹果)
启动性能并非毫无意义.这在大多数企业基于Web的应用程序中非常重要,这就是为什么Microsoft引入了在.NET 4.0中预加载(自动启动)的网页的原因.当应用程序池每隔一段时间被回收时,第一次加载每个页面会给复杂页面增加一个显着的延迟并导致浏览器超时.
微软在早期的营销材料中声称.NET的性能更快.他们还对垃圾收集器做出各种声明,对性能影响很小或没有影响.其中一些声明在早期版本中已经出现在各种书籍中(在ASP.NET和.NET上).虽然微软并没有特别说你的C#应用​​程序会比你的C++应用程序更快,但它们可能会大肆宣传通用评论和营销口号,例如"Just-In-Time Means Run-It-Fast"(http:// msdn. microsoft.com/en-us/library/ms973894.aspx).
好帖子!+1 - 互联网上很多论坛的主要问题是很多新手在不了解技术的情况下听取微软的营销宣传
你会相信微软在他们的博客上提供的一切东西吗?

5> Neil N..:

我的第一个猜测是编译器优化,因为你从不使用root.你只需分配它,然后反复覆盖它.

编辑:该死的,击败了9秒!


我说你是对的.实际变量被覆盖,除此之外从未使用过.csc最有可能放弃整个循环,而c ++编译器可能会将其保留.更准确的测试是累积结果然后在结束时打印出结果.此外,不应该对种子值进行硬编码,而是将其留给用户定义.这不会给c#编译器留下任何空间.

6> 小智..:

要查看循环是否正在优化,请尝试将代码更改为

root += Math.Sqrt(i);

ans在C代码中类似,然后在循环外打印root的值.



7> i_am_jorf..:

也许c#编译器注意到你没有在任何地方使用root,所以它只是跳过整个for循环.:)

可能不是这种情况,但我怀疑原因是什么,它依赖于编译器实现.尝试使用Microsoft编译器(cl.exe,作为win32 sdk的一部分提供)使用优化和发布模式编译C程序.我打赌你会看到其他编译器的性能提升.

编辑:我不认为编译器可以只优化for循环,因为它必须知道Math.Sqrt()没有任何副作用.


也许它确实知道这一点.
@ Neil,@ jeff:同意,它可以很容易地知道.根据实现,Math.Sqrt()上的静态分析可能不那么难,虽然我不确定具体执行哪些优化.

8> Tom..:

无论什么时间差异.可能是,"经过的时间"无效.如果您能保证两个程序在完全相同的条件下运行,那么它只是一个有效的.

也许你应该尝试一场胜利.相当于$/usr/bin/time my_cprog;/usr/bin/time my_csprog



9> Cecil Has a ..:

我把(根据你的代码)放在一起,在C和C#中进行了两次更具竞争力的测试.这两个使用模运算符编写一个较小的数组用于索引(它增加了一点开销,但是,嘿,我们试图比较性能[粗略级别]).

C代码:

#include 
#include 
#include 
#include 

void main()
{
    int count = (int)1e8;
    int subcount = 1000;
    double* roots = (double*)malloc(sizeof(double) * subcount);
    clock_t start = clock();
    for (int i = 0 ; i < count; i++)
    {
        roots[i % subcount] = sqrt((double)i);
    }
    clock_t end = clock();
    double length = ((double)end - start) / CLOCKS_PER_SEC;
    printf("Time elapsed: %f\n", length);
}

在C#中:

using System;

namespace CsPerfTest
{
    class Program
    {
        static void Main(string[] args)
        {
            int count = (int)1e8;
            int subcount = 1000;
            double[] roots = new double[subcount];
            DateTime startTime = DateTime.Now;
            for (int i = 0; i < count; i++)
            {
                roots[i % subcount] = Math.Sqrt(i);
            }
            TimeSpan runTime = DateTime.Now - startTime;
            Console.WriteLine("Time elapsed: " + Convert.ToString(runTime.TotalMilliseconds / 1000));
        }
    }
}

这些测试将数据写入数组(因此不应允许.NET运行时剔除sqrt操作)尽管该数组明显更小(不想使用过多的内存).我在发布配置中编译了这些并在控制台窗口内运行它们(而不是从VS开始).

在我的计算机上,C#程序在6.2到6.9秒之间变化,而C版本在6.9和7.1之间变化.



10> Mike Dunlave..:

如果您只是在程序集级别单步执行代码,包括单步执行平方根程序,您可能会得到问题的答案.

不需要受过教育的猜测.

推荐阅读
手机用户2402852307
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有