Geschwindigkeitsvergleich C, Basic, Pascal Unerwartetes Ergebnis?!

  • Könntest du bitte die Sourcecodes dazu posten? würd mich interessieren...

    Ich hab noch einen kleinen Vergleich gemacht.
    Diesmal wird wirklich gerechnet :D
    Und zwar soll eine zackige Linie geglättet werden

    Die Variable $iStep ist die Anzahl der Durchläufe und $iPoints die Anzahl der Punkte auf dem Path (sollte nicht viel größer eingestellt werden, da sich sonst GDI+ überanstrengt ;))

    Beim ersten Test, also AutoIt, dauert es etwas länger, denn das AutoIt-Array muss auch noch in ein DllStruct umgewandelt werden, damit GDI+ es zeichnen kann.
    gemessen wird aber nur die tatsächliche Zeit der Smooth Funktion

    Die Ergebnisse:

    Code
    !==============================================
    > AutoIt: 7299.84146029733
    > FreeBasic: 7.10509296572609
    > Lazarus: 8.35301693371644
    > FreePascal: 9.44924564434865
    > DevC: 8.16165183005103
    > MinGW: 7.96330259851462
    !==============================================

    Auch hab ich mit der von Andy vorgeschlagenen ASM Sin und Cos Variante experimentiert, jedoch wurde meine Freebasic-Dll dadurch etwas langsamer
    verwendet hab ich das hier:

    E

  • Hmm, war dein FB-Code zuvor SSE-optimiert? Dann ist dein ASM nicht so performant denke ich.

  • eukalyptus,
    hattest du den Algorithmus in meioem Link mal ausprobiert?
    https://autoit.de/index.php?page…4474#post214474

    @progandy
    ja, kann schon sein, erstens verarbeitet ein gut (ich gehe davon aus dassdie Compiler gut optimieren) umgesetzter SSE-code 4 floats auf einen Schlag (incl Bonus durch loop-unrolling) , zweitens ist auch beim "einfachen" ersetzen von FPU-Befehlen mit SSE-Pendant der Prozessor schneller (rechnet aber auch nur einfach genau).

  • Hi,

    Zitat

    Auch hab ich mit der von Andy vorgeschlagenen ASM Sin und Cos Variante experimentiert, jedoch wurde meine Freebasic-Dll dadurch etwas langsamer
    verwendet hab ich das hier:


    Das ist deswegen langsamer, weil Freebasic diesen "Trick" schon benutzt! Während C (und auch C++) die Sinf()-Funktion aus der Bibliothek callen (keine Ahnung was die da ausrechnen, hab nach ner Viertelstunde durch den Speicher springen aufgegeben...) , setzt Freebasic den FSIN direkt ein!

    Ich habe mir die 3 Dll´s mal angeschaut, Freebasic bastelt ja wirklich einen feinen Code! Was mich echt erstaunt hatte, Pascal war der einzige Compiler, der Pi statt über eine Variable mit dem Prozessorbefehl FLDPI direkt geladen hatte, SOWAS nenn ich mal ne Optimierung^^
    Pascal erstellt fast den gleichen Code wie Basic, allerdings werden dort nach jedem FSIN/FCOS usw. nochmal die Exceptions abgefragt, in langen Loops ist das natürlich für die Laufzeit tödlich

    Was keiner der Compiler macht, ist die eigendliche Geschwindigkeitssteigerung!
    Alle Zugriffe auf die Variablen erfolgen über den Speicher, kein einziger Compiler lädt (ausser Basic wenigstens teilweise) die Variablen in die Prozessorregister und arbeitet damit!
    Selbst wenn die Variablen im Cache stehen, ist z.B. ein
    cmp ebx, [ebp+var_18]
    wesentlich langsamer als ein
    cmp ebx, eax ;vor dem loop ein MOV eax,[ebp+var_18] vorrausgesetzt^^


    Übrigens wird beim Kantenglätten-Beispiel vom "schnellen" DevC nur der "langsame" FPU-Code verwendet, Freebasic benutzt zwar SSE-Befehle, aber (uuuurgsss) NICHT skalar, d.h. es wird nur eine statt 4 Zahlen gleichzeitig berechnet!
    Wie man z.B. beim "Tunnelflug" im ASM_Tut nachschauen kann, beschleunigt sich der FPU-Code bei Verwendung von skalarem Einsatz von SSE um ca Faktor 3-4!
    Da das "Speicherrumgehopse" bei handgeschriebenem ASM-Code auch entfällt, schätze ich eine mögliche Beschleunigung mal locker auf Faktor 6 bis 8, wenn nicht sogar 10 gegenüber dem DevC-Code. Und das ist jedenfalls eine Hausnummer!

  • Hey ich hab mal selber mal nen Speedtest gemacht und zwar zwischen C,C++ und AutoIt... weil ich ja keine anderen Sprachen wie Pascal so kann.
    Ich hab ne for-schleife von 0 zu 1.000.000 durchlaufen lassen und diese Ergebnisse bekommen:

    C 77,390 s
    C++ 132.977 s
    AutoIt 133 s

    Wie ist des möglich dass AutoIt so schnell ist wie C++???

    Bei C hab ich mit printf,
    Bei C++ mit cout,
    AutoIt ein Label und mit SetData immer wieder bearbeitet...

  • Wenn ich mir den Thread so durchlese scheint es auf 2 Fazits hinauszulaufen:
    1. Die Sprache(!) Basic ist schneller als C/C++
    2. Assembler ist DAS Allheilmittel für performanten Code.

    Beide Punkte bereiten mir allerdings ziemliche Bauchschmerzen.
    Zum 1. Punkt:
    Um zu zeigen das die Performance in erster Linie nicht von der Sprache sondern vom Compiler abhängt habe ich nun in Eukalyptus' Vergleich noch den Visual C++ Compiler hinzugenommen, ihn mit dem unbearbeiteten Code für DevC gefüttert und mal munter optimieren lassen.
    Hier meine Ergebnisse des ganzen Tests:

    Spoiler anzeigen
    Code
    !==============================================
    > AutoIt: 4326.98
    > FreeBasic: 4.92
    > Lazarus: 6.94
    > FreePascal: 6.98
    > DevC: 6.66
    > MinGW: 6.73
    > Visual C++: 1.84
    !==============================================

    Man kann sich dann vorstellen was der Intel-Compiler noch daraus gebastelt hätte...
    Die Visual-C++ Dll ist mit 8kb übrigens auch noch die kleinste aller Dlls. (bearbeitetes Vergleichsprojekt von Eukalyptus im Anhang)
    Man sieht also deutlich das es prinzipiell eigentlich nur auf den Compiler ankommt und keiner der C kann auf einmal auf Basic umsteigen muss weil der FreeBasic-Compiler in einem Test mal schneller war als der MinGW.

    Nun zum 2. Punkt:
    Wer, wie ich, größere Projekte zu schreiben hat der sucht sich seine Sprache nicht nur aufgrund der Performance sondern auch aufgrund der Produktivität aus.
    Aktuell schreibe ich Programme mit Python und lagere die wenigen Performance-kritischen Stellen in andere compilerbasierte Sprachen aus.
    Bei den meisten Projekten will ich nicht mehr auf objektorientierte Programmierung verzichten und so kommt es das ich aufgrund dessen nie C für meine Projekte einsetze sondern mindestens auf C++ ausweiche.
    Wenn mir nun schon C zu umständlich zu programmieren ist kann man sich vorstellen was dann erst ein Umstieg auf Assembler bedeutet...
    Wenn überhaupt hat Assembler nur in kleinen extrem Performance-kritischen Codeschnipseln seine heutige Daseinsberechtigung.
    Die guten Compiler sind, entgegen der bisherigen Darstellung hier, nicht dumm und nur für die wirklich besten Assembler-Profis besteht eine Chance die Compiler wirklich zu schlagen.
    Es ist keine Allgemeingültigkeit das Assembler schneller ist als die Hochsprachen.
    Von den Nachteilen Assemblers' wie dem stark gesteigerten Programmieraufwandes und der schlechten Portabilität des Codes reden wir dabei noch gar nicht.
    Wenn es allerdings darum geht wirklich schnellen Code zu erzeugen wird schnell merken das Assembler heutzutage keinen Stellenwert mehr besitzt.
    Wer schonmal für einen Hochleistungsrechner programmiert hat wird schnell wissen was gemeint ist.
    Mit Assembler bekommt man vielleicht den schnellsten sequentiellen Code der für ein Problem möglich ist.
    Wirklich schnell werden heutzutage Programme aber erst durch Parallelisierung.
    Threading für den Quadcore-Prozessor für zu Hause mag mit Assembler durchaus möglich, aber sicher nicht wirtschaftlich sein.
    Spätestens aber wenn es um Multiprocessing per MPI auf nem Clusterrechner geht ist dann aber wirklich Schluss.
    In meiner aktuellen Arbeit (grob Bildverarbeitung) benötige ich eine Menge Performance.
    Assembler kommt aber nicht wegen das wahnwitzigen Arbeitsaufwandes nicht in Frage sondern aufgrund der "schlechten Performance".
    Um wirklich schnell zu sein werden die meisten meiner Algorithmen per OpenCL auf der Grafikkarte berechnet.
    Und spätestens da ist jeder Assembler-Code machtlos wenn es sich um gut parallelisierbare Aufgaben handelt.

    Um nicht falsch verstanden zu werden:
    Assembler ist die theoretisch beste Möglichkeit effektiven Code zu erzeugen.
    Der Aufwand dafür ist aber enorm und nur ein paar (@include Andy) sind wirklich dazu in der Lage.

    Da der Thread hier Leser dazu verleitet zu denken Assembler wäre die einzige Allzweckwaffe für gutes Programmieren möchte ich lediglich noch einmal folgende Punkte zur Diskussion stellen:

    • Gute Compiler sind nicht so dumm wie es scheint.
    • Große Programme sollte man auch weiterhin unbedingt in einer Hochsprache schreiben
    • Assembler sollte man nur für kleine performancekritische Bereiche verwenden.
    • Wirklich effektiven Assemblercode bekommt man nicht durch mal schnell ein Buch lesen hin
    • Assembler ist nicht die einzige Möglichkeit wirklich schnellen Code zu erzeugen.
    • Für Threading/Multiprocessing/GPU-Programmierung ist Assembler sogar denkbar ungünstig.

    Wer schnellen Code haben möchte sollte eher folgendermaßen vorgehen (am besten immer mit Profiling-Werkzeugen arbeiten):

    • Optimierung auf Algorithmenebene
    • Optimierung auf sequentieller Ebene
    • Parallelisierung des sequentiellen Codes
  • OpenCL ist doch vergleichbar mit dem was Assembler Code zusammen mit der CPU macht.
    Von daher hinkt der Vergleich imho etwas.
    Grafiklastige Anwendungen profitieren deutlich sobald man Teile auf die GPU auslagern kann.

  • OpenCL ist doch vergleichbar mit dem was Assembler Code zusammen mit der CPU macht.

    Nein
    OpenCL ist eine Hochsprache mit C-artiger Syntax für eine breite Palette an Prozessoren (CPUs, DSPs, GPUs...)
    Assembler ist eine Low-Level Sprache für einen bestimmten Prozessorbefehlssatz.

    In meiner Ausführung ging es darum das man das OpenCL-Potential praktisch nur mit Hochsprachen nutzen kann und damit dann deutlich performantere Programme erzeugen kann als mit reinem Assembler-Code.
    Prinzipiell sollte es aber möglich sein aus einem Assembler-Programm OpenCL anzusprechen.
    Der Aufwand wäre aber um Größenordnungen höher als z.B. in C und einen Performance-Vorteil würde es ebenfalls nicht bringen da die OpenCL-Berechnungen die selben bleiben.
    Es ging also darum zu zeigen das wenn man wirklich hohe Performance benötigt nicht nächtelang Assembler-Code bis auf den letzten Prozessortakt hin optimieren muss sondern viel schneller zu noch performanteren Lösungen kommen kann durch Paralellisierung.

  • --> Äquivalent zur GDI+ könnte man auch die OpenCL Dll (s) in AutoIt einbauen,
    oder zumindest eine Möglichkeit (äquivalent zu der FASM Umgebung) schaffen um mit AutoIt auf der Grafikkarte zu hantieren.
    (Dann kann ich mit meiner Karte mal bissl was rausholen. die ist 10Mal schneller als mein restlicher Rechner^^)

    Das würde wahrscheinlich sämtliche Bemühungen AutoIt iwie auf anderem Wege grafikmäßig aufzumöbeln sinnlos machen.

    Mit (Inline)Asm kann man zwar als schlechter Asm-Bastler in den meisten Fällen keine VC++ oder OpenCL Programme/Dll´s/Algorithmen schlagen,
    aber AutoIt zu 99,9%, sobald man etwas auf die Reihe kriegt.
    Und darum geht es den meisten Asm-Nutzern hier (auch wenn die Zahl relativ klein ist). AutoIt für Sachen nutzen für die es nicht gedacht ist und diese dann etwas Beschleunigen, damit es trotzdem geht.

    Mit einer 5GB Entwicklungsumgebung von C++ für die man sich anmelden muss und erstmal 3 Jahre Lernen muss, bevor man ein HalloWelt Programm hinbekommt wollen viele einfach nicht arbeiten^^

    lg
    Mars(i)

  • Du hast mit allem Recht Marsi.
    In AutoIt ist Assembler das Maß aller Dinge um seine Programme zu beschleunigen.

    Da ich aber anscheinend dennoch missverstanden wurde nochmal zusammengefasst:
    Assembler ist toll.
    Hier ging es um den Performancevergleich verschiedener Sprachen.
    Als indirektes Fazit kam hier heraus das Assembler im Handstreich alles andere schlägt.
    Das Performance aber vielschichtiger ist als ein paar trigonometrische Funktionen zu optimieren wollte ich hiermit nur noch einmal verdeutlichen.
    Keiner muss seine Programme in Assembler umschreiben um schnelle Programme zu bekommen.
    Andy hat ja ganz gut am Beispiel verdeutlicht wie man Assembler hingegen effektiv einsetzen kann.

  • Hi,
    gebe jetzt mal meinen Senf dazu:

    AspirinJunkie
    ich gebe dir in so gut wie allen Punkten Recht, besonders die Punkte "Produktivität" und "Parallelisierung" sind weitab jeglicher Diskussionsgrundlage.
    Da Programmierer heute derart billig sind, kommt es nicht mehr auf die Geschwindigkeit oder Größe des Codes (Programmes) an, sondern nur noch auf die Kosten der Codeerstellung. Time is cash.

    Bei einem "lahmarschigen" Programm (aus welchen Gründen auch immer) wird bei Kundenreklamation einfach auf einen Prozessor der nächsten (schnelleren) Generation verwiesen, schon passt es wieder. Dabei kostet der neue Rechner nur einen Bruchteil dessen, was eine "Optimierung" der Software kosten würde. Thema abgehakt.

    Dazu kommt, daß sich kaum noch jemand Gedanken über eine fehlende Programm-Funktion machen muss, irgendwo gibts immer eine passende Bibliothek. Runterladen, Funktion dazulinken, fertig. Hauptsache läuft.
    Btw, ich würde echt gerne sehen, was der Intel-Compiler aus dem Code macht *sabber*


    @Marsi
    ja, als schlechter ASM-Bastler kann man keine 20 Jahre lang immer wieder und wieder von sehr guten Programmierern optimierten C-Routinen schlagen, das ist sonnenklar.
    Aber wie schon am Beispiel gezeigt, und von AspirinJunkie angesprochen, der schnellere Algorithmus macht das Rennen^^. Und ab und zu könnte man ja auch mal die "neuen", seit 10 Jahren in der Hardware verbauten Features z.B. auch mal SSE o.ä., einsetzen.


    Jetzt was in eigener Sache, hab heute in ca. 1,5h (incl. Kaffeetrinken :D ) aus der Eukalyptus-Glättfunktion eine Assembler-DLL gebastelt.
    Hab den Autoit-Code

    Spoiler anzeigen
    [autoit]

    Func _Smooth(ByRef $aP, $iS = 1)
    Local $iI = $aP[0][0]
    For $s = 1 To $iS
    $aP[1][0] = _B($aP[$iI][0], $aP[1][0], $aP[2][0], $aP[3][0])
    $aP[1][1] = _B($aP[$iI][1], $aP[1][1], $aP[2][1], $aP[3][1])
    For $i = 2 To $iI - 2
    $aP[$i][0] = _B($aP[$i - 1][0], $aP[$i][0], $aP[$i + 1][0], $aP[$i + 2][0])
    $aP[$i][1] = _B($aP[$i - 1][1], $aP[$i][1], $aP[$i + 1][1], $aP[$i + 2][1])
    Next
    $aP[$iI - 1][0] = _B($aP[$iI - 2][0], $aP[$iI - 1][0], $aP[$iI][0], $aP[1][0])
    $aP[$iI - 1][1] = _B($aP[$iI - 2][1], $aP[$iI - 1][1], $aP[$iI][1], $aP[1][1])
    $aP[$iI][0] = _B($aP[$iI - 1][0], $aP[$iI][0], $aP[1][0], $aP[2][0])
    $aP[$iI][1] = _B($aP[$iI - 1][1], $aP[$iI][1], $aP[1][1], $aP[2][1])
    Next
    EndFunc ;==>_Smooth

    [/autoit] [autoit][/autoit] [autoit]

    Func _B($1, $2, $3, $4, $t = 0.5)
    Local $V1, $V2, $V3, $V4
    $V1 = (-$1 + 3 * $2 - 3 * $3 + $4) / 6.0
    $V2 = (3 * $1 - 6 * $2 + 3 * $3) / 6.0
    $V3 = (-3 * $1 + 3 * $3) / 6.0
    $V4 = ($1 + 4 * $2 + $3) / 6.0
    Return ($V3 + $t * ($V2 + $t * $V1)) * $t + $V4
    EndFunc ;==>_B

    [/autoit]

    genommen und einfach 1:1 in Asm übertragen. Wollte mal sehen, was SSE rausholen kann. Meine Vermutung hat sich bestätigt. 10-15x schneller als die "Compiler"-Ergebnisse, dank paralleler Berechnung von 4 Koordinatenpunkten gleichzeitig. Um mir nicht die Finger wundzutippen, habe ich die Größe der Struct um 2 Koordinaten-Punkte erweitert, das hat reichlich Code gespart. Soviel zum Thema Produktivität^^ Hat echte Vorteile, wenn man ein fauler Sack ist und sich vorher ein paar Gedanken macht :rofl:
    Geschummelt hab ich natürlich auch, die Berechnungen sind nur einfach genau (in 128 Bit passen halt nur 4x32Bit ), daher kann es dazu kommen, dass nach 10 Runden "Glätten" die Eckpunkte nicht pixelgenau mit den anderen Ergebnissen der Compiler übereinstimmen. Aber genau hier liegt der Hase im Pfeffer! Bei Faktor 10-15 (und ich hab mir nichtmal die Mühe gemacht den Code zu optimieren) schnellerer Laufzeit ist die Abweichung derart minimal, damit kann ich leben....

    Btw, die Assembler-dll ist nur 3.5K groß (*zum Intel-Compiler rüberschiel*)
    autoit.de/wcf/attachment/13713/

    Hab natürlich mit AssembleIt und dem eingebauten Debugger die SSE-Register verfolgt, endlich mal ne Anwendung dafür ^^

    /EDIT/ hatte die falsche Zip hochgeladen...

  • 10-15x schneller als die "Compiler"-Ergebnisse,

    Hab das ganze mal bei mir getestet und die Dll mit aufgenommen um zu vergleichen.
    Vor allem lasse ich die Berechnungen mehrmals (1000x) durchführen um zuverlässigere Ergebnisse zu erhalten (alles wieder im Anhang).
    Meine Ergebnisse waren dann folgende:

    Spoiler anzeigen
    [autoit]

    !==============================================
    > AutoIt: 21781.74
    > FreeBasic: 4.69
    > Lazarus: 6.46
    > FreePascal: 6.49
    > DevC: 6.49
    > MinGW: 6.51
    > Visual C++: 1.77
    > Assembler: 1.1
    !==============================================

    [/autoit]


    Warum AutoIt auf einmal so schlecht abschneidet im Vergleich zum ersten Versuch verstehe ich nicht ganz.
    Die Assembler-Dll ist hier ganz klar die schnellste Lösung. Nur den Faktor 10-15 kann ich nicht reproduzieren. So schlecht stehen die Compiler also im Vergleich zu Assembler dann doch nicht bei mir da ;)

    Um doch nochmal auf die SSE-Sache zurück zu kommen:
    Nicht nur mit Assembler kann man explizit die SSE-Möglichkeiten nutzen.
    Auch mit Hochsprachen ist dies durchaus explizit möglich (implizit geht es ja sowieso schon z.B. durch den Intel-Compiler).
    OpenCL besitzt die entsprechenden Vektordatentypen welche bei einer Ausführung auf der CPU dazu führen das dabei die entsprechenden SSE-Befehle genutzt werden.
    Das heißt SSE-Code + Threading + Plattformunabhängigkeit!
    Wär das nicht ein Anreiz für dich dich doch ein klein wenig mehr mit den Hochsprachen anzufreunden...? ;)

  • huhu AspirinJunkie,
    habe jetzt auch mal mehrere Durchläufe gefahren und bin (bis auf c++) ca. beim Faktor 6-8 rausgekommen, das deckt sich auch mit deinen Ergebnissen. Dazu später mehr^^

    Hab die asm-dll jetzt so modifiziert, dass im "inner loop" (die _b()-Funktion) nicht mehr auf Speicher zugegriffen wird. Allerdings beschleunigt das entgegen meiner Annahme nur minimal, da die Zugriffe auf die Struct (im Ram) vom Prozessor schon so gut gecached werden, daß nur der erste Zugriff auf eine Speicheradresse ca. 10 Takte braucht, jeder weitere Zugriff wird in EINEM Takt erledigt (aus dem dann im L1-Cache stehenden Speicherbereich). Da machen natürlich auch die parallen Pipelines was aus, du hattest es ja angesprochen. Wenn parallel gearbeitet werden kann, "fluppt" es einfach^^

    Leider ist der loop so kurz, dass auch ein prefetch (frühes laden vom Speicher in den L1-Cache) nichts bringt. Ich müsste den loop "unrollen" (übrigens macht der VC++Compiler das in deiner dll!), das würde aber wieder Programmieraufwand bedeuten aber ggf bis zu 50% rausholen.

    Was extrem reinhaut sind zeitlich "lange" Durchläufe, d.h. > einigen Millisekunden.
    Ich bin sicher, der windowsinterne Thread(Task)manager schaltet dann zwischenzeitlich auf andere Threads um, das merkt man besonders deutlich bei den dll´s von Lazarus und Pascal, welche den "Umweg" über das Abfangen von Exceptions nehmen. Da braucht bei einem gleichzeitigen Plattenzugriff die dll 5x so lange!
    Autoit blutet dadurch Laufzeitbedingt natürlich am meisten!

    Ich könnte auch anhand deiner Ergebnisse schwören, dass du einen Intel-Prozessor hast ;)
    Weniger an den guten Ergebnissen der Compiler-dlls, als an den "schlechten" vom asm-SSE. Ein unaligned Zugriff auf eine Speicheradresse kostet bei einem Intel-Dual-core oder Sandy-bridge Prozessor 3-8 Takte, während ein alter AMD-Prozessor das in 2 Takten abwatscht. Bei vielen unaligned Speicherzugriffen (der Algorithmus ist einfach besch*** von mir umgesetzt^^) läppert sich das natürlich.

    Zitat

    Das heißt SSE-Code + Threading + Plattformunabhängigkeit!
    Wär das nicht ein Anreiz für dich dich doch ein klein wenig mehr mit den Hochsprachen anzufreunden...?

    habs immer gesagt, JAVA ftw! Da fällt das ganze rumgeeiere im Speicher weg und das Programm läuft auch auf einer Armbanduhr oder einem Mixer!
    Bzgl. optimieren von C++-Code habe ich schon einige extrem gute Artikel von Agner Fog gelesen. Einige passend zum Code eingestreute _mm_*****-Funktionen und der Compiler setzt den C++-code perfekt in SSE um, eine 32x32 Matrixmultiplikation ist dann in C++ genauso schnell wie (wesentlich unproduktiverer) handgeschriebener Assemblercode.

    Aber ich bleibe dabei, in 99,99% aller Fälle bringt Assembler nix, aber beim echten Numbercrunchen und Code der sich fürs Handoptimieren eignet, ist ein schnellerer "inner loop" meistens machbar ;)

    2,7Mhz AMD 4850e (K8, 45Watt TDP 8o)

    Code
    > AutoIt: 6199.06
    > FreeBasic: 9.4
    > Lazarus: 8.93
    > FreePascal: 12.38
    > DevC: 6.84
    > MinGW: 7.17
    > Visual C++: 2.44
    > Assembler: 1.37
  • Hi,
    die neuen Prozessoren haben es in sich! Durch die massiven Paralellisierungsmöglichkeiten und die Aufteilung in verschiedene Rechenwerke und Pipelines wird es für die Compiler(bauer) immer schwerer.
    Für den Assemblerprogrammierer wird es fast unmöglich, die Compiler zu schlagen! Bissl was geht immer, aber um welchen Preis?

    Nach ausstoppen des "inner loop" bei der ASM-dll hatte ich festgestellt, daß der Prozessor dort wahnsinnig viel Zeit verbringt. Den "Fehler" konnte ich erst nicht lokalisieren, die reinen Latenzzeiten der SSE-Befehle hätten zu einer Dauer von ca. 30-40 Takten für die Berechnungen (immerhin ~200 Additionen/Subtraktionen und 40 Multiplikationen pro Schleifendurchgang) führen sollen.
    Mitten im Code hatte der Prozessor aber plötzlich 300 Takte lang andere Arbeit, nur welche? Das Geheimnis lag in der Abhängigkeit von Befehlen voneinander, im Endeffekt hatte der Prozessor meinen dilettantischen Code immer wieder umsortiert, um die parallelen Ausführungseinheiten besser auszunutzen....und das mitten im Schleifendurchlauf....

    Ich versuch(t)e bisher immer, die Speicherzugriffe zu minimieren, und alle Berechnungen mit den Prozessorregistern zu machen.
    Viel wichtiger als einige verbummelte Wartetakte infolge Cachemiss (Speicherzugriff) ist aber bei den neuen Prozessoren, die Befehle so in eine Reihenfolge zu bringen, daß die Recheneinheiten parallel zueinander arbeiten können ohne gegenseitig auf Ergebnisse zu warten.
    Das führt nun zur paradoxen Situation, daß ein längeres Programm teilweise wesentlich schneller ausgeführt werden kann!
    Am günstigten ist es natürlich, wenige Befehle (die dann auch noch bestenfalls in eine Instruction-Cacheline passen) zu verwenden, die parallel abgearbeitet werden können.
    Der vorliegende Algorithmus ist dafür denkbar schlecht geeignet, da zwangsläufig in der Berechnung von z.B.

    [autoit]

    Return ($V3 + $t * ($V2 + $t * $V1)) * $t + $V4

    [/autoit]

    Abhängigkeiten auftreten, die nicht parallel ausgeführt werden können. Was das "kostet" kann man sich vorsstellen wenn man weiß, daß der Prozessor im Idealfall 4-16 Additionen und 4-16 Multiplikationen in einem Takt ausführen kann.

    Bei einem Algorithmus, der die paralelle Verarbeitung von Daten nicht hergibt, nützen auch die besten Möglichkeiten des Prozessors nichts!

    Der VC++-Compiler hat nur deshalb im Verhältnis zum SSE-Code so "gut" abgeschitten, weil der Algorithmus nicht gut zu parallelisieren ist! ;) Da machen dann die "langsamen" vom Compiler verwendeten oldschool-FPU-Befehle den Kohl auch nicht fett. Jedenfalls hat der Compiler einige Tricks eingesetzt um bissl Speed rauszuholen!

    Ich habe aber meine Hausaufgaben gemacht und mal die SSE-Befehle etwas umgeordnet, das führt nun zu "nur" 3-facher Geschwindigkeit gegenüber dem Compilercode. Bissl was geht also immer 8o


    Es reicht eben nicht mehr irgendwelchen Code zu tippern und der Compiler/Assembler macht aus Sch*** dann Gold! Das gilt sowohl für C(++) Programmierer, als auch für Assemblerfreaks!