Delphi-PRAXiS
Seite 1 von 5  1 23     Letzte »    

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Delphi Performance Vergleich zu C# (https://www.delphipraxis.net/202612-delphi-performance-vergleich-zu-c.html)

4dk2 22. Nov 2019 08:39

Delphi-Version: 10.1 Berlin

Delphi Performance Vergleich zu C#
 
Moin zusammen,
Hauptsächlich programmier ich zwar nur noch mit C#,
Aber weil ich dort wegen nem Performance-Problem, einige Basic Tests gemacht habe,
hat mich auch interessiert, wie die Performance im Vergleich zu Delphi aussieht.
Und es hat mich doch sehr verwundert :shock::shock::shock:

Vorne Weg:
Für den Test bei C# hab ich die neueste(last patch) VS2019 Ide genommen,
Target App: Standard 4.7.2, Konsole, x86, Release (kein debugging...)
(Zeit messen per Stopwatch)

Für den Test unter Delphi:
XE10.1(Berlin) Konsole, X86, Kein Debugging, Optimierung Aktiv
(Zeit messen per JclCounter)

Zusatzttest:
Auch unter Delphi7 gemacht.


Der Test der mich interessiert hat, ist anfür sich recht simple:

Delphi-Quellcode:
// JCL_DEBUG_EXPERT_GENERATEJDBG OFF
// JCL_DEBUG_EXPERT_INSERTJDBG OFF
// JCL_DEBUG_EXPERT_DELETEMAPFILE OFF
program BenchmarkCallSpeed;

{$APPTYPE CONSOLE}

{$R *.res}

uses

  System.SysUtils, JclCounter;

var
  s:string;
  tc:TJclCounter;
  i:integer;
  itotal:integer;
  x:integer;
  totalSecs:double;
  const Iterations=1000000000;
  const TestTarget=100;

function foo(x:integer):integer;
begin
  result:=x*3;
end;

function fooInline(x:integer):integer;inline;
begin
  result:=x*3;
end;

begin
  try
    totalSecs:=0;

    tc:=TJclCounter.Create(true);
    Writeln('Start');
    for itotal := 1 to TestTarget do
    begin
      x:=3;

      tc.Stop;
      tc.Start;
      for i := 0 to Iterations-1 do
      begin
        x:=foo(x);
        //x:=fooInline(x);
      end;
      tc.Stop;
      totalSecs:=totalSecs+tc.ElapsedTime;
      Writeln('Step: '+itotal.ToString()+' = '+tc.ElapsedTime.ToString()+ ' sec X: '+IntToStr(x));
    end;
    Writeln('TOTAL~: '+(totalSecs/TestTarget).ToString());

    tc.Free;

    Writeln('ENDE');
    Readln(s);


  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.
Ich glaub das ganze ist ja so simpel, dass es jeder verstehen sollte :lol:
Ergebnis ist jetzt folgendes:


EDIT!!!!! Die Zeiten angepasst, nachdem X auch noch benutzt wird!

DELPHI XE10.1:
100 Tests
Durchschnitt : 2,360 sekunden
CPU auslastung : 35%~

DELPHI 7:
100 Tests
Durchschnitt : (neuer kommt noch) sekunden
CPU auslastung : 35%~

C#
100 Tests
Durchschnitt : 0,7227 sekunden
CPU auslastung : 35-50%~

C# .Net Core 3.0
100 Tests :
Durchschnitt : (neuer kommt noch) sekunden
CPU auslastung : 35-50%~




Ich finde das extrem krass!
Ich hätte ja gesagt, wegen JIT Code vs Native, wäre da Delphi wahrscheinlich schneller, bzw gleichauf.
aber 4x langsamer?
Was meint ihr dazu?


EDIT:
Falls es jemand selber mit C# vergleichen will...
Wichtig ist aber! Auf Release Testen! das macht nen gewaltigen Unterschied aus.
Code:
public static int Foo(int x)
        {
            return x * 3;
        }

        static void Main(string[] args)
        {

            //Dauertest von Normal

            const int Iterations = 1000000000;
            int x = 0;
            int testtarget = 100;
            double timeges = 0;
            System.Diagnostics.Stopwatch sw;

            Console.WriteLine($"Start:");
            Console.WriteLine($"init: {Foo(3)}"); //JIT init, erste ergebnis sonst evtl verfälscht

            for (int itotal = 1; itotal <= testtarget; itotal++)
            {
                x = 3;
                sw = System.Diagnostics.Stopwatch.StartNew();
                for (int i = 0; i < Iterations; i++)
                {
                    x = Foo(x);
                }
                sw.Stop();
                timeges = timeges + sw.ElapsedMilliseconds;
                Console.WriteLine($"Step: {itotal} = {sw.ElapsedMilliseconds/1000d} X: {x}" );
            }
            Console.WriteLine($"TOTAL: {timeges / testtarget/1000d} ms~");

        }
