为什么在Delphi中循环比C#更快?

时间:2010-04-18 15:25:30

标签: c# delphi

的Delphi:


procedure TForm1.Button1Click(Sender: TObject);
var I,Tick:Integer;
begin
  Tick := GetTickCount();
  for I := 0 to 1000000000 do
    begin
    end;
  Button1.Caption := IntToStr(GetTickCount()-Tick)+' ms';
end;

C#:


private void button1_Click(object sender, EventArgs e)
        {
            int tick = System.Environment.TickCount;
            for (int i = 0; i < 1000000000; ++i)
            {
            }
            tick = System.Environment.TickCount - tick;
            button1.Text = tick.ToString()+" ms"; 
        }

Delphi提供大约515毫秒

C#给出大约3775毫秒

10 个答案:

答案 0 :(得分:28)

Delphi被编译为本机代码,而C#被编译为CLR代码,然后在运行时进行翻译。那就是说C#确实使用了JIT编译,所以你可能期望时间更相似,但它不是给定的。

如果你能描述你运行它的硬件(CPU,时钟频率),那将会很有用。

我无法访问Delphi重复您的实验,但使用原生C ++与C#以及以下代码:

VC ++ 2008

#include <iostream>
#include <windows.h>

int main(void)
{
    int tick = GetTickCount() ;
    for (int i = 0; i < 1000000000; ++i)
    {
    }
    tick = GetTickCount() - tick;
    std::cout << tick << " ms" << std::endl  ; 
}

<强> C#

using System;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            int tick = System.Environment.TickCount;
            for (int i = 0; i < 1000000000; ++i)
            {
            }
            tick = System.Environment.TickCount - tick;
            Console.Write( tick.ToString() + " ms" ) ; 
        }
    }
}

我最初得到了:

C++  2792ms
C#   2980ms

然而,我在C#版本上执行了Rebuild,并分别直接从命令行运行<project>\bin\release<project>\bin\debug中的可执行文件。这产生了:

C# (release):  720ms
C# (debug):    3105ms

所以我认为这就是真正存在差异的地方,你是从IDE运行调试版的C#代码。

如果您认为C ++特别慢,我将其作为优化发布版本运行并得到:

C++ (Optimised): 0ms

这并不奇怪,因为循环是空的,并且控制变量不在循环外使用,因此优化器完全删除它。为避免这种情况,我将i声明为volatile,结果如下:

C++ (volatile i): 2932ms

我的猜测是C#实现也删除了循环,而720ms来自其他东西;这可以解释第一次测试中时间的大部分区别。

Delphi在做什么我无法分辨,你可能会看一下生成的汇编代码。

所有上述测试均采用AMD Athlon双核5000B 2.60GHz,Windows 7 32位。

答案 1 :(得分:9)

如果这是一个基准测试,它是一个非常糟糕的基准测试,因为在这两种情况下都可以优化循环,所以你必须查看生成的机器代码才能看到正在发生的事情。如果您对C#使用发布模式,请使用以下代码

 Stopwatch sw = Stopwatch.StartNew();
 for (int i = 0; i < 1000000000; ++i){ }
 sw.Stop();
 Console.WriteLine(sw.Elapsed);

由JITter转换为:

 push        ebp 
 mov         ebp,esp 
 push        edi 
 push        esi 
 call        67CDBBB0 
 mov         edi,eax 
 xor         eax,eax               ; i = 0
 inc         eax                   ; ++i
 cmp         eax,3B9ACA00h         ; i == 1000000000?
 jl          0000000E              ; false: jmp
 mov         ecx,edi 
 cmp         dword ptr [ecx],ecx 
 call        67CDBC10 
 mov         ecx,66DDAEDCh 
 call        FFE8FBE0 
 mov         esi,eax 
 mov         ecx,edi 
 call        67CD75A8 
 mov         ecx,eax 
 lea         eax,[esi+4] 
 mov         dword ptr [eax],ecx 
 mov         dword ptr [eax+4],edx 
 call        66A94C90 
 mov         ecx,eax 
 mov         edx,esi 
 mov         eax,dword ptr [ecx] 
 mov         eax,dword ptr [eax+3Ch] 
 call        dword ptr [eax+14h] 
 pop         esi 
 pop         edi 
 pop         ebp 
 ret

答案 2 :(得分:7)

TickCount不是一个可靠的计时器;你应该使用.Net的Stopwatch课程。 (我不知道Delphi的等价物是什么)。

此外,您是否正在运行发布版本?
你有一个调试器吗?

答案 3 :(得分:4)

Delphi编译器向下使用for循环计数器(如果可能);上面的代码示例编译为:

Unit1.pas. 42: Tick := GetTickCount();
00489367 E8B802F8FF       call GetTickCount
0048936C 8BF0             mov esi,eax
Unit1.pas.43: for I := 0 to 1000000000 do
0048936E B801CA9A3B       mov eax,$3b9aca01
00489373 48               dec eax
00489374 75FD             jnz $00489373

答案 4 :(得分:3)

您正在将本机代码与VM JITted代码进行比较,这是不公平的。 本机代码总是更快,因为JITter无法像本机编译器那样优化代码。

那就是说,将Delphi与C#进行比较是不公平的,Delphi二进制文件总是会赢(更快,更小,没有任何依赖等)。

Btw,我很遗憾地惊讶于这里有多少海报不知道这些差异...或者你可能只是伤害了一些试图捍卫C#的.NET狂热者,反对任何显示更好选择的东西有

答案 5 :(得分:2)

这是c#反汇编:
DEBUG:

// int i = 0; while (++i != 1000000000) ;//==for(int i ...blah blah blah)
0000004e 33 D2            xor         edx,edx 
00000050 89 55 B8         mov         dword ptr [ebp-48h],edx 
00000053 90               nop              
00000054 EB 00            jmp         00000056 
00000056 FF 45 B8         inc         dword ptr [ebp-48h] 
00000059 81 7D B8 00 CA 9A 3B cmp         dword ptr [ebp-48h],3B9ACA00h 
00000060 0F 95 C0         setne       al   
00000063 0F B6 C0         movzx       eax,al 
00000066 89 45 B4         mov         dword ptr [ebp-4Ch],eax 
00000069 83 7D B4 00      cmp         dword ptr [ebp-4Ch],0 
0000006d 75 E7            jne         00000056 

因为你认为这是浪费cpu 修改
RELEASE:

   //unchecked
   //{
   //int i = 0; while (++i != 1000000000) ;//==for(int i ...blah blah blah)
00000032 33 D2            xor         edx,edx 
00000034 89 55 F4         mov         dword ptr [ebp-0Ch],edx 
00000037 FF 45 F4         inc         dword ptr [ebp-0Ch] 
0000003a 81 7D F4 00 CA 9A 3B cmp         dword ptr [ebp-0Ch],3B9ACA00h 
00000041 75 F4            jne         00000037 
   //}

修改
这是c ++版本:在我的机器上运行速度提高了9倍。

    __asm
    {
        PUSH ECX
        PUSH EBX
        XOR  ECX, ECX
        MOV  EBX, 1000000000
NEXT:   INC  ECX
        CMP  ECX, EBX
        JS   NEXT
        POP  EBX
        POP  ECX
    }

答案 6 :(得分:1)

您应该附加调试器并查看每个调试器生成的机器代码。

答案 7 :(得分:0)

Delphi几乎肯定会优化该循环以反向顺序执行(即DOWNTO零而不是从零开始) - 只要Delphi确定它是“安全”的,它就会这样做,可能是因为减法或检查零比添加或检查非零数字。

如果您尝试指定循环以相反顺序执行的两种情况会发生什么?

答案 8 :(得分:0)

在Delphi中,中断条件仅在循环过程开始之前计算一次,而在C#中,中断条件在每次循环中再次计算。

这就是为什么Delphi中的循环比C#更快的原因。

答案 9 :(得分:-1)

“// int i = 0; while(++ i!= 1000000000);”

这很有意思。

while(++ i!= x)与(; i!= x; i ++)

不同

区别在于while循环不执行i = 0的循环。

(尝试一下:运行这样的东西:


int i;

for (i = 0; i < 5; i++)
    Console.WriteLine(i);

i = 0;
while (++i != 5)
    Console.WriteLine(i);