在这种情况下,为什么.NET比C++更快?

12 .net c# c++ benchmarking fibonacci

确保您在IDE外部运行.这是关键.

-edit-我喜欢SLaks评论."这些答案中的错误信息量是惊人的." :d

冷静下来.几乎所有人都错了.我做了优化. 事实证明,我所做的任何优化都不够好. 我使用gettimeofday在GCC中运行代码(我将在下面粘贴代码)并使用g++ -O2 file.cpp并获得稍快于C#的结果. 也许MS没有创建在这个特定情况下所需的优化,但在下载和安装mingw后,我进行了测试,发现速度几乎相同. Justicle似乎是对的.我可以发誓我在我的电脑上使用时钟并使用它来计算并发现它速度较慢但问题已解决.在MS编译器中,C++速度几乎不会慢两倍.

当我的朋友告诉我这件事时我无法相信.所以我拿了他的代码并把一些定时器放在上面.

而不是Boo我使用C#.我不断在C#中获得更快的结果.为什么?无论我使用什么数字,.NET版本几乎都有一半的时间.

C++版(坏版):

#include <iostream>
#include <stdio.h>
#include <intrin.h>
#include <windows.h>
using namespace std;

int fib(int n)
{
    if (n < 2) return n;
    return fib(n - 1) + fib(n - 2);
}

int main()
{
    __int64 time = 0xFFFFFFFF;
    while (1)
    {
        int n;
        //cin >> n;
        n = 41;
        if (n < 0) break;
__int64 start = __rdtsc();
        int res = fib(n);
__int64 end = __rdtsc();
        cout << res << endl;
        cout << (float)(end-start)/1000000<<endl;
        break;
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

C++版(更好的版本):

#include <iostream>
#include <stdio.h>
#include <intrin.h>
#include <windows.h>
using namespace std;

int fib(int n)
{
    if (n < 2) return n;
    return fib(n - 1) + fib(n - 2);
}

int main()
{
    __int64 time = 0xFFFFFFFF;
    while (1)
    {
        int n;
        //cin >> n;
        n = 41;
        if (n < 0) break;
        LARGE_INTEGER start, end, delta, freq;
        ::QueryPerformanceFrequency( &freq );
        ::QueryPerformanceCounter( &start );
        int res = fib(n);
        ::QueryPerformanceCounter( &end );
        delta.QuadPart = end.QuadPart - start.QuadPart;
        cout << res << endl;
        cout << ( delta.QuadPart * 1000 ) / freq.QuadPart <<endl;
break;
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

C#版本:

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

using System.Runtime.InteropServices;
using System.ComponentModel;
using System.Threading;
using System.IO;

using System.Diagnostics;

namespace fibCSTest
{
    class Program
    {
         static int fib(int n)
         {
            if (n < 2)return n;
            return fib(n - 1) + fib(n - 2);
         }

         static void Main(string[] args)
         {
             //var sw = new Stopwatch();
             //var timer = new PAB.HiPerfTimer();
             var timer = new Stopwatch();
             while (true)
             {
                 int n;
                 //cin >> n;
                 n = 41;
                 if (n < 0) break;
                 timer.Start();
                 int res = fib(n);
                 timer.Stop();
                 Console.WriteLine(res);
                 Console.WriteLine(timer.ElapsedMilliseconds);
                 break;
             }
         }
    }
}
Run Code Online (Sandbox Code Playgroud)

GCC版本:

#include <iostream>
#include <stdio.h>
#include <sys/time.h>
using namespace std;

int fib(int n)
{
    if (n < 2) return n;
    return fib(n - 1) + fib(n - 2);
}

int main()
{
    timeval start, end;
    while (1)
    {
        int n;
        //cin >> n;
        n = 41;
        if (n < 0) break;
        gettimeofday(&start, 0);
        int res = fib(n);
        gettimeofday(&end, 0);
        int sec = end.tv_sec - start.tv_sec;
        int usec = end.tv_usec - start.tv_usec;
        cout << res << endl;
        cout << sec << " " << usec <<endl;
        break;
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

MSN*_*MSN 16

编辑:TL/DR版本:CLR JIT将内联一级递归,MSVC 8 SP1也不会没有#pragma inline_recursion(on).您应该在调试器之外运行C#版本以获得完全优化的JIT.

我使用VS 2008 SP1在运行Vista的Core2 Duo笔记本电脑上使用VS 2008 SP1获得了类似于使用C#与C++的类似结果,其中插入了"高性能"电源设置(~1600 ms与~3800 ms).看到优化的JIT的C#代码有点棘手,但对于x86,它归结为:

00000000 55               push        ebp  
00000001 8B EC            mov         ebp,esp 
00000003 57               push        edi  
00000004 56               push        esi  
00000005 53               push        ebx  
00000006 8B F1            mov         esi,ecx 
00000008 83 FE 02         cmp         esi,2 
0000000b 7D 07            jge         00000014 
0000000d 8B C6            mov         eax,esi 
0000000f 5B               pop         ebx  
00000010 5E               pop         esi  
00000011 5F               pop         edi  
00000012 5D               pop         ebp  
00000013 C3               ret              
            return fib(n - 1) + fib(n - 2);
00000014 8D 7E FF         lea         edi,[esi-1] 
00000017 83 FF 02         cmp         edi,2 
0000001a 7D 04            jge         00000020 
0000001c 8B DF            mov         ebx,edi 
0000001e EB 19            jmp         00000039 
00000020 8D 4F FF         lea         ecx,[edi-1] 
00000023 FF 15 F8 2F 12 00 call        dword ptr ds:[00122FF8h] 
00000029 8B D8            mov         ebx,eax 
0000002b 4F               dec         edi  
0000002c 4F               dec         edi  
0000002d 8B CF            mov         ecx,edi 
0000002f FF 15 F8 2F 12 00 call        dword ptr ds:[00122FF8h] 
00000035 03 C3            add         eax,ebx 
00000037 8B D8            mov         ebx,eax 
00000039 4E               dec         esi  
0000003a 4E               dec         esi  
0000003b 83 FE 02         cmp         esi,2 
0000003e 7D 04            jge         00000044 
00000040 8B D6            mov         edx,esi 
00000042 EB 19            jmp         0000005D 
00000044 8D 4E FF         lea         ecx,[esi-1] 
00000047 FF 15 F8 2F 12 00 call        dword ptr ds:[00122FF8h] 
0000004d 8B F8            mov         edi,eax 
0000004f 4E               dec         esi  
00000050 4E               dec         esi  
00000051 8B CE            mov         ecx,esi 
00000053 FF 15 F8 2F 12 00 call        dword ptr ds:[00122FF8h] 
00000059 03 C7            add         eax,edi 
0000005b 8B D0            mov         edx,eax 
0000005d 03 DA            add         ebx,edx 
0000005f 8B C3            mov         eax,ebx 
00000061 5B               pop         ebx  
00000062 5E               pop         esi  
00000063 5F               pop         edi  
00000064 5D               pop         ebp  
00000065 C3               ret  
Run Code Online (Sandbox Code Playgroud)

与C++生成的代码(/ Ox/Ob2/Oi/Ot/Oy/GL/Gr)相反:

int fib(int n)
{ 
00B31000 56               push        esi  
00B31001 8B F1            mov         esi,ecx 
    if (n < 2) return n; 
00B31003 83 FE 02         cmp         esi,2 
00B31006 7D 04            jge         fib+0Ch (0B3100Ch) 
00B31008 8B C6            mov         eax,esi 
00B3100A 5E               pop         esi  
00B3100B C3               ret              
00B3100C 57               push        edi  
    return fib(n - 1) + fib(n - 2); 
00B3100D 8D 4E FE         lea         ecx,[esi-2] 
00B31010 E8 EB FF FF FF   call        fib (0B31000h) 
00B31015 8D 4E FF         lea         ecx,[esi-1] 
00B31018 8B F8            mov         edi,eax 
00B3101A E8 E1 FF FF FF   call        fib (0B31000h) 
00B3101F 03 C7            add         eax,edi 
00B31021 5F               pop         edi  
00B31022 5E               pop         esi  
} 
00B31023 C3               ret              
Run Code Online (Sandbox Code Playgroud)

C#版本基本上是内联fib(n-1)和fib(n-2).对于如此call繁重的函数,减少函数调用的数量是速度的关键.替换fib为以下内容:

int fib(int n);

int fib2(int n) 
{ 
    if (n < 2) return n; 
    return fib(n - 1) + fib(n - 2); 
} 

int fib(int n)
{ 
    if (n < 2) return n; 
    return fib2(n - 1) + fib2(n - 2); 
} 
Run Code Online (Sandbox Code Playgroud)

得到它~~ 1900毫秒.顺便说一句,如果我使用#pragma inline_recursion(on)我得到与原始相似的结果fib.再展开一个级别:

int fib(int n);

int fib3(int n) 
{ 
    if (n < 2) return n; 
    return fib(n - 1) + fib(n - 2); 
} 

int fib2(int n) 
{ 
    if (n < 2) return n; 
    return fib3(n - 1) + fib3(n - 2); 
} 

int fib(int n)
{ 
    if (n < 2) return n; 
    return fib2(n - 1) + fib2(n - 2); 
} 
Run Code Online (Sandbox Code Playgroud)

得到它~1380毫秒.除此之外,它逐渐消失.

因此,我的机器的CLR JIT似乎会将递归调用内联到一个级别,而C++编译器默认情况下不会这样做.

如果只有所有性能关键代码都是这样的fib!

  • @justicle,我添加了一个`while(true){}`并附加到进程.至于C++版本,我打开了"Omit frame layout"并将调用约定更改为`__fastcall`. (2认同)

Jus*_*cle 8

编辑:虽然最初的C++时序是错误的(比较周期到毫秒),但更好的时序确实表明C#在使用vanilla编译器设置时更快.

好的,足够的随机猜测,一些科学的时间.在使用现有的C++代码获得奇怪的结果后,我只是尝试运行:

int fib(int n)
{
    if (n < 2) return n;
    return fib(n - 1) + fib(n - 2);
}

int main()
{
    __int64 time = 0xFFFFFFFF;
    while (1)
    {
        int n;
        //cin >> n;
        n = 41;
        if (n < 0) break;
        LARGE_INTEGER start, end, delta, freq;
        ::QueryPerformanceFrequency( &freq );
        ::QueryPerformanceCounter( &start );
        int res = fib(n);
        ::QueryPerformanceCounter( &end );
        delta.QuadPart = end.QuadPart - start.QuadPart;
        cout << res << endl;
        cout << ( delta.QuadPart * 1000 ) / freq.QuadPart <<endl;
break;
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

编辑:

MSN指出你应该把C#放在调试器外面,所以我重新运行了一切:

最佳结果(VC2008,从命令行运行发布版本,未启用特殊选项)

  • C++原始代码 - 10239
  • C++ QPF - 3427
  • C# - 2166(调试器中为4700).

原始的C++代码(带有rdtsc)没有返回毫秒,只是报告的时钟周期的一个因素,因此直接与StopWatch()结果进行比较是无效的.原始的时间码是错的.

注意StopWatch()使用QueryPerformance*调用:http: //msdn.microsoft.com/en-us/library/system.diagnostics.stopwatch.aspx

所以在这种情况下,C++比C#更快. 这取决于您的编译器设置 - 请参阅MSN的答案.