Warum Parallelisierung? Das soll den Prozess beschleunigen. Als ich zuvor MPI parallel eingeführt habe, wurde ich gefragt, ob es eine einfachere Parallele gibt. Daher werde ich eine Parallele einführen, die bei mehreren Kernen problemlos durchgeführt werden kann. Versuchen Sie es auf einem Computer mit einer CPU von 2core oder höher. Es ist auf Linux ausgerichtet, aber versuchen Sie es auf einem anderen Betriebssystem.
Bereiten Sie das Zielprogramm vor. Wenn Sie ein Programm haben, das Arrays verwendet, versuchen Sie es. Ich hätte gerne eine Arraygröße von ca. 1000. Wenn nicht, habe ich das folgende Programm vorbereitet. Bitte kopieren und versuchen.
#include <stdio.h>
#include <time.h>
int main()
{
int n,m,i,j,k;
double a[5000][600];
double b[600][5000];
double us,t0,t1,t2,sum;
t0=clock();
us = 10.0 ;
n=5000 ;
m=600 ;
for (j =0;j<m;j++){
for (i =0;i<n;i++){
b[j][i] = (j+i+2)/10.0 ;
}
}
for (k=0;k<200;k++) {
for (j =0;j<m;j++){
for (i =0;i<n;i++){
a[i][j] = b[j][i] + us ;
}
}
us = us + 1.2 ;
}
sum=0.0 ;
for (j =0;j<m;j++){
for (i =0;i<n;i++){
sum=sum+a[i][j] ;
}
}
t1=clock();
t2 = (t1 - t0) * 1.0e-6 ;
printf(" TIME=%f data= %f\n",t2,sum);
}
Dieses Programm hat keine physikalische Bedeutung. Es ist ein Übungsprogramm, das nur eine absichtliche Übung ist, bei der Werte einfach in ein Array eingefügt und aggregiert werden.
Lassen Sie uns dies aufbauen. Rufen wir die Datei sample2.c auf. (Beliebig * .c)
[]$ gcc sample2.c
[]$ ./a.out
Segmentation fault
[]$
Ah, wie unschuldig. .. Entfernen wir die Einschränkungen.
[]$ ulimit -s unlimited
[]$ ./a.out
TIME=3.898616 data= 1586700000.000000
[]$
Fügen wir eine Optimierungsoption hinzu.
[]$ gcc -O2 sample2.c
[]$ ./a.out
TIME=3.840020 data= 1586700000.000000
[]$
Die Optimierungsoption scheint nicht sehr gut zu funktionieren. Lassen Sie uns ein wenig abrollen.
[]$ vi sample2a.c (sample2.Ich habe Änderungen an c vorgenommen und es gespeichert)
[]$ gcc sample2a.c
[]$ ./a.out
TIME=1.300404 data= 1586700000.000000
[]$
Es scheint zu funktionieren. Fügen wir nun einige Optimierungen hinzu.
[]$ gcc -O2 sample2a.c
[]$ ./a.out
TIME=1.192950 data= 1586700000.000000
[]$
Es scheint gut zu funktionieren. Da das Thema dieser Zeit SMP parallel ist, werde ich auf die Optimierung eingehen, um sie später zu beschleunigen, aber ich würde es gerne zu einem späteren Zeitpunkt tun, wenn sich eine Gelegenheit ergibt.
Lassen Sie uns jetzt parallel laufen.
Fügen wir eine Parallelisierungsanweisungszeile in ein einfaches Programm ein. Der schwerste Teil ist vorerst die nächste Schleife. Fügen Sie daher die OpenMP-Parallelisierungsanweisungszeile wie folgt ein.
[]$ cp sample2.c sample2b.c
[]$ vi sample2b.c
for (k=0;k<200;k++) {
#pragma omp parallel for private(i,j)
for (j =0;j<m;j++){
for (i =0;i<n;i++){
a[i][j] = b[j][i] + us ;
}
}
us = us + 1.2 ;
}
Es gibt 3 Schleifen, aber dies ist die schwerste Dreifachschleife. Versuchen Sie also, die angegebenen Positionen zu tauschen. Darüber hinaus gibt es verschiedene Optionen und Anweisungen. Bitte überprüfen Sie es und probieren Sie es selbst aus. Es kann in anderen Schleifen angegeben werden, aber der Effekt ist aufgrund der geringen Granularität begrenzt. Aber bitte versuchen Sie es. Fügen Sie der Summenschleife eine Reduzierung (+: Summe) hinzu. Es ist so.
sum=0.0 ;
#pragma omp parallel for private(i,j) reduction(+:sum)
for (j =0;j<m;j++){
for (i =0;i<n;i++){
sum+=a[i][j] ;
}
}
Hier ist die einzugebende Anweisungszeile nur die Grundform. Jetzt kompilieren wir. Fügen Sie der Option "-fopenmp" hinzu.
[]$ gcc -O2 -fopenmp sample2b.c
Das ist in Ordnung.
Lass es uns laufen.
[]$ ./a.out
TIME=3.932937 data= 1586700000.000000
[]$
Ich denke es funktioniert normal. Als nächstes wollen wir endlich parallel arbeiten. Zuallererst, weil es Magie ist
[]$ export OMP_NUM_THREADS=2
[]$ ./a.out
TIME=3.945268 data= 1586700000.000000
[]$
Hmm, es ist spät. .. .. ?? Oh, ich habe es vergessen. Im Fall von C wurde die Thread-Zeit hinzugefügt. Verwenden Sie die Zeitmessbefehlszeit. Der als "real" angezeigte Teil ist die Ausführungszeit. Lass uns weiter rennen.
[]$ export OMP_NUM_THREADS=1
[]$ time ./a.out
TIME=3.826071 data= 1586700000.000000
real 0m3.836s
user 0m3.816s
sys 0m0.012s
[]$ export OMP_NUM_THREADS=2
[]$ time ./a.out
TIME=3.908159 data= 1586700000.000000
real 0m1.960s
user 0m3.895s
sys 0m0.016s
[]$ export OMP_NUM_THREADS=4
[]$ time ./a.out
TIME=5.014193 data= 1586700000.000000
real 0m1.284s
user 0m4.974s
sys 0m0.044s
[]$
Wie war. Bist du schneller geworden? Wenn Sie eine Maschine mit mehr Kernen haben, versuchen Sie es mit der Anzahl der Kerne. Die Maschine, die ich dieses Mal benutzte, war 4 Kerne, also etwas schneller, aber ich denke, wenn Sie die Anzahl der Kerne haben, können Sie perfekt arbeiten. Versuchen Sie, das Array etwas größer zu machen. Wenn sich das Ergebnis ändert, bedeutet dies, dass Sie die Befehlszeile in das Teil einfügen, das nicht parallelisiert werden kann. Selbst in diesem Fall besteht die Möglichkeit, dass es korrekt parallelisiert werden kann. Versuchen Sie es also bitte. Wenn es nicht parallelisiert ist, ist die Befehlszeile möglicherweise falsch geschrieben oder gcc ist möglicherweise zu alt, um sie zu unterstützen. Wenn Sie nach der Version fragen, sehen Sie normalerweise mehr Zahlen als im folgenden Beispiel. Bitte prüfen.
[]$ gcc --version
gcc (GCC) 4.9.3
Copyright (C) 2015 Free Software Foundation, Inc.
Wenn Sie viel Verarbeitung benötigen und genügend CPU im Knoten haben, ziehen Sie bitte parallel zu OpenMP in Betracht (diesmal eingeführt). Wenn die Maschinenumgebung dies zulässt, berücksichtigen Sie bitte MPI (vorherige Einführung). Wenn die Datenmenge nicht sehr groß ist, kann das Abrollen möglicherweise schneller erfolgen. Bitte erwägen Sie eine Beschleunigung entsprechend der Datenmenge.
Wenn Sie sich Sorgen über das Abrollen machen, lesen Sie bitte Folgendes. Dies ist ein Beispiel für 8 Schritte. Erhöhen Sie die Berechnungsdichte in der Schleife und lassen Sie sie sofort berechnen. Die optimale Anzahl von Stufen hängt von der Berechnung ab. Bitte versuchen Sie es. Es ist zu beachten, dass die Anzahl der Anordnungen durch die Anzahl der Stufen geteilt wird. Andernfalls wird ein Segmentierungsfehler angezeigt, wenn Sie die Fraktionierung nicht ordnungsgemäß durchführen, oder das Ergebnis ändert sich.
for (k=0;k<200;k++) {
for (j =0;j<m;j+=8){
for (i =0;i<n;i++){
a[i][j] = b[j][i] + us ;
a[i][j+1] = b[j+1][i] + us ;
a[i][j+2] = b[j+2][i] + us ;
a[i][j+3] = b[j+3][i] + us ;
a[i][j+4] = b[j+4][i] + us ;
a[i][j+5] = b[j+5][i] + us ;
a[i][j+6] = b[j+6][i] + us ;
a[i][j+7] = b[j+7][i] + us ;
}
}
us = us + 1.2 ;
}
Die innerste Schleife wird kontinuierlich gehalten, damit sie in der Pipeline verarbeitet werden kann, und die äußere Schleife wird übersprungen. In diesem Beispiel wird absichtlich ein Array mit denselben Indizes verwendet, die jedoch schneller ausgerichtet werden können, und es scheint, dass das Blockieren der Cache-Optimierung ebenfalls effektiv ist. Versuch es. Es ist ein paralleles Thema, also lasst es uns nach dem Einstellen überprüfen.
[]$ gcc -fopenmp sample2a.c
[]$ export OMP_NUM_THREADS=2
[]$ time ./a.out
TIME=2.066413 data= 1586700000.000000
real 0m1.038s
user 0m2.053s
sys 0m0.016s
[]$ export OMP_NUM_THREADS=4
[]$ time ./a.out
TIME=5.264482 data= 1586700000.000000
real 0m1.344s
user 0m5.206s
sys 0m0.062s
[]$
4 Parallel dazu wurde es langsam. Es wird angenommen, dass dies daran liegt, dass die Granularität der Verarbeitung kleiner geworden ist und die Prozeduren (Fork und Join) vor und nach der Zunahme der Parallelisierung den Effekt der Parallelisierung überschritten haben. Der Grenzwert hierfür hängt vom Verarbeitungsinhalt, der Verarbeitungsmenge und der Maschinenleistung ab. Überprüfen Sie ihn daher und verwenden Sie ihn entsprechend Ihrer eigenen Umgebung. Dieses Mal wurde es auf i5 4core Maschine + VirtualBox + Vinelinux durchgeführt.
das ist alles
Recommended Posts