C#vs C - 性能差异很大

时间:2009-03-26 16:22:40

标签: c# c performance

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

C代码是:

#include <stdio.h>
#include <time.h>
#include <math.h>

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为3.75秒 C#

为2.61秒

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

13 个答案:

答案 0 :(得分:158)

您必须比较调试版本。我刚刚编译了你的C代码,并得到了

Time elapsed: 0.000000

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

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

顺便说一句,如果你放弃任何语言比其他语言“更快”的概念,那么从长远来看,你会省去很多麻烦。 C#的速度不会超过英语。

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

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

  • 您正在运行的平台(操作系统,硬件,系统上运行的其他软件)
  • 编译器
  • 您的源代码

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

答案 1 :(得分:113)

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

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

答案 2 :(得分:59)

由于您从不使用“root”,编译器可能已经删除了调用以优化您的方法。

您可以尝试将平方根值累积到累加器中,在方法结束时将其打印出来,然后看看发生了什么。

修改:请参阅下面的Jalf's answer

答案 3 :(得分:52)

答案 4 :(得分:10)

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

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

答案 5 :(得分:7)

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

root += Math.Sqrt(i);

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

答案 6 :(得分:6)

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

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

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

答案 7 :(得分:5)

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

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

答案 8 :(得分:5)

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

C代码:

#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <math.h>

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之间变化。

答案 9 :(得分:5)

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

无需进行有根据的猜测。

答案 10 :(得分:2)

这里可能存在问题的另一个因素是C编译器编译为您所针对的处理器系列的通用本机代码,而编译C#代码时生成的MSIL然后进行JIT编译以针对您拥有的确切处理器完成任何可能的优化。因此,从C#生成的本机代码可能比C语言快得多。

答案 11 :(得分:1)

在我看来,这与语言本身无关,而是与平方根函数的不同实现有关。

答案 12 :(得分:1)

实际上伙计们,循环没有被优化掉。我编译了John的代码并检查了生成的.exe。循环的内容如下:

 IL_0005:  stloc.0
 IL_0006:  ldc.i4.0
 IL_0007:  stloc.1
 IL_0008:  br.s       IL_0016
 IL_000a:  ldloc.1
 IL_000b:  conv.r8
 IL_000c:  call       float64 [mscorlib]System.Math::Sqrt(float64)
 IL_0011:  pop
 IL_0012:  ldloc.1
 IL_0013:  ldc.i4.1
 IL_0014:  add
 IL_0015:  stloc.1
 IL_0016:  ldloc.1
 IL_0017:  ldc.i4     0x5f5e100
 IL_001c:  ble.s      IL_000a

除非运行时足够聪明才能意识到循环什么都不做并跳过它?

编辑: 将C#更改为:

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

经过的时间(在我的机器上)从0.047变为2.17。但这仅仅是增加1亿个附加运营商的开销吗?