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

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# дает приблизительно 3 775 мс

5
задан isa 18 April 2010 в 15:25
поделиться

9 ответов

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

1
ответ дан 18 December 2019 в 05:13
поделиться

Компилятор 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
ответ дан 18 December 2019 в 05:13
поделиться

это дизассемблер 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 

как вы видите, это пустая трата процессора.
РЕДАКТИРОВАТЬ:
РЕДАКТИРОВАТЬ:

   //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
    }
2
ответ дан 18 December 2019 в 05:13
поделиться

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

Это интересно.

while (++ i! = X) не то же самое, что for (; 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);
-1
ответ дан 18 December 2019 в 05:13
поделиться

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

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

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

7
ответ дан 18 December 2019 в 05:13
поделиться

Delphi почти наверняка оптимизирует этот цикл для выполнения в обратном порядке (т. Е. ВНИЗ ноль, а не ОТ нуля) - Delphi делает это всякий раз, когда определяет, что он "безопасен". "делать, предположительно потому, что вычитание или проверка на ноль быстрее, чем сложение или проверка на ненулевое число.

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

0
ответ дан 18 December 2019 в 05:13
поделиться

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 # и запустите исполняемый файл в \ bin \ release и \ 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,60 ГГц, на 32-битной Windows 7.

29
ответ дан 18 December 2019 в 05:13
поделиться

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

При этом сравнивать Delphi против C# вообще нечестно, бинарник на Delphi будет выигрывать всегда (быстрее, меньше, без всяких зависимостей и т.д.).

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

3
ответ дан 18 December 2019 в 05:13
поделиться
Другие вопросы по тегам:

Похожие вопросы: