Dies ist eine ziemlich interessante Frage, also lassen Sie mich die Szene einstellen. Ich arbeite im National Museum of Computing, und wir haben es gerade geschafft, einen Cray Y-MP EL-Supercomputer von 1992 zum Laufen zu bringen, und wir wollen wirklich sehen, wie schnell es gehen kann!
Wir entschieden, dass der beste Weg, dies zu tun, darin bestand, ein einfaches C-Programm zu schreiben, das Primzahlen berechnet und zeigt, wie lange es dafür gedauert hat, das Programm dann auf einem schnellen modernen Desktop-PC auszuführen und die Ergebnisse zu vergleichen.
Wir haben uns schnell diesen Code ausgedacht, um Primzahlen zu zählen:
#include <stdio.h>
#include <time.h>
void main() {
clock_t start, end;
double runTime;
start = clock();
int i, num = 1, primes = 0;
while (num <= 1000) {
i = 2;
while (i <= num) {
if(num % i == 0)
break;
i++;
}
if (i == num)
primes++;
system("clear");
printf("%d prime numbers calculated\n",primes);
num++;
}
end = clock();
runTime = (end - start) / (double) CLOCKS_PER_SEC;
printf("This machine calculated all %d prime numbers under 1000 in %g seconds\n", primes, runTime);
}
Was auf unserem Dual-Core-Laptop mit Ubuntu (The Cray läuft UNICOS) perfekt funktionierte, eine 100% ige CPU-Auslastung erreichte und ungefähr 10 Minuten dauerte. Als ich nach Hause kam, beschloss ich, es auf meinem modernen Hex-Core-Gaming-PC zu versuchen, und hier bekommen wir unsere ersten Ausgaben.
Ich habe den Code zuerst für die Ausführung unter Windows angepasst, da dies vom Gaming-PC verwendet wurde. Ich war jedoch traurig, dass der Prozess nur etwa 15% der CPU-Leistung beanspruchte. Ich dachte, dass Windows Windows sein muss, also startete ich eine Live-CD von Ubuntu und dachte, Ubuntu würde es dem Prozess ermöglichen, sein volles Potenzial auszuschöpfen, wie es zuvor auf meinem Laptop geschehen war.
Allerdings habe ich nur 5% Auslastung! Meine Frage ist also, wie ich das Programm so anpassen kann, dass es auf meinem Spielautomaten unter Windows 7 oder unter Linux mit 100% CPU-Auslastung ausgeführt wird. Eine andere Sache, die großartig, aber nicht notwendig wäre, ist, wenn das Endprodukt eine EXE-Datei sein kann, die einfach verteilt und auf Windows-Computern ausgeführt werden kann.
Danke vielmals!
PS: Natürlich hat dieses Programm mit den Crays 8-Spezialprozessoren nicht wirklich funktioniert, und das ist ein ganz anderes Problem ... Wenn Sie etwas über die Optimierung von Code für die Arbeit mit Cray-Supercomputern der 90er Jahre wissen, rufen Sie uns auch an!
Antworten:
Wenn Sie 100% CPU möchten, müssen Sie mehr als 1 Kern verwenden. Dazu benötigen Sie mehrere Threads.
Hier ist eine parallele Version mit OpenMP:
Ich musste das Limit erhöhen
1000000
, damit es auf meinem Computer länger als 1 Sekunde dauerte.#include <stdio.h> #include <time.h> #include <omp.h> int main() { double start, end; double runTime; start = omp_get_wtime(); int num = 1,primes = 0; int limit = 1000000; #pragma omp parallel for schedule(dynamic) reduction(+ : primes) for (num = 1; num <= limit; num++) { int i = 2; while(i <= num) { if(num % i == 0) break; i++; } if(i == num) primes++; // printf("%d prime numbers calculated\n",primes); } end = omp_get_wtime(); runTime = end - start; printf("This machine calculated all %d prime numbers under %d in %g seconds\n",primes,limit,runTime); return 0; }
Ausgabe:
Hier ist Ihre 100% CPU:
quelle
Sie führen einen Prozess auf einem Multi-Core-Computer aus, sodass er nur auf einem Core ausgeführt wird.
Die Lösung ist einfach genug, da Sie nur versuchen, den Prozessor zu fixieren. Wenn Sie über N Kerne verfügen, führen Sie Ihr Programm N-mal aus (natürlich parallel).
Beispiel
Hier ist ein Code, der Ihre Programmzeiten
NUM_OF_CORES
parallel ausführt. Es ist POSIXy-Code - er verwendetfork
- also sollten Sie ihn unter Linux ausführen. Wenn das, was ich über den Cray lese, richtig ist, ist es möglicherweise einfacher, diesen Code zu portieren als den OpenMP-Code in der anderen Antwort.#include <stdio.h> #include <time.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #define NUM_OF_CORES 8 #define MAX_PRIME 100000 void do_primes() { unsigned long i, num, primes = 0; for (num = 1; num <= MAX_PRIME; ++num) { for (i = 2; (i <= num) && (num % i != 0); ++i); if (i == num) ++primes; } printf("Calculated %d primes.\n", primes); } int main(int argc, char ** argv) { time_t start, end; time_t run_time; unsigned long i; pid_t pids[NUM_OF_CORES]; /* start of test */ start = time(NULL); for (i = 0; i < NUM_OF_CORES; ++i) { if (!(pids[i] = fork())) { do_primes(); exit(0); } if (pids[i] < 0) { perror("Fork"); exit(1); } } for (i = 0; i < NUM_OF_CORES; ++i) { waitpid(pids[i], NULL, 0); } end = time(NULL); run_time = (end - start); printf("This machine calculated all prime numbers under %d %d times " "in %d seconds\n", MAX_PRIME, NUM_OF_CORES, run_time); return 0; }
Ausgabe
$ ./primes Calculated 9592 primes. Calculated 9592 primes. Calculated 9592 primes. Calculated 9592 primes. Calculated 9592 primes. Calculated 9592 primes. Calculated 9592 primes. Calculated 9592 primes. This machine calculated all prime numbers under 100000 8 times in 8 seconds
quelle
UNICOS
sieht so aus, als ob es Unix etwas ähnlich ist (Wikipedia lässt es sowieso denken), also hat es wahrscheinlichfork()
. Du solltest lernen, wie man das benutzt, denke ich.Ihr Algorithmus zum Generieren von Primzahlen ist sehr ineffizient. Vergleichen Sie es mit Primegen , das auf einem Pentium II-350 die Primzahlen 50847534 bis zu 1000000000 in nur 8 Sekunden generiert.
Um alle CPUs einfach zu verbrauchen, können Sie ein peinlich paralleles Problem lösen , z. B. das Mandelbrot-Set berechnen oder mithilfe der genetischen Programmierung Mona Lisa in mehreren Threads (Prozessen) malen .
Ein anderer Ansatz besteht darin, ein vorhandenes Benchmark-Programm für den Cray-Supercomputer auf einen modernen PC zu portieren.
quelle
Der Grund, warum Sie 15% auf einem Hex-Core-Prozessor erhalten, ist, dass Ihr Code 1 Core zu 100% verwendet. 100/6 = 16,67%, was bei Verwendung eines gleitenden Durchschnitts mit Prozessplanung (Ihr Prozess würde unter normaler Priorität ausgeführt werden) leicht als 15% angegeben werden könnte.
Um 100% CPU zu verwenden, müssten Sie daher alle Kerne Ihrer CPU verwenden - starten Sie 6 parallele Ausführungscodepfade für eine Hex-Core-CPU und haben Sie diese Skalierung bis zu wie vielen Prozessoren Ihre Cray-Maschine hat :)
quelle
Beachten Sie auch, wie Sie die CPU laden. Eine CPU kann viele verschiedene Aufgaben ausführen, und während viele von ihnen als "100% CPU laden" gemeldet werden, können sie jeweils 100% verschiedener Teile der CPU verwenden. Mit anderen Worten, es ist sehr schwierig, zwei verschiedene CPUs hinsichtlich ihrer Leistung zu vergleichen, insbesondere zwei verschiedene CPU-Architekturen. Das Ausführen von Aufgabe A kann eine CPU einer anderen vorziehen, während das Ausführen von Aufgabe B leicht umgekehrt sein kann (da die beiden CPUs intern unterschiedliche Ressourcen haben und Code sehr unterschiedlich ausführen können).
Dies ist der Grund, warum Software für die optimale Leistung von Computern genauso wichtig ist wie Hardware. Dies gilt in der Tat auch für "Supercomputer".
Ein Maß für die CPU-Leistung könnten Anweisungen pro Sekunde sein, aber andererseits werden Anweisungen auf verschiedenen CPU-Architekturen nicht gleich erstellt. Ein weiteres Maß könnte die Cache-E / A-Leistung sein, aber auch die Cache-Infrastruktur ist nicht gleich. Dann könnte ein Maß die Anzahl der Anweisungen pro verwendetem Watt sein, da die Leistungsabgabe und -ableitung beim Entwurf eines Clustercomputers häufig ein begrenzender Faktor ist.
Ihre erste Frage sollte also lauten: Welcher Leistungsparameter ist für Sie wichtig? Was möchten Sie messen? Wenn Sie sehen möchten, welche Maschine die meisten FPS aus Quake 4 herausholt, ist die Antwort einfach. Ihr Gaming-Rig wird es tun, da der Cray dieses Programm überhaupt nicht ausführen kann ;-)
Prost, Steen
quelle
TLDR; Die akzeptierte Antwort ist sowohl ineffizient als auch inkompatibel. Das Folgen von algo funktioniert 100x schneller.
Der auf dem MAC verfügbare gcc-Compiler kann nicht ausgeführt werden
omp
. Ich musste llvm installieren(brew install llvm )
. Ich habe jedoch nicht gesehen, dass der CPU-Leerlauf während der Ausführung der OMP-Version ausfällt.Hier ist ein Screenshot, während die OMP-Version ausgeführt wurde.
Alternativ habe ich den grundlegenden POSIX-Thread verwendet, der mit jedem c-Compiler ausgeführt werden kann, und fast die gesamte CPU verbraucht, wenn
nos of thread
=no of cores
= 4 (MacBook Pro, 2,3 GHz Intel Core i5). Hier ist das Programm -#include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <math.h> #define NUM_THREADS 10 #define THREAD_LOAD 100000 using namespace std; struct prime_range { int min; int max; int total; }; void* findPrime(void *threadarg) { int i, primes = 0; struct prime_range *this_range; this_range = (struct prime_range *) threadarg; int minLimit = this_range -> min ; int maxLimit = this_range -> max ; int flag = false; while (minLimit <= maxLimit) { i = 2; int lim = ceil(sqrt(minLimit)); while (i <= lim) { if (minLimit % i == 0){ flag = true; break; } i++; } if (!flag){ primes++; } flag = false; minLimit++; } this_range ->total = primes; pthread_exit(NULL); } int main (int argc, char *argv[]) { struct timespec start, finish; double elapsed; clock_gettime(CLOCK_MONOTONIC, &start); pthread_t threads[NUM_THREADS]; struct prime_range pr[NUM_THREADS]; int rc; pthread_attr_t attr; void *status; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); for(int t=1; t<= NUM_THREADS; t++){ pr[t].min = (t-1) * THREAD_LOAD + 1; pr[t].max = t*THREAD_LOAD; rc = pthread_create(&threads[t], NULL, findPrime,(void *)&pr[t]); if (rc){ printf("ERROR; return code from pthread_create() is %d\n", rc); exit(-1); } } int totalPrimesFound = 0; // free attribute and wait for the other threads pthread_attr_destroy(&attr); for(int t=1; t<= NUM_THREADS; t++){ rc = pthread_join(threads[t], &status); if (rc) { printf("Error:unable to join, %d" ,rc); exit(-1); } totalPrimesFound += pr[t].total; } clock_gettime(CLOCK_MONOTONIC, &finish); elapsed = (finish.tv_sec - start.tv_sec); elapsed += (finish.tv_nsec - start.tv_nsec) / 1000000000.0; printf("This machine calculated all %d prime numbers under %d in %lf seconds\n",totalPrimesFound, NUM_THREADS*THREAD_LOAD, elapsed); pthread_exit(NULL); }
Beachten Sie, wie die gesamte CPU verbraucht ist -
PS - Wenn Sie die Anzahl der Threads erhöhen, sinkt die tatsächliche CPU-Auslastung (versuchen Sie, keine Threads = 20 zu erstellen), da das System beim Kontextwechsel mehr Zeit benötigt als beim eigentlichen Rechnen.
Übrigens ist meine Maschine nicht so bullig wie @mystical (Akzeptierte Antwort). Aber meine Version mit einfachem POSIX-Threading funktioniert viel schneller als OMP. Hier ist das Ergebnis -
PS Erhöhen Sie die Thread-Last auf 2,5 Millionen, um die CPU-Auslastung zu sehen, da sie in weniger als einer Sekunde abgeschlossen ist.
quelle
Versuchen Sie, Ihr Programm mit OpenMP zu parallelisieren. Es ist ein sehr einfacher und effektiver Rahmen für die Erstellung paralleler Programme.
quelle
Entfernen Sie für eine schnelle Verbesserung eines Kerns Systemaufrufe, um die Kontextumschaltung zu reduzieren. Entfernen Sie diese Zeilen:
system("clear"); printf("%d prime numbers calculated\n",primes);
Das erste ist besonders schlecht, da es bei jeder Iteration einen neuen Prozess erzeugt.
quelle
Versuchen Sie einfach, eine große Datei zu komprimieren und zu entpacken.
quelle