Geschwindigkeitsvergleich C, Basic, Pascal Unerwartetes Ergebnis?!

  • Hi


    Ich brauche für ein Projekt ein paar schnelle Berechnungen, deshalb hab ich einen Geschwindigkeitsvergleich gemacht, damit ich dann die schnellste Variante verwenden kann.
    Aus diesem Grund hab ich in Dev-C, FreeBasic und FreePascal DLL´s geschrieben, die die benötigten Rechenoperationen verwenden und die Zeit gemessen


    Allerdings ist das Ergebnis ganz anders, als erwartet!
    Ich dachte, daß es die Reihenfolge C, Pascal, Basic wäre, aber es ist genau andersrum:

    Code
    Basic: 631ms
    Pascal: 1221ms
    C: 2538ms


    Hab ich was übersehen, oder muß der Code in C/Pascal anders geschrieben werden; gibt es weitere Optimierungen bei den Compilern...?
    Oder ist der FreeBasic Compiler einfach sehr gut?


    C-Code:


    Pascal-Code:


    Basic-Code:


    Würd mich interessieren, was die Profis dazu sagen...


    Edit: Kann jemand testen, welche Ergebnisse mit anderen Compilern erreicht werden (VisualC++, Delphi...)


    Danke

  • In C und Pascal hast du mit pow() bzw. power() pro Schleifendurchgang 2 Funktionsaufrufe mehr.
    Das kann ganz schön ausbremsen. Vllt. wäre sqrt( fX * fX + fY * fY ); schneller!?


    Lg Ahnungslos

    Wer andern eine Grube gräbt, der hat ein Grubengrabgerät...

  • Danke, das hilft natürlich gewaltig, da ich sowieso nur x^2 benötige...!


    Das Ergebnis ist nun:

    Code
    + P: 723.776904606591
    > B: 632.287623147635
    - C: 1532.69368034205


    Da sieht Pascal nun etwas besser aus, aber C hinkt trotzdem noch extrem hinterher...


    Edit: Habs jetzt auch mit GNU GCC versucht und das Ergebnis ist: 2671ms 8|

  • Klar gibt es es nicht die Performance einer Sprache sondern die eines spezifischen Compilers.
    Soweit ich mich erinnern kann war der MinGW-Compiler in Dev-C hoffnungslos veraltet und ein damaliger Vergleich von mir zwischen diesem Compiler und dem VS2008 brachte massive Performance-Steigerungen mit sich.
    Momentan hab ich grad keinen Compiler installiert und kann daher nicht testen.


    Vielmehr interessiert mich aber die Frage WIE du gemessen hast und woher diese, von dir angegebenen, Werte stammen?


    Wenn du aber das Ziel hast so schnell wie möglich zu rechnen dann würde ich als allererstes mal auf Algorithmenebene optimieren:

    • Warum lässt du die Schleife im Gradmaß durchlaufen wenn du die Schleifenvariable intern sofort wieder in Radiant umrechnest? - Warum die Schleife nicht gleich im Bogenmaß durchgehen - spart schonmal eine Zuweisung, 1 Multiplikation und 1 Division.
    • atan2(sin($fa) * 100, cos($fa) * 100) = atan2(sin($fa), cos($fa)) - wieder 2 Multiplikationen verschwunden.
    • Wie Ahnungslos schon ansprach: Oftmals (wenn der Compiler soetwas nicht erkennt) ist ein x*x schneller als ein x^2 (auch in AutoIt so).
    • PI kann auch als globale Konstante deklariert werden - spart eine Zuweisung bei jedem Funktionsaufruf.
    • Du berechnest fA = atan2(fY, fX) verwirfst das Ergebnis aber wieder sofort da du fA in der nächsten Zeile wieder überschreibst.
    • Da Rückgabewert = Eingabeparameter könnte man die Funktion auch auf die Return-Anweisung zusammenschrumpfen.


    Ich denke mal das war nur ein Test und nicht wirklich als echte Berechnungsfunktion zu interpretieren.
    Aber vielleicht solltest du doch erstmal die richtige Funktion erstellen bevor du Geschwindigkeitsvergleiche zwischen verschiedenen Programmiersprachen/Compilern machst.
    Mag ja z.B. sein das der Basic-Compiler die sinnlose Atan-Berechnung komplett raushaut da sie eh nicht verwendet wird oder sowas.

  • Danke für die Antwort!


    Ich bin schon etwas schlauer:
    FreeBasic scheint überflüssige Rechenoperationen zu optimieren.
    z.B.: wird anscheinend "Pi/180" nur einmal berechnet und der Wert dann in der Schleife verwendet, während Pascal jedesmal neu rechnet.
    Desweiteren bringt Pi als Konstante in Pascal auch einiges an Geschwindigkeit


    Ich wollte mir eigentlich ersparen, in mehreren Sprachen die Algorithmen zu schreiben, sondern schon vorher die Geschwindigkeit testen.
    Wahrscheinlich muss ich aber wirklich fertige Funktionen zum Vergleich verwenden...


    Ich sags jetzt mal so: Wenn ich zu faul bin, meine Algorithmen selber zu optimieren, dann ist FreeBasic die bessere/einfachere Lösung :D


    E

  • Übrigens könnte es sein, dass bei Schleifen der Vergleich < schneller ist als <=. Außerdem schreibe mal ++wert statt wert++ (eigentlich sollte das der Compiler schon optimieren, aber man weiß ja nie)


    Edit: Wie viele Durchläufe hast du eigentlich gemacht? C(pp) bei mir: (VS 2010)
    1000: ~150ms
    10000: ~1350ms
    Code:


    PPS: Mit der Option "Geschwindigkeit optimieren" (= /O2) komme ich für 10000 Durchläufe auf 0.06ms (Da die Schleifen ganz rausgekürzt werden :P)

  • Ich hab 10000 Durchläufe gemacht


    Ich hab nun dieses Ergebnis:

    Code
    Pascal: 651.49593034869
    Basic: 606.111111887125
    Dev_C: 1614.23197641041
    GCC: 1253.04417181513


    Mittlerweile hab ich auch einen "echten" Algo in FreeBasic und FreePascal verglichen und das Ergebnis spricht mit 410ms zu 430ms leicht für FreeBasic
    C scheidet eigentlich eh aus, da mir das etwas zu kompliziert ist :whistling:


    Desweiteren ist auch die Größe der Dll bei FreeBasic ungeschlagen:

    Code
    Pascal: 78 KB
    Basic: 8 KB
    Dev_C: 15 KB
    GCC: 25 KB


    Danke für die Antworten...

  • Die Größe beträgt bei mir mit Code:Blocks und MinGw 6,50 KB. Ich aber deutlich langsamer als Basic und nach dem Weglassen von pow() ungefähr gleich schnell wie Pascal. Wenn ich allerdings "Pi/180" als Konstante verwende, dann wird es wieder langsamer. Vielleicht solltest du den richtigen Algo nun auch noch in C kompilieren, damit nicht die besseren Optimierungen in den Kompilern über die bessere Sprache siegen. :)

  • Verwende am besten ASM mit SSE2-Instruktionen, wenn dir Geschwindigkeit so wichtig ist ;)


    Bei FreeBasic hab ich bereits die Compilerschalter "-fpmode Fast -fpu SSE" gefunden - das ist sozusagen nochmal der Turbo: 454ms :rock:
    Deshalb werd ich für mein Projekt FreeBasic verwenden!
    Für einen entgültigen Vergleich mit "richtigen" Algos fehlt mir aber die Zeit...


    E

  • Was nutzt du als Free Basic IDE ?


    Nach dem ganzen Kram hier ist es für mich auch auf ein Mal interessant geworden (obwohl ich normal kritische stellen mit InlineASM mache.)
    Damit kann man dann auch etwas komplexere Sachen schnell in einer Kleinen Dll berechnen und diese per Memorycall direkt mitnehmen...


    Da sieht man wieder die Unfähigkeit der ganzen Compiler. (Außer der von FreeBasic. Obwohl 8kb doch noch ziemlich viel sind...)
    Wenn man den Spaß komplett in Asm macht dürfte das unter 1000 Bytes ausfallen.
    Jede Dll ist allerdings größer als die andere. Da drängt sich die Frage auf: Was machen die Dlls sonst noch außer das Berechnen ?
    Bei 70KB. Da passt ein halbes Computerspiel rein...


    lg
    Mars(i)

  • FbEdit hab ich mir heute Mittag schonmal angesehen.
    Nachdem ich verstanden habe, wie man den Compiler einbindet lief es super.


    :thumbup:

  • Hi,
    mal wieder ein schönes Beispiel, dass "vermeintlich" schnellere Programmiersprachen nicht immer soooo gut abschneiden ^^
    Weiterhin auch ein schönes Beispiel, dass


    EIN SCHNELLER ALGORITHMUS NICHT ZU TOPPEN IST!!! <--das ist ein Link :D



    In C (++) bietet es sich übrigens auch an, die Sinus/Cosinus usw- Funktionen als einfachen Assemblerbefehl "Inzulinen" :rolleyes: , das spart ca Faktor 3 beim ausführen,
    also aus sinf($var)


    _asm
    FLD ($float) ;load $float in Stack
    FSIN ;sinus
    FST ($float) ;speichert sinus in
    _asmend


    so (oder ähnlich, bin kein C-Freak) geht das, habe diesen Trick schon einmal benutzt, um c++ - Dll´s zu beschleunigen

  • VC++ optimiert zu gut. :D


    Da letztendlich alle Ergebnisse wieder verworfen werden und der Rüchgabewert der eine Parameter ist, wird alles weggestrichen.


    Code
    .text:00401000 ; __stdcall Calc(x)
    .text:00401000 _Calc@4 proc near ; CODE XREF: _main+15p
    .text:00401000
    .text:00401000 arg_0 = dword ptr 4
    .text:00401000
    .text:00401000 mov eax, [esp+arg_0]
    .text:00401004 retn 4
    .text:00401004 _Calc@4 endp
  • die Optimierung über den Algorithmus (s. meinen vorherigen Post) ist ca. 10x schneller als der ursprüngliche Code.
    Da optimieren auch diverse Compilerschalter nichts mehr weg^^. Meine Rede seit 30 Jahren...."Ein langsames Programm wird auch nicht besser, wenn es auf einem schnelleren Rechner (Prozessor) ausgeführt wird!"
    Der schnelle(re) Algorithmus macht immer das Rennen...


    Wenn man sich mal ernsthaft überlegt, wie lahmar***ig heutzutage ein ultramoderner 1,4 GHZ-Prozessor (512MB Ram) mit läppischen 480x800 Pixeln in einem "Mobiltelefon" umgeht (von "Multitasking" mal garnicht zu reden), dann wundert gar nichts mehr. Da wird mehr Code emuliert als sonstwas. Aber über Windows meckern! Das war vor 10 Jahren wenigstens schon Multitaskingfähig und man konnte halbwegs damit arbeiten^^, auch mit 1,4Ghz :thumbsup:
    Der PIII Tualatin ist nicht umsonst als KING in die Prozessorgeschichte eingegangen :thumbsup:
    Genau so einen schiesse ich mir gleich bei Ebay, der möbelt dann meinen 1,2Ghz-Celeron noch mal richtig auf :rofl:

  • Ich habe gestern, weil ich das mal vergleichen wollte, geschaut, was D, Delphi und verschiedene C-Compiler aus einer kleinen BubbleSort-Funktion machen. Ist natürlich nur ein Aspekt bezüglich Optimierungen und andere Algorithmen könnten ganz anders ausfallen, aber immerhin ist es ein Vergleich :P


    Delphi: 6.5s
    Free Pascal: ~20s
    D: 7s
    MinGW: 8s
    MS C++: 8.5s
    TCC: ~19s
    PureBasic: >20s


    MS C++ hat zwar alle Loops aligned, allerdings lag dessen Problem sowie bei D an der Komplexität der Addressierung des Arrays:


    MS C++: mov eax,[edx*4+offset array]
    D: mov eax,[edx*4+edi]


    Delphi hingegen hat sich den Loop zunutze gemacht und über ein einzelnes Register addressiert, das im inneren Loop immer inkrementiert und im äußeren dann zurückgesetzt wurde.


    Der Code von TCC war im Gesamten vermurkst, überall Sprünge von x nach y nach z und zurück..
    Zu den anderen fällt mir gerade nichts mehr ein :O