Berechnung mit OpenMP zu langsam



  • Ist openmp wirklich akzeptabel für sowas? So allgemein unter Entwicklern meine ich.

    Ich würde das nie machen, allein schon weil ich nicht weiß was genau das Ding im Hintergrund macht. Ich würde auf jeden Fall von Hand threads starten und Funktionspointer übergeben.



  • randa schrieb:

    Ist openmp wirklich akzeptabel für sowas? So allgemein unter Entwicklern meine ich.

    Ich würde das nie machen, allein schon weil ich nicht weiß was genau das Ding im Hintergrund macht. Ich würde auf jeden Fall von Hand threads starten und Funktionspointer übergeben.

    Und wie würdest du eine Schleife parallisieren?

    #pragma omp parallel for
    for(int i = 0; i < n; i++)
    {
        arr[i] = func();
    }
    

    Das mit (std::)Threads nachzubauen sollte gehen, ist aber ziemlich viel Arbeit, dafür dass du dich gegen einen Standard durchsetzen willst. (Und du kannst nicht durch ein Compilerflag sämtliche Parallelisierung rausnehmen...)

    Und die Jungs, die OpenMP implementieren auf den einzelnen Compilern, haben gewöhnlich schon ein bisschen Ahnung und wissen, was sie machen.

    Es ist halt für High Performance Computing und nicht um mal eben in ner Business Application einen Thread Pool für Requests zu managen.

    Ich hab mir jetzt zwar nicht den den Code vom OP im Detail angeschaut, aber das sieht schon ganz gut aus, was die Zuständigkeit von OpenMP angeht.



  • Skym0sh0 schrieb:

    Und wie würdest du eine Schleife parallisieren?

    #pragma omp parallel for
    for(int i = 0; i < n; i++)
    {
        arr[i] = func();
    }
    

    naja ich würde jeden Thread n/n_threads elemente durchlaufen lassen

    Es ist halt für High Performance Computing und nicht um mal eben in ner Business Application einen Thread Pool für Requests zu managen.

    gerade da würde ich darauf bestehen dass ich alles von Hand mache. Deswegen frage ich ob das in computing projekten auch wirklich einfach mit einer handvoll #pragmas abgekanzelt wird, ich zweifle.



  • randa schrieb:

    Skym0sh0 schrieb:

    Und wie würdest du eine Schleife parallisieren?

    #pragma omp parallel for
    for(int i = 0; i < n; i++)
    {
        arr[i] = func();
    }
    

    naja ich würde jeden Thread n/n_threads elemente durchlaufen lassen

    Und wie siehts aus mit?

    #pragma omp parallel for schedule(guided)
    for(int i = 0; i < n; i++)
    {
        arr[i] = func();
    }
    

    randa schrieb:

    Es ist halt für High Performance Computing und nicht um mal eben in ner Business Application einen Thread Pool für Requests zu managen.

    gerade da würde ich darauf bestehen dass ich alles von Hand mache. Deswegen frage ich ob das in computing projekten auch wirklich einfach mit einer handvoll #pragmas abgekanzelt wird, ich zweifle.

    Naja, es gibt ja noch andere Tricks/Frameworks, z.B. MPI.
    OpenMP bietet halt noch sehr viel Stuff mehr als nur eine kleine Kapsel um Threads.

    Umgekehrt gebe ich dir Recht, dass gerade mit der std::Threading Bibliothek viele coole Sachen hinzugekommen sind wie der Mutex-Lock im RAII Objekt gekapselt



  • randa schrieb:

    Deswegen frage ich ob das in computing projekten auch wirklich einfach mit einer handvoll #pragmas abgekanzelt wird, ich zweifle.

    Wieso nicht?
    Der OpenMP/Cilk/AMP/TBB/... Scheduler ist weitaus performanter als alles was man selbst mit vertretbarem Aufwand hinbekommt.



  • @Simon S.
    Hast du mal probiert num_threads beim #pragma parallel for zu verwenden statt bzw. zusätzlich zu omp_set_num_threads ?
    Oder mal nen anderen Compiler probiert (MSVC, GCC)?



  • hustbaer schrieb:

    Wieso nicht?

    man kann nicht sehen was konkret am Ende für ein Code produziert wird. Und bei parallelisierung und kritischen Anwendungen will ich das genau wissen.



  • Klassische Noob-Paranoia.



  • hustbaer schrieb:

    Klassische Noob-Paranoia.

    ich?



  • Klar, wer sonst.
    Google auch gerne mal NIH (not invented here).

    Bzw. allgemein Dinge selbst zu implementieren nur weil man meint (ohne gute Grundlage) dass die fertigen Implementierungen evtl. nicht gut genug sein könnten...
    Trotz dem diese von Teams entwickelt wurden die sich wirklich mit der Materie auskennen...
    Das macht keinen Sinn.

    Passt aber genau zu dir.

    ps: Wenn man früh Testläuft macht, kommt man üblicherweise recht schnell drauf ob das gewählte Tool den Anforderungen entspricht. Und dann tauscht man es einfach aus, bzw. schreibt dann was eigenes. Bzw. wenn die Softwarearchitektur halbwegs sauber ist, ist es auch kein grosses Problem wenn man erst sehr spät draufkommt.



  • hustbaer schrieb:

    @Simon S.
    Hast du mal probiert num_threads beim #pragma parallel for zu verwenden statt bzw. zusätzlich zu omp_set_num_threads ?
    Oder mal nen anderen Compiler probiert (MSVC, GCC)?

    Hat nichts geändert. Der Intel war am besten, habe die anderen beiden schon getestet.

    Habe jetzt noch paar Messungen mit verschiedenen Varianten durchgeführt.
    Variante0:(ohne RunFull(), läuft in einem Thread)
    0,2s
    Diese müssen also bei allen folgenden Werten abgezogen werden um die Zahlen für RunFull() zu erhalten.

    Variante1:(ein großer LUT)
    1 Thread: 13s
    2 Threads: 7s
    8 Threads: 4s
    16 Threads: 2s
    32 Threads: 1,5s

    Variante2:(ein großer LUT und alle Schleifen guided)
    32 Threads: 1,22s

    Variante3:(Schleifen 42, 42, 42, 43)
    1 Thread: 6,3s
    2 Threads: 3,3s
    4 Threads: 2,1s
    8 Threads: 1,4s
    16 Threads: 1,3s
    32 Threads: 1,3s

    Variante4: (Schleifen 42, 42, 42, 43 und alle Schleifen guided)
    8 Threads: 1,22s
    16 Threads: 0,92s
    32 Threads: 1,15s

    Variante5: (Schleifen 32, 32, 32, 32, 41)
    8 Threads: 1,46s
    16 Threads: 1,24s
    32 Threads: 1,33s

    Variante4: (Schleifen 32, 32, 32, 32, 41 und alle Schleifen guided)
    8 Threads: 1,23s
    16 Threads: 1,07s
    32 Threads: 1,06s

    Variante6: (Schleifen 32, 32, 32, 32, 41 nur Schleife mit 41 guided)
    8 Threads: 1,55s
    16 Threads: 1,14s
    32 Threads: 1,18s

    Die CPU-Auslastung ist wie gesagt laut htops nicht bei 100%.
    z.B. bei Variante 4 32Threads nur bei 50%
    Da später mal noch mehrere Berechnungen in die 3er for-Schleife kommen, würde sich die Berechnungszeit erstmal hoffentlich nicht erhöhen und die CPU-Auslastung Richtung 100% gehen, oder ist das Quatsch?

    So richtig zufrieden bin ich mit den Ergebnissen nicht wirklich. Ich würde mich jetzt für die Variante 4 entscheiden.
    Es ist wohl guided all > guided last > non guided und LUT split passend zu threadzahl > LUT split random > LUT big

    Zu arr[i] = func();
    

    Das verstehe ich nicht. Wieso soll ich die Werte erst in ein Array speichern.
    Ist das so gedacht, das

    arr[i] = ((3.0f*iterations1) * lut3Linear[zwischenErgebnis1] - iterations2) * zwischenErgebnis2;
    

    sich leichter vektorisieren lässt und nach der Vektorisierung wird das Array aufaddiert um auf

    berechnung1 = berechnung1 + ((3.0f*iterations1) * lut3Linear[zwischenErgebnis1] - iterations2) * zwischenErgebnis2;
    

    zukommen.

    Ich bin für jeden weiter Hilfe sehr dankbar und möchte mich bei allen für ihre Antworten bedanken.


Anmelden zum Antworten