Assemblercode - Noch optimierbar?

  • Oki, hier die EXE meines Assemblercodes als FPU-Lösung aus Post #6 als Consolenanwendung in 1536 Bytes....Der identische Assemblercode, also auch knapp 1kByte in eine AutoIt-EXE kompiliert, ist knapp 1,15MB groß, also ca. 1000 (tausend! ) mal größer :whistling:
    Die in der CMD angegebene Zeit ist natürlich die reine Laufzeit des Codes (ohne das Laden der Exe zu berücksichtigen, das ist imho auch nonsens)
    Pi_calc_Andy.zip

    Bei mir hat Avira angefangen, irgendwelche Trojaner zu entdecken, als ich in den ASM-Code die Zeile
    NUMBER dq 0.0
    eingefügt hatte...damit wird nichts weiter gemacht, als 8 Bytes mit Nullen zu füllen ||
    Wer will, kann die Datei einfach in FASMW laden und F9 drücken...

    Ich probiere morgen mal das ein oder andere hiervon aus und schaue mal, ob das noch was bringt.


    Ich werde bei der SSE/SIMD-Version Loop-Unrolling testen, und dann auch nicht die Zeit messen, sondern die benötigten Prozessortakte per RDTSC. Da lässt sich dann wesentlich besser optimieren, wenn man bspw. aus 10 Durchläufen den schnellsten nimmt.
    //EDIT Loop unrolling und umsortieren bzw. "parallelisieren" (reorder/pairing) in der Instruction-Pipeline bringt zumindest bei meinem Prozessor nichts...
    Ich habe aktuell 5.22 Prozessortakte pro Schleifendurchlauf. Da der sub ecx,1 und das anschließende jnz _loop zusammen 0,77 Takte benötigen, bleiben für die 4 SSE-Befehle 4.5 Prozessortakte.
    Jetzt wird es interessant, denn das DIVPD benötigt allein 6.2 Takte!!! Durch die verschiedenen parallel arbeitenden Pipelines werden die Prozessorbefehle nicht "nacheinander" sondern wenn möglich gleichzeitig abgearbeitet. Man kann mehrere Zeilen völlig unnötiger Befehle in die Schleife einfügen, an der Laufzeit ändert sich...nichts!!!
    Daher ist es sinnvoll beim Optimieren, wenn man sich die die sog. Serialisierung vornimmt und ausrechnet, welcher Befehl am besten wo in der Reihenfolge steht...um die "teueren" Abhängigkeiten (dependencies) aufzulösen!
    http://www.agner.org/optimize/optimizing_cpp.pdf
    http://www.agner.org/optimize/optimizing_assembly.pdf
    http://www.agner.org/optimize/microarchitecture.pdf <-- DAS ist richtig klasse! Ich gehe davon aus, dass Compilerbauer dieses Dokument unter dem Kopfkissen liegen haben :thumbup:


    @Mars, natürlich habe ich deine EXE laufen lassen und auch versucht zu disassemblieren, daher auch die Info, dass die Datei eine 64-Bit-Code-EXE ist und auch SSE-Befehle nutzt!


    @Alle interessierten, und vor allem die, welche meinen, ein Compiler würde "automatisch" den besten Code erzeugen, ein (ihr kennt mich ja) ernst gemeinter Hinweis bzw. auch mal ein Grund, "nachzudenken".
    Mal angenommen, ein "cleverer" Compiler merkt bei der Analyse des Codes, dass sich eine bestimmte Variable immer weiter an PI annähert.
    Mit dem FPU-Befehl FLDPI, der aus 2 Bytes besteht und eine "Laufzeit" von 2-3 Prozessortakten hat, wäre PI auf 80bit genau bestimmt, und man könnte sich den Algorithmus sparen...
    Mal weiter angenommen, im Code wäre dieses PI "zu genau" für weitere Berechnungen, dürfte dann der Compiler die ungenaue "Berechnung" von PI mit einem "besseren" PI ersetzen um im Endeffekt ein wesentlich schnelleres und genaueres Ergebnis auszugeben?
    Was aber, wenn ich genau diese "ungenauen" Ergebnisse aber benötige, sollte ich als Programmierer dann die Wahl haben, "meinen" und genau diesen MEINEN Code generiert zu bekommen, oder doch den "optimierten" Code des Compilers?

  • Kurze Frage an alle, hat schon jemand die "neuen" (jaja, die gibts auch schon einige Jahre) AVX und/ oder FMA-Befehle ausprobiert?
    Leider unterstützt mein Laptopprozessor diese Features nicht.
    Imho sollte sich mit diesen Befehlen erstens alleine durch die Verdopplung der Registerbreite bei AVX (256Bit statt 128 wie bei SSE) die Berechnung durch 4 statt 2 gleichzeitiger Berechnungen verdoppeln! Der RCP (reziprok)-Befehl ist dort auch voerhanden....
    Ob dann nochmal durch die Verwendung von FMA (a=b+c*d in einem Takt) eine Beschleunigung erreicht werden könnte, ist die Frage?!

    Die innere Schleife zur Berechnung von Pi/4 erfolgt ja zzt in 4 Prozessorbefehlen

    Code
    1.0 ins Register laden
    1.0/positive und 1.0/negative berechnen
    positive+4 und negative-4 berechnen
    PIpositive+=positive und PInegative+=negative

    Diese Schleife kann man bei AVX genau so beibehalten, da man nun aber 4 statt 2 Double in einem Register unterbringen kann, halbiert sich die Anzahl der Schleifendurchläufe =>doppelte Geschwindigkeit! DOPPELT!

    Die Frage ist, ob FMA zu benutzen ist, also a=b+c*d in einem Takt und das noch serialisierbar hat ja was!

  • So, ich habe auch nochmal ein wenig rumprobiert. Die Zeitmessung per Linux' time haben wir deswegen gewählt, damit nicht in jeder zu testenden Sprache noch die Zeitmessung selbst implementiert werden muss. Dass da auch einiges an Overhead gemessen wird, ist klar. Das war aber in diesem Moment die praktikabelste Lösung.

    So, jetzt zurück zum Thema. Ich habe meinen Code nach euren Vorschlägen ein wenig optimiert. Allerdings zeigen sich bei mir ein paar interessante Phänomene. Generell ist zum Beispiel fld1 langsamer als fldi dword [one]. Daher ist (bei mir) der Code Nr. 1 auch schneller als der Code Nr. 2 (Unterschiede => 1 arbeitet mit einer Speichervariable zum Zwischenspeichern von Pi, 2 arbeitet komplett auf dem FPU-Stack.).

    Codes


    Nr. 1


    Nr. 2

  • Die Zeitmessung per Linux' time haben wir deswegen gewählt, damit nicht in jeder zu testenden Sprache noch die Zeitmessung selbst implementiert werden muss. Dass da auch einiges an Overhead gemessen wird, ist klar. Das war aber in diesem Moment die praktikabelste Lösung.

    Aus dem Bauch raus würde ich vermuten, dass der Overhead Programm laden/starten/beenden im Bereich von 2-4 Millisekunden liegt...und da du bei jedem zu testenden Programm diesen Overhead hast, ist sogar mal egal, wenn der Wert um eine Handvoll Millisekunden driftet. Also alles im grünen Bereich!


    Ich habe meinen Code nach euren Vorschlägen ein wenig optimiert.

    Hut ab! :klatschen:
    Nachdem ich deinen ursprünglichen Code nach FASM portiert hatte um direkt zu vergleichen, hatte ich festgestellt, dass dein ursprünglicher Code schon sehr schnell war!
    Wenn man die Laufzeiten analysiert (ich benutze dazu keine Zeitmessung, sondern zähle per RDTSC Takte) dann stellt man fest, dass sich die vermeintlichen "teueren" Speicherlese/schreibebefehle gar nicht so arg auswirken. Das hat einfach damit zu tun, dass bspw. die Schleifenzählvariablen in einer extra Pipeline parallel zu den FPU-Befehlen abgewickelt werden. Dazu liegen die Variablen entweder im Level1-Cache oder auf dem Stack, welcher idR. auch immer gecached ist! Ausführungszeit ca. 1-2 Takte pro DWORD laden!
    Im Vergleich zu den ca. 30-40 Takten für einen einzigen FDIV/FIDIV fällt das nicht sonderlich ins Gewicht.
    Daher hatte ich auch keinerlei Ambitionen, dort in eine Optimierung zu investieren, mein FPU-Code sollte nur zeigen, wie man den FPU-Stack nutzen kann.

    Allerdings zeigen sich bei mir ein paar interessante Phänomene.

    Gratuliere! :thumbup:
    Du hast begriffen, um was es geht! :thumbup:
    Ich bin davon überzeugt, dass dir die vergangenen Stunden für die Analyse und das rumprobieren an deinem ASM-Code für dein gesamtes weiteres Programmiererleben (vor allem mit HLL´s) viele Vorteile bringen werden.
    Wenn schon allein für diese "Pillepalle"-Anwendung mit der sehr kleinen Schleife so immense Unterschiede in der Laufzeit (vgl. UEZ´s erste Compilerversionen) feststellbar sind, was bringen dann erst Optimierungen für lange rechenintensive Schleifen!? 8)

    Im Grunde ist natürlich völlig egal, ob das Programm einige Millisekunden mehr oder weniger benötigt. Aber eine Beschleunigung um Faktor 3 für gewisse Anwendungen (bspw. Grafikfilter/analyse) ist ein definitives Kaufkriterium! Und damit wird dann Geld verdient...bestenfalls dein eigenes oder das deines Arbeitgebers ;)

  • Bin gerade auf einem Tablet mit Atom Z3735F@1300Mhz unterwegs und habe mal die beiden Versionen FPU und SSE verglichen. Ernüchterung sondergleichen, beide annähernd gleich schnell!
    Kurz mal bei Agner Fog geblättert, nun ist auch klar warum!
    Der SSE Befehl DIVPD, welcher gleichzeitig zwei 64-Bit-Double dividieren kann, wird im Kapitel für den Atom folgendermaßen erwähnt:

    Division is slow and not pipelined. A single precision scalar floating point division takes 30clock cycles. Double precision takes 60 clock cycles. A 64-bit integer division takes 207clock cycles.

    60 Takte für eine Division....ohne Worte...die "richtigen" Prozessoren machen das in 15-20 Takten (//EDIT wenn pipelined genaugenommen dann in 6-7 Takten) , also mindestens 3-4x schneller! Weiterhin sind auch die ADDPD nicht pipelined, das fällt aber garnicht ins Gewicht!

    Wenn man optimiert, sollte man also auch die Plattform im Auge behalten, ansonsten kann man sich die Arbeit sparen :rtfm:
    Allerdings kann es aber auch sein, dass man auf einem bestimmten Prozessor ohne großartigen Gewinn optimiert, und dieser Code aber auf einer anderen Maschine eine immense Beschleunigung erlebt!
    Knowhow ist durch nichts zu ersetzen, außer durch mehr Knowhow :D

  • Wenn schon allein für diese "Pillepalle"-Anwendung mit der sehr kleinen Schleife so immense Unterschiede in der Laufzeit (vgl. UEZ´s erste Compilerversionen) feststellbar sind, was bringen dann erst Optimierungen für lange rechenintensive Schleifen!?

    Was lernen wir daraus? Shit in - Shit out. Ergo, der Compiler schafft keine Wunder.

    Auch am Arsch geht ein Weg vorbei...

    ¯\_(ツ)_/¯

  • Was lernen wir daraus? Shit in - Shit out. Ergo, der Compiler schafft keine Wunder.

    Ergo kein Widerspruch zu meiner Behauptung.
    Übrigens mal Daumen hoch, tolle Optimierung die ihr betrieben habt :)
    Ein Gedanke zum loop unrolling noch @Andy - Angenommen du weißt wie viel stages deine Pipeline hat, für die Division, dann könntest du den Loop meines Theorieverständnisses nach auf diese Abrollen und als Step Größe die Anzahl der Stages nehmen.
    Andererseits Frage ich mich ob das wirklich einen Unterschied macht, da die Heuristik der Pipeline in dem Falle nur 1, maximal 2 Mal falsch liegt, und das ist wenn auf einmal nicht gesprungen sondern die Schleife verlassen wird.

    Es gibt sehr viele Leute, die glauben. Aber aus Aberglauben.
    - Blaise Pascal

  • Und wir sind hier nur langweilige Skriptsprachenkiddies (wie uns die "richtigen" Programmierer gerne nennen), was würde erst passieren, wenn jemand der auch Ahnung hat den Code optimieren würde [PLENK][/ironie] ?

    Jedenfalls Gute Arbeit Leute :D

    lg
    M

  • Oder einen besseren Prozessor, das ist heute üblich.

    Und, wie oben anhand der Beispielcodes gezeigt, meistens überflüssig und falsch!
    Wohin ging denn die Entwicklung zu "besseren" Prozessoren in den letzten Jahren? Stromsparen, ok! Schneller deswegen? Kein Stück! Mal abgesehen von technischen Machbarkeitsstudien (bspw. Intel 6950X (10 Kerne, 20 Threads) für atm 1700€), die aufgrund von Strukturverkleinerungen in der Fertigung nun einige Megaherz höher getaktet werden können.
    Mehr Prozessor-Cache wo es nur geht...macht Sinn, denn die Compiler werfen Unmengen an Code/Daten in den Ring.
    Und um mal zu vergleichen: der 1700€ teuere 6950X schlägt den 200€ teuren i5-6500 bei Single-Thread-Anwendungen nicht! Oha! Wieso auch Single Thread? Werden denn nicht sämtliche Anwendungen schon so compiliert, dass sie die vielen Prozessorcores und die darin enthaltenen SIMD-Funktionen "optimal" ausnutzen? Eben nicht....
    Und genau deshalb macht es auch keinen Sinn, einen "besseren" Prozessor zu kaufen um eine Software zu beschleunigen, wenn diese Software nicht in der Lage ist die Features des Prozessors auch nur ansatzweise auszunutzen!
    Spielehersteller haben erst in den letzten Jahren begonnen, ihre Software auf Multicore/thread umzustellen. Man konnte auch den Kunden schlecht erklären, wieso für das Daddeln eines 50€-Spiels eine 1500€ teure und hunderte von Watt elektrischer Leistung verballernde Maschine gekauft werden muss!
    Die dann aber, was für ein hochgradig geistiger Dünnschiss, beim (Script-)Programmieren per Sleep() permanent im Tiefschlaf gehalten werden muss. "...Quiiiiieeek, meine Prozessorlast liegt bei 8%, quiiieeekkk, ogottogott, mein Prozessor arbeitet für das viele Geld was er gekostet hat, DAS kann und darf ja nicht sein!!!..."
    Da wird der Prozessor bis an die Grenzen übertaktet, um dann in den untersten P-states vor sich hinzuschlafen......

    Was mich an dieser Stelle bzgl. Prozessor/Hardwareverbesserung wirklich gefreut hat, war bspw. die Beschleunigung des DIV/IDIV-Befehls. Der war schon immer "teuer" und hat, nachdem u.a. dieser eine Befehl innerhalb des Prozessors beschleunigt wurde, für teilweise immense Beschleunigungen der (nicht veränderten) Software gesorgt! Wenn man sich klar macht, dass dieser Schritt ca. 30 Jahre dauerte, kann man ermessen, welche Fortschritte Software gemacht haben könnte, wenn in dieser Zeit die in den Prozessoren NICHT GENUTZTEN FEATURES benutzt worden wären... ||

    Warum bei einem "aktuellen" und unverschämt billigen Atom-Prozessor diese schnell abgearbeiteten Befehle nicht implementiert sind, sollte klar sein. NIEMAND aber auch niemand würde einen zehnfachen Preis für gerade mal 30 bis 40% mehr Leistung ausgeben! Intel käme sicherlich mit der Produktion von Atom-Prozessoren nicht mehr hinterher, davon würde jeder PC-Hersteller 8-10 in eine Kiste packen und so einen "Superserver" für wenig Geld bereitstellen können...


    Und wir sind hier nur langweilige Skriptsprachenkiddies

    Ein wichtiger Grundsatz in der Optimierung lautet: "Zuerst die großen Brocken!"
    Man muss herausfinden, wo eine Optimierung am meisten lohnt. Und das ist unabhängig davon, ob bei Hardware, Software oder in jedem anderen beliebigen Arbeitsablauf optimiert werden soll. Irgendwann ist ein Punkt erreicht, wo man die nur immer kleiner werdende "Verbesserung" den hohen Kosten/Aufwand gegenüberstellen muss,
    Dann macht es meistens Sinn, die Optimierung an ein weiteres, bis dato zu diesem Prozess unvoreingenommenes, Team abzugeben. Oft werden dann durch andere Lösungsansätze noch einmal Verbesserungen erreicht.


    Ein Gedanke zum loop unrolling noch @Andy - Angenommen du weißt wie viel stages deine Pipeline hat, für die Division, dann könntest du den Loop meines Theorieverständnisses nach auf diese Abrollen und als Step Größe die Anzahl der Stages nehmen.
    Andererseits Frage ich mich ob das wirklich einen Unterschied macht, da die Heuristik der Pipeline in dem Falle nur 1, maximal 2 Mal falsch liegt, und das ist wenn auf einmal nicht gesprungen sondern die Schleife verlassen wird.

    Ja, die Frage, ob es einen Unterschied macht, kann man ja ausprobieren :D
    Das habe ich auch gemacht, für SSE/SIMD. Da der Loop aber nur aus 4 Befehlen besteht, und auch Abhängigkeiten (Dependencies) innerhalb des Codes bestehen, lohnt sich das unrolling hier nicht.
    Die 10-20 Takte für den DIVPD sind zu "fett", um mit anderen Befehlen parallelisiert zu werden. Das macht in langen Sequenzen sicherlich mehr Sinn.
    Bei der Sprungvorhersage (branch-prediction) sieht es ähnlich aus. Da die handvoll Befehle sowieso alle in die Befehls-Ausführungspipeline passen, ist es auch völlig egal, ob die Sprungvorhersage IMMER FALSCH liegt! Die 4 Befehle für die "falsche" Vorhersage laufen ja parallel mit....der Inhalt der Pipelines muss nie umsortiert werden.

    Was sicherlich eine Option wäre, ist Multithreading. Man müsste mal probieren, inwieweit sich dieser Schritt bei der vergleichsweise "billigen" Berechnung lohnt.
    Durch Overhead und Threadgedöns vermute ich mal Faktor +0,7 für jeden zusätzlichen Thread. Bei einem Quadcore also ca. Faktor 3.
    Da mit AutoIt ja Multithreading in Kombination mit ( idR IMMER threadsicherem) Assemblercode ziemlich easy ist, sollte man das mal ins Auge fassen.
    Dann wäre imho für eine CPU das Limit erreicht.

    Spoiler anzeigen


    Da aber der Leibniz-Algorithmus sehr gut zu parallelisieren ist, wäre es interessant, mal OpenCL auszuprobieren. Leider unterstützen die meisten Grafikkarten keine (dann nur per Softwareemulation) oder nur sehr langsame Double-Berechnungen. Die Beschleunigung durch die hunderte GPU-Prozessoren würde imho durch die Emulation von Double-Berechnungen mehr als aufgefressen.

  • Ein Thema ist mir noch in den Sinn gekommen -> Multithreading.

    Ich habe mal mit FB verglichen, wie schnell 1 Thread zu 4 Threads sind.


    Hier das Resultat:

    x86:

    Code
    1 Thread  PI: 3.14159265258805            11998.85867815465 ms for 1000000000 iterations.
    4 Threads PI: 3.14159266258921            3342.891288688406 ms for 1000000000 iterations.


    x64:

    Code
    1 Thread  PI: 3.14159265258805            5485.781099298038 ms for 1000000000 iterations.
    4 Threads PI: 3.14159266258921            2726.45756660495 ms for 1000000000 iterations.


    Ich habe 1.000.000.000 (eine Milliarde) Durchgänge gewählt, dass man die Auslastung der CPU mitverfolgen kann -> Faktor ca. 3.6!

    Source Code:

    Spoiler anzeigen


    Vergleich Andy's ASM Code:

    Code
    : $pi Leibnitz = 3.14159265258555
    : Time [ms] = 2631.10135102957


    Selbst 4 Threads schlagen den ASM Code von Andy nicht! :huh:

    Oder ich den nehme gleich den ASM Code von Andy als Thread_Leibniz_PI Funktion. Dann sollte es brutal abgehen...

    Auch am Arsch geht ein Weg vorbei...

    ¯\_(ツ)_/¯

  • Code
    1 Thread  PI: 3.14159265258805            12060.16655091662 ms for 1000000000 iterations.
    2 Threads PI: 3.141592650585555           4845.030240947381 ms for 1000000000 iterations.

    Wodurch kommt das unterschiedliche Ergebnis zustande? Die 9. Nachkommastellen stimmen nicht überein (sowie der Rest danach).
    Ist er durch die 2. Threads durch doppelt so viele Iterationen gegangen und demnach genauer berechnet?

  • @alpines: ich habe den Code jetzt auch 4 Threads modifiziert. Ich vermute, dass die Unterschiede von der Addition der Teilergebnisse kommen.

    Auch am Arsch geht ein Weg vorbei...

    ¯\_(ツ)_/¯

  • Wodurch kommt das unterschiedliche Ergebnis zustande? Die 9. Nachkommastellen stimmen nicht überein (sowie der Rest danach).

    Das hatte ich in meinem Post zum SSE/SIMD-Code schon dargestellt!
    Die FPU rechnet intern mit 80 Bit Genauigkeit, die SSE-Register sind 128 Bit breit und somit je nur 64 Bit (Double)!
    Bei 10 Millionen Durchgängen summiert sich der "Rundungsfehler".
    Abhilfe schafft, die FPU zum Rechnen mit 64Bit zu zwingen, aber wer macht das schon, die FPU ist sowieso schon schneckenlahm, da kann das Ergebnis wenigstens "genau"(er) sein....

    Ich werden mal den ASM-SSE-Code per Autoit in Threads aufsplitten lassen, schaumamal... 8o

  • Sodele, das ist imho das Ende der Fahnenstange...Multithreading in AutoIt 8o

    Ich lasse einfach Threads von 1 bis 24 laufen, ist natürlich Quatsch, idR sollte "Otto-Normalo-PC-User" Quad-, Hex-, oder Octa-Cores haben.
    Bei mir zumindest bringen viele Threads nichts, ist auch klar, der Overhead frisst irgendwann den Gewinn völlig auf...aber spielt selbst mal rum.

    Für meinen Laptop-Quadcore AMD @2.3Ghz:
    Ich habe 1000000000 Durchläufe eingestellt, da bekomme ich folgendes Ergebnis für die ersten 10 Threads

    Spoiler anzeigen

    Threads: 1 Time: 3814.4ms PI: 3.141592652586
    Threads: 2 Time: 1959.2ms PI: 3.141592652591
    Threads: 3 Time: 1426.5ms PI: 3.141592652598
    Threads: 4 Time: 1517.9ms PI: 3.141592652593
    Threads: 5 Time: 1221.8ms PI: 3.141592652591
    Threads: 6 Time: 1201.7ms PI: 3.141592652588
    Threads: 7 Time: 1138.5ms PI: 3.141592652589
    Threads: 8 Time: 1073.5ms PI: 3.141592652586
    Threads: 9 Time: 1047.2ms PI: 3.141592652587
    Threads: 10 Time: 1093.9ms PI: 3.141592652590


    Bei den aus dem Startpost vorgegebenen 10000000 Durchläufen erhalte ich

    Spoiler anzeigen

    Threads: 1 Time: 41.0ms PI: 3.141592553586
    Threads: 2 Time: 21.4ms PI: 3.141592553591
    Threads: 3 Time: 13.4ms PI: 3.141592553590
    Threads: 4 Time: 20.8ms PI: 3.141592553589
    Threads: 5 Time: 16.8ms PI: 3.141592553590
    Threads: 6 Time: 22.3ms PI: 3.141592553590
    Threads: 7 Time: 19.6ms PI: 3.141592553589
    Threads: 8 Time: 16.4ms PI: 3.141592553590
    Threads: 9 Time: 17.9ms PI: 3.141592553589
    Threads: 10 Time: 22.2ms PI: 3.141592553590


    was man feststellen kann ist maximal ein Faktor 3 an Geschwindigkeit durch Multithreading! Aber auch nicht schlecht....

    OpenCL anyone? :rock:

  • Top :thumbup:

    $n = 1000000000 -> Intel(R) Core(TM) i5-4300U CPU:

    Code
    Threads:   1   Time: 2922.8ms   PI: 3.141592652586 
    Threads:   2   Time: 1786.9ms   PI: 3.141592652591 
    Threads:   3   Time: 1485.4ms   PI: 3.141592652598 
    Threads:   4   Time: 1383.9ms   PI: 3.141592652593 
    Threads:   5   Time: 1476.1ms   PI: 3.141592652591 
    Threads:   6   Time: 1398.5ms   PI: 3.141592652588 
    Threads:   7   Time: 1439.3ms   PI: 3.141592652589 
    Threads:   8   Time: 1397.7ms   PI: 3.141592652586 
    Threads:   9   Time: 1385.6ms   PI: 3.141592652587 
    Threads:  10   Time: 1388.6ms   PI: 3.141592652590

    Auch am Arsch geht ein Weg vorbei...

    ¯\_(ツ)_/¯

  • Wieder mal sehr interessant die unterschiedlichen Ergebnisse, obwohl ich für eine "schöne" Darstellung die letzten 3 Nachkommastellen schon abgeschnitten hatte. Ggf. habe ich ja Lust, die entsprechenden Bits (wie hier http://www.plantation-productions.com/Webster/www.ar…Arithmetic.html beschrieben gibts das auch für SSE/SIMD) zu setzen.... ||

  • Ich habe mal den FB Code als x64 laufen lassen -> Resultat siehe oben Post#31.

    Bei einem Thread die Laufzeit mehr als halbiert!

    Auch am Arsch geht ein Weg vorbei...

    ¯\_(ツ)_/¯

  • Hier mal die Resultate von Andys Post auf meinem Intel Core i7-4790. Schon krass wie sehr der Faktor abnimmt wenn man über 3 Threads hinaus geht.

  • Ich vermute, bei einem Quadcore-Prozessor (ggf auch Dual-Core mit HT) liegt die Grenze bei 3 Threads, weil Windows sich immer einen Thread "reserviert", um das System laufen zu lassen. Sicher wird es auch trotzdem "Core-Hopping" geben, das müsste man mal genauer profilen. Aber Faktor 3 ist definitiv eine Hausnummer!

    Für eine Compilersprache ist die Optimierung per Multithreading ziemlich einfach, Schalter setzen, fettich!


    Ich habe mal den FB Code als x64 laufen lassen -> Resultat siehe oben Post#31.

    Bei einem Thread die Laufzeit mehr als halbiert!

    Naja, das demonstriert nur, wie "langsam" der erzeugte 32Bit-Code ist, obwohl, wenn SSE eingeschaltet, der SSE-Code in 32Bit genauso schnell läuft wie im 64Bit-Modus! Für die SSE-Befehle bleibt die Laufzeit in beiden Modi ja gleich!
    Wenn man anfängt zu "handoptimieren", wird der Abstand zum vom Compiler erstellten Code immer größer. Es macht also in einer Compilersprache Sinn, die Funktionsweise des Compilers wenigstens zu kennen und zu wissen, an welchem "Rädchen" man drehen muss, um dessen Code schneller laufen zu lassen.
    Wenn man sich jetzt noch verdeutlicht, wie viel schneller der Code wäre, wenn (wie schon weiter oben erwähnt) AVX/FMA verwendet wird, dann ist auch klar, warum auch der Abstand von "Profi-Compilern" und dessen Einsatz auf den neuesten Prozessoren zum "Otto-Normalo-F5-Drücker" mit seinem 200-€-Prozessor immer größer wird (werden muss).