Edits:
-.Net Core 3.0 hinzugefügt
-Stevie hat darauf hingewiesen, das ja wegen optimierung, die X variable wenigstens 1x benutzt werden muss, daher angepasst, fürs archiv XD

Uwe Raabe 22. Nov 2019 09:00

AW: Delphi Performance Vergleich zu C#
 
Ändere mal die Deklaration der Funktion auf inline:

Delphi-Quellcode:
function foo(x:integer):integer; inline;

Bernhard Geyer 22. Nov 2019 09:02

AW: Delphi Performance Vergleich zu C#
 
Mach mal deine funktion inline.

Solche sehr einfachen Tests sind immer mit Vorsicht zu genießen.
I.d.R. sind alle modernen Umgebungen im Alltag gleich schell, da oft eher das "drumherum" wie Framework (VCL gegen - Was ist gerade bei C#/.NET "das Hippe Ding" oder
die Implementierungsalgortihmen der relevantere Teil sind.


Bei C# könnte die "numbercrunshing" Compileroptimierungen für moderne Prozessoren hier eingiges Ausmachen.

4dk2 22. Nov 2019 09:08

AW: Delphi Performance Vergleich zu C#
 
hmmm, okaay, dann ist delphi gleichauf:

DELPHI XE10.1 (inline):
100 Tests
Durchschnitt : 0,3567 sekunden
CPU auslastung : 35%~

Bernhard Geyer 22. Nov 2019 09:14

AW: Delphi Performance Vergleich zu C#
 
Zitat:

Zitat von 4dk2 (Beitrag 1451760)
hmmm, okaay, dann ist delphi gleichauf:

Oft sind es die kleinen Dinge.
Auch eine const-Angabe hilft teilweise.
Oder in diesem Fall ein var-Übergabe damit gar (fast) keine zusätzlichen Variablen/Stack/.... benötigt werden.

Hate "damals" zu D6 Zeiten unsere Anwendung Unicode-Enabled.
Eine ergänzung von const an allen String-Übergabeparametern hatte hier schon auch ind der kompletten Anwendung merklich Geschwindigkeit gebracht.

Für Sowas empfiehlt sich immer eine Performance-Analyse mit AQTime um die relevanten stellen gleich zu finden.

4dk2 22. Nov 2019 09:22

AW: Delphi Performance Vergleich zu C#
 
Zitat:

Zitat von Bernhard Geyer (Beitrag 1451759)
I.d.R. sind alle modernen Umgebungen im Alltag gleich schell, da oft eher das "drumherum" wie Framework (VCL gegen - Was ist gerade bei C#/.NET "das Hippe Ding" oder
die Implementierungsalgortihmen der relevantere Teil sind.

Eigendlich hat Delphi garnix mit meinem Problem zu tun gehabt.
Es ging darum, dass ich Performance Probleme bei Interfaced aufrufen hatte.
Deswegen hab ich unter C# Normal/Interfaced/Delegate/Events verglichen...
Delphi war dann Just4Fun :stupid:

Zitat:

Zitat von Bernhard Geyer (Beitrag 1451759)
Bei C# könnte die "numbercrunshing" Compileroptimierungen für moderne Prozessoren hier eingiges Ausmachen.

Hab mal danach gesucht, aber nix gefunden?

4dk2 22. Nov 2019 09:27

AW: Delphi Performance Vergleich zu C#
 
Zitat:

Zitat von Bernhard Geyer (Beitrag 1451762)
Zitat:

Zitat von 4dk2 (Beitrag 1451760)
hmmm, okaay, dann ist delphi gleichauf:

...
Oder in diesem Fall ein var-Übergabe damit gar (fast) keine zusätzlichen Variablen/Stack/.... benötigt werden.

Delphi-Quellcode:
procedure fooVar(var x:integer);//inline;
begin
  x:=x*3;
end;

procedure fooOut(x:integer;out xout:integer);//inline;
begin
  xout:=x*3;
end;
falls du das so meintest, mit und ohne inline, sind die langsam, langsamer als die normale ohne inline (2,1 sek)

Stevie 22. Nov 2019 09:55

AW: Delphi Performance Vergleich zu C#
 
Halt stop!

Wenn du Foo inline markierst, wird der Code schneller, weil er nix mehr ausführt (siehe H2077 in den Compilermeldungen) außer einer leeren Schleife. Es wird nix mit x gemacht also spart er sich auch die Multiplikation. Ein beherztes Writeln(x) nach der Schleife und der code ist wieder langsam. Das liegt einfach daran, dass der Delphi Compiler schrottigen Code erzeugt und Register nicht optimal nutzt.

Aus dem geinlinetem
Delphi-Quellcode:
x:=Foo(x)
Aufruf wird folgendes:

Delphi-Quellcode:
Project351.dpr.38: x:=foo(x);
004EE8DD 8B155C944F00     mov edx,[$004f945c]
004EE8E3 8D1452           lea edx,[edx+edx*2]
004EE8E6 89155C944F00     mov [$004f945c],edx
Die Variable wird also nicht einfach im Register gehalten sondern unnötigerweise immer zurück geschrieben und gelesen. Den Code und die globalen Variablen in eine Routine zu verlagern hilft übrigens auch nicht. Die Multiplikation direkt durchzuführen hat übrigens denselben ungünstigen Assembly Code zum Ergebnis.

Edit: Wenn
Delphi-Quellcode:
Writeln(x)
direkt vor oder nach dem
Delphi-Quellcode:
sw.Stop
steht, dann ist der Assembly Code optimal, denn dann kann x im Register gehalten werden.

Uwe Raabe 22. Nov 2019 10:12

AW: Delphi Performance Vergleich zu C#
 
Zitat:

Zitat von Stevie (Beitrag 1451774)
Ein beherztes Writeln(x) nach der Schleife und der code ist wieder langsam.

Nicht in 10.3.3 Rio. Da liegen die Werte mit und ohne Writeln(x) annähernd gleichauf.

Übrigens bekomme ich ähnliche Werte, wenn ich den Schleifenbody komplett auskommentiere 8-)

Delphi-Quellcode:
program BenchmarkCallSpeed;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  System.Diagnostics;

function foo(x:integer):integer; inline;
begin
  result:=x*3;
end;

const
  Iterations=1000000000;
  TestTarget=100;

var
  s:string;
  tc:TStopwatch;
  i:integer;
  itotal:integer;
  x:integer;
  totalMSecs:Int64;
begin
  try
    totalMSecs:=0;

    tc:=TStopwatch.Create.Create;
    Writeln('Start');
    for itotal := 1 to TestTarget do
    begin
      x:=3;

      tc.Reset;
      tc.Start;
      for i := 0 to Iterations-1 do
      begin
        x:=foo(x);
      end;
      tc.Stop;
      totalMSecs := totalMSecs + tc.ElapsedMilliseconds;
      Writeln(x);
      Writeln('Step: '+itotal.ToString()+' = '+tc.ElapsedMilliseconds.ToString()+ ' ms');
    end;
    Writeln('TOTAL~: '+(totalMSecs/TestTarget).ToString());

    Writeln('ENDE');
    Readln(s);
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

Stevie 22. Nov 2019 10:15

AW: Delphi Performance Vergleich zu C#
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1451780)
Zitat:

Zitat von Stevie (Beitrag 1451774)
Ein beherztes Writeln(x) nach der Schleife und der code ist wieder langsam.

Nicht in 10.3.3 Rio. Da liegen die Werte mit und ohne Writeln(x) annähernd gleichauf.

Dann schreib doch mal das
Delphi-Quellcode:
Writeln(x)
hinter das
Delphi-Quellcode:
Writeln('Step: '+itotal.ToString()+' = '+tc.ElapsedMilliseconds.ToString()+ ' ms');
:roll:

Der Punkt bleibt: der Delphi Compiler optimiert relevante Stellen nicht, nur weil er später den Wert noch benötigt und ihn bis dahin nicht im Register halten kann.
Er könnte problemlos für die Schleife mit dem Register arbeiten und danach einmal das mov machen.

Erzähl mir nochmal einer, dass nativ kompiliert so viel geiler ist als diese teuflische managed Zeugs! :evil:


Alle Zeitangaben in WEZ +1. Es ist jetzt 06:03 Uhr.
Seite 1 von 5  1 23     Letzte »    

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz