我发现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仍然需要更长时间,但这是可以接受的
答案 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)
答案 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亿个附加运营商的开销吗?