So erhalten Sie 100% CPU-Auslastung von einem C-Programm


79

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!


8
Ich kann nicht glauben, dass es kein Unicos- Tag gibt. ;)
Edward Thomson

32
Es ist eine seltsame Sache, dass dieses Single-Thread-Programm 100% der CPU-Auslastung auf dem DUAL CORE-Prozessor
beanspruchte

24
Bin ich der einzige, der diese Frage überhaupt nicht interessant findet? Kommen Sie, ein Single-Thread-Programm auf einem n-Core-Computer auszuführen und zu fragen, warum es 1 / n der CPU verwendet, ist einfach ... egal, ich stimme nur ab :-)
Gunther Piez

16
@drhirsch Nun, die Frage zeigt den Forschungsaufwand. Ich habe mich dafür entschieden - auch wenn dem OP etwas Grundlegendes am Multi-Core-Computing fehlt.
Mysticial

9
@drhirsch Es gibt viele uninteressante Fragen auf der Website. Interessant oder nicht ist jedoch subjektiv. Möglicherweise fehlen ihm die Grundlagen, und das ist nicht subjektiv. Wie Mystical sagte, zeigt es Forschungsaufwand und ist nicht so einfach zu beantworten, wie es scheint.
Carl

Antworten:


81

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:

Diese Maschine berechnete alle 78498 Primzahlen unter 1000000 in 29,753 Sekunden

Hier ist Ihre 100% CPU:

Geben Sie hier die Bildbeschreibung ein


1
@ cha0site Ja, ich habe hauptsächlich die Frage für den Spielautomaten beantwortet. Es gibt definitiv interessantere Möglichkeiten, die CPU zu fixieren. Einer der berüchtigtsten Benchmarks, die ich durchgeführt habe, ist meine Antwort auf diese Frage, die 2 von 4 von mir getesteten Maschinen überhitzt hat.
Mysticial

1
@Mystical Offtopic: Welche Hardware verwenden Sie? Mein Hex-Core AMD @ 3.2Ghz hat es in 92 Sekunden geschafft ...
Bag-Man

1
@ Owen: Er hat einen Core i7 2600K ... ich bin eifersüchtig.
Cha0site

19
Augh! Zu ... viel ... pink!
Mateen Ulhaq

2
@MohammadFadin en.wikipedia.org/wiki/Parallel_computing Grundsätzlich müssen Sie mehrere Aufgaben parallel zu können , verarbeiten können ein Multi-Core - Computer nutzen.
Mysticial

24

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_CORESparallel ausführt. Es ist POSIXy-Code - er verwendet fork- 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

Wenn Sie Prime95 ausführen müssen, haben Sie mehrere Instanzen davon ... Sicher gibt es eine Möglichkeit für einen Prozess, mehrere Kerne zu verwenden? Wie Hash-Cracking-Programme.
Bag-Man

Nun, ein Prozess könnte Threads für die Mehrfachverarbeitung verwenden, aber ich glaube nicht, dass Sie das gemeint haben, da ein Thread in diesem Zusammenhang fast ein separater Prozess ist. Worüber wir hier wirklich sprechen, sind "Ausführungsköpfe", seien es Threads oder Prozesse. Nein, es gibt keine Möglichkeit, ein Single-Thread-Programm auf mehreren Kernen auszuführen. Sie müssen es neu schreiben. Und manchmal ist es wirklich schwer. Und manchmal ist es tatsächlich unmöglich.
Cha0site

Nun, ich denke, es wird nicht so schwer sein, das Programm auch für den Cray zum Laufen zu bringen. In Anbetracht dessen, dass ich ziemlich neu in diesem Bereich bin (Was hat mich verraten: P), wo wäre ein guter Anfang?
Bag-Man

@Owen: Nun, es UNICOSsieht so aus, als ob es Unix etwas ähnlich ist (Wikipedia lässt es sowieso denken), also hat es wahrscheinlich fork(). Du solltest lernen, wie man das benutzt, denke ich.
Cha0site

2
Oooh! + 1'ed jetzt, wo Sie das Beispiel haben. :)
Mysticial

7

wir wollen wirklich sehen, wie schnell es gehen kann!

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.


Es spielt keine Rolle, dass der Algorithmus ineffizient ist, da das Ziel nicht darin besteht, die Primzahlen tatsächlich zu berechnen, sondern eine allgemein schwierige Aufgabe auszuführen und zu sehen, wie viel besser oder schlechter er darin ist als ein moderner Desktop. Ein effizienter Algorithmus würde diesen Vergleich nur erschweren und sogar die Ergebnisse ruinieren, wenn er so gut ist, dass er absichtlich die modernen CPU-Funktionen / Macken nutzt.
Numeron

5

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 :)


Das Problem dabei ist, wie kann ich die Geschwindigkeit jeder Maschine klar abschätzen? Außerdem hat der Cray anscheinend "Vektorprozessoren", so dass eine Menge mehr Arbeit erforderlich ist, um ordnungsgemäß zu funktionieren
bag-man

Weiß nicht. Wahrscheinlich Unterschiede in den Planungsprozessen.
Carl

2

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


2

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. Geben Sie hier die Bildbeschreibung ein

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 - Geben Sie hier die Bildbeschreibung ein

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 -

Geben Sie hier die Bildbeschreibung ein

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.


0

Versuchen Sie, Ihr Programm mit OpenMP zu parallelisieren. Es ist ein sehr einfacher und effektiver Rahmen für die Erstellung paralleler Programme.


0

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.


Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.