Ich behaupte zu C und OpenMP können wir dir bessere Unterstützung anbieten als zu Pascalosculumobscenum hat geschrieben: ↑So Feb 06, 2022 8:21 pmMöglicherweise werde ich meine sehr spärlichen C-Kenntnisse aufpolieren und mir diese OpenMP-Sache mal zu Gemüte führen, und sei es nur für Benchmark-Zwecke.
FreePascal // Multithread ?
Re: FreePascal // Multithread ?
-
osculumobscenum
- Beiträge: 27
- Registriert: Do Okt 31, 2013 9:03 pm
Re: FreePascal // Multithread ?
Das werde ich zu gegebener Zeit dann dankend annehmen! 
-
osculumobscenum
- Beiträge: 27
- Registriert: Do Okt 31, 2013 9:03 pm
Re: FreePascal // Multithread ?
Ich werde wohl mangels genügend Zeit auf weitere Recherchen verzichten müssen, aber der Komplettheit halber will ich noch folgendes festhalten :
Ich habe folgenden Code in Freepascal angesetzt, der EINEN Prozessorkern nutzt :
...und folgenden Code in C zusammen-gepfuscht, der mithilfe dieser OpenMP-Lösung alle Prozessorkerne nutzt :
Bei beiden wird jeweils durch Teilung der zu untersuchenden Werte bis Wurzel(Wert) mit der Modulo-Division ein Rest gesucht, also das gleiche Verfahren genutzt.
Bei einer Untersuchung eines Wertebereichs zwischen 1.000.000 und 1.500.000 hat sich das Einkern-Pascal-Programm mit 4,5 Sekunden geschlagen, während das Mehrkern-C-Programm wirklich nur eine halbe Sekunde gebraucht hat. (Ich habe dabei die Schreibausgabe in eine Datei umgeleitet, weil Schreiben an der Konsole zu lange dauert und das Ergebnis verfälscht.)
Aber man sieht definitiv, die OpenMP-Einbindung macht das ganze schon viel schneller, auch wenn die Sortierung der Primzahlen dann nicht mehr stimmt.
Ich habe folgenden Code in Freepascal angesetzt, der EINEN Prozessorkern nutzt :
Code: Alles auswählen
program grosse_primzahlen;
uses crt;
var
i, j, start, ende, jstop : qword;
prim : boolean;
begin
clrscr;
writeln;
writeln('Berechnung grosser Primzahlen');
writeln('-----------------------------');
write('starten bei : ');readln(start);
write('enden bei : ');readln(ende);
i:=start;
repeat
prim:=true;
inc(i);
j:=1;
jstop:=round(sqrt(i));
repeat
inc(j);
if i mod j = 0 then prim := false;
until j>= jstop;
if prim=true then writeln(i,' ist Primzahl');
until i= ende;
end.
...und folgenden Code in C zusammen-gepfuscht, der mithilfe dieser OpenMP-Lösung alle Prozessorkerne nutzt :
Code: Alles auswählen
// Über die Unit OpenMP kann die Nutzung aller Kerne bei aufwändigen Rechnereien erreicht werden.
#include <omp.h>
#include <stdio.h>
#include <math.h>
int check_prime(int);
int main()
{
unsigned int n, result; // 0 to 4294967295
#pragma omp parallel for
for (n=1000000;n<=1500000; n++)
{
result = check_prime(n);
if (result == 1)
printf("%d is prime.\n", n);
}
return 0;
}
int check_prime(int a)
{
unsigned int c, ptrue;
unsigned int h = (int)sqrt(a);
ptrue=1;
// printf("%d als Wurzel \n", h);
for (c = 2; c <= h; c++)
{
if (a%c == 0) ptrue = 0;
}
if (ptrue == 0)
return 0;
if (ptrue == 1)
return 1;
}
Bei einer Untersuchung eines Wertebereichs zwischen 1.000.000 und 1.500.000 hat sich das Einkern-Pascal-Programm mit 4,5 Sekunden geschlagen, während das Mehrkern-C-Programm wirklich nur eine halbe Sekunde gebraucht hat. (Ich habe dabei die Schreibausgabe in eine Datei umgeleitet, weil Schreiben an der Konsole zu lange dauert und das Ergebnis verfälscht.)
Aber man sieht definitiv, die OpenMP-Einbindung macht das ganze schon viel schneller, auch wenn die Sortierung der Primzahlen dann nicht mehr stimmt.
Re: FreePascal // Multithread ?
Dass die C Variante hier schneller ist, ist nicht verwunderlich. Der FreePascal Compiler ist nicht gerade dafür bekannt gut zu optimieren, schon garnicht im Vergleich zu den "Großen" wie gcc oder clang. Seit dem GCC 13 kann man das sogar ein bisschen vergleichen, denn da gibt es ein Modula-2 Frontend. Und Pascal in Modula-2 zu transformieren ist ziemlich einfach. Man kann dann auch schön im Compiler Explorer die Unterschiede sehen. Zum Beispiel sieht man, dass der GCC SIMD Maschinencode (also MMX/SSE/AVX) generiert.
Dadurch dass die äußere Schleife kein Abhängigkeit hat, kann man jeden Test einer spezifischen Zahl als ein Thread realisieren. Wenn man also von 10 bis 100 testet, sind das 90 Threads, wobei in jedem Thread die innere Schleife läuft. Das ist keine gute Idee, weil das Spawnen eines Threads und dann die Verwaltung aller Threads vermutlich mehr Zeit kostet als die Abarbeitung der inneren Schleife. Insbesondere muss die Ausgabe der Primzahl gegen parallele Zugriffe geschützt werden (Stichwort Mutex). Idealerweise verwendet man einen Threadpool mit so vielen Threads, wie man CPU-Kerne hat, und dann "füttert" man die Threads bis alle Tests durchgelaufen sind. Die Primzahl gibt man auch nicht im Thread aus, sondern sammelt sie in einer Liste oder Queue. Idealerweise sollten diese "lock-free" sein oder gegen parallele Zugriffe geschützt werden. Das ist eigentlich eine sehr schöne Aufgabe für modernes C++.
Dadurch dass die äußere Schleife kein Abhängigkeit hat, kann man jeden Test einer spezifischen Zahl als ein Thread realisieren. Wenn man also von 10 bis 100 testet, sind das 90 Threads, wobei in jedem Thread die innere Schleife läuft. Das ist keine gute Idee, weil das Spawnen eines Threads und dann die Verwaltung aller Threads vermutlich mehr Zeit kostet als die Abarbeitung der inneren Schleife. Insbesondere muss die Ausgabe der Primzahl gegen parallele Zugriffe geschützt werden (Stichwort Mutex). Idealerweise verwendet man einen Threadpool mit so vielen Threads, wie man CPU-Kerne hat, und dann "füttert" man die Threads bis alle Tests durchgelaufen sind. Die Primzahl gibt man auch nicht im Thread aus, sondern sammelt sie in einer Liste oder Queue. Idealerweise sollten diese "lock-free" sein oder gegen parallele Zugriffe geschützt werden. Das ist eigentlich eine sehr schöne Aufgabe für modernes C++.
