Почему циклы в Delphi быстрее, чем в C #? - PullRequest
5 голосов
/ 18 апреля 2010

Delphi:

<code>
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 #:

<code>
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 ]

28 голосов
/ 18 апреля 2010

Delphi компилируется в собственный код, тогда как C # компилируется в код CLR, который затем транслируется во время выполнения. Тем не менее, C # использует JIT-компиляцию, поэтому вы можете ожидать, что время будет более похожим, но это не дано.

Было бы полезно, если бы вы могли описать оборудование, на котором вы его запускали (процессор, тактовая частота).

У меня нет доступа к 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 # и запустил исполняемый файл в <project>\bin\release и <project>\bin\debug соответственно непосредственно из командной строки. Это дало:

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

Поэтому я считаю, что именно в этом и заключается разница: вы запускали отладочную версию кода C # из IDE.

Если вы думаете, что C ++ особенно медленный, я запустил его как оптимизированную сборку релиза и получил:

C++ (Optimised): 0ms

Это неудивительно, поскольку цикл пуст, и переменная управления не используется вне цикла, поэтому оптимизатор удаляет его полностью. Чтобы избежать этого, я объявил i как volatile со следующим результатом:

C++ (volatile i): 2932ms

Я предполагаю, что реализация C # также удалила цикл и что 720 мс - это что-то еще; это может объяснить большую часть различий между таймингами в первом тесте.

Что делает Delphi, я не могу сказать, вы можете посмотреть на сгенерированный код сборки.

Все вышеперечисленные тесты на AMD Athlon Dual Core 5000B 2.60GHz, на Windows 7 32bit.

9 голосов
/ 18 апреля 2010

Если это задумано как эталонный тест, он исключительно плохой, так как в обоих случаях цикл можно оптимизировать, поэтому вам нужно посмотреть на сгенерированный машинный код, чтобы увидеть, что происходит. Если вы используете режим выпуска для 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
7 голосов
/ 18 апреля 2010

TickCount не является надежным таймером; Вы должны использовать класс .Net Stopwatch. (Я не знаю, что такое эквивалент Delphi).

Кроме того, вы используете сборку релиза?
У вас есть отладчик?

4 голосов
/ 18 апреля 2010

Компилятор 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
3 голосов
/ 18 апреля 2010

Вы сравниваете собственный код с кодом VM JITted, и это не честно. Собственный код будет ВСЕГДА быстрее , поскольку JITter не может оптимизировать код, как это делает нативный компилятор.

Тем не менее, сравнение Delphi с C # совсем не справедливо , двоичный файл Delphi всегда будет выигрывать (быстрее, меньше, без каких-либо зависимостей и т. Д.).

Кстати, я, к сожалению, поражен тем, что многие постеры здесь не знают об этих различиях ... или, может быть, вы просто ранили некоторых фанатов .NET, которые пытаются защитить C # от всего, что там показано - лучшие варианты из есть .

2 голосов
/ 18 апреля 2010

это разборка 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 

как видите, это пустая трата процессора. EDIT:
РЕЛИЗ:

   //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 
   //}

EDIT:
и это версия 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
    }
1 голос
/ 18 апреля 2010

Вы должны прикрепить отладчик и взглянуть на машинный код, сгенерированный каждым.

0 голосов
/ 29 августа 2012

В Delphi условие прерывания вычисляется только один раз до начала цикла, тогда как в C # условие прерывания вычисляется снова при каждом проходе цикла.

Вот почему цикл в Delphi быстрее, чем в C #.

0 голосов
/ 19 апреля 2010

Delphi почти наверняка оптимизирует этот цикл для выполнения в обратном порядке (т. Е. DOWNTO с нуля, а не с нуля) - Delphi делает это всякий раз, когда решает, что это «безопасно», предположительно потому, что вычитание или проверка на ноль выполняется быстрее, чем сложение или проверка на ненулевое число.

Что произойдет, если вы попытаетесь в обоих случаях указать циклы для выполнения в обратном порядке?

0 голосов
/ 19 апреля 2010

"// 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);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...