it-swarm.com.de

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

Dies ist eine sehr interessante Frage, also lassen Sie mich die Szene festlegen. Ich arbeite im National Museum of Computing, und wir haben es gerade geschafft, einen Supercomputer von Cray Y-MP EL aus dem Jahr 1992 zum Laufen zu bringen, und wir wollen wirklich sehen, wie schnell es gehen kann!

Wir haben uns dazu entschlossen, ein einfaches C-Programm zu schreiben, das Primzahlen berechnet und zeigt, wie lange es dauert. Anschließend wird das Programm auf einem schnellen, modernen Desktop-PC ausgeführt und die Ergebnisse verglichen.

Wir haben diesen Code schnell gefunden, 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 unter UNICOS) funktioniert, funktioniert einwandfrei, ist zu 100% CPU-Auslastung und dauert etwa 10 Minuten. Als ich nach Hause kam, entschied ich mich, es auf meinem modernen Gaming-PC zu testen, und hier bekommen wir unsere ersten Ausgaben.

Ich habe den Code zunächst für die Ausführung unter Windows angepasst, da dies der Gaming-PC war, aber ich war traurig, als er feststellte, dass der Prozess nur etwa 15% der CPU-Leistung erreichte. Ich dachte mir, dass dies Windows sein muss, also Windows, also bootete ich mit einer Live-CD von Ubuntu und dachte, Ubuntu würde den Prozess mit seinem vollen Potenzial laufen lassen, wie es früher auf meinem Laptop der Fall war.

Ich habe jedoch nur 5% Verbrauch erhalten! Meine Frage ist also: Wie kann ich das Programm so anpassen, dass es auf meinem Spielautomaten in Windows 7 oder Live-Linux mit 100% CPU-Auslastung ausgeführt wird? Eine andere Sache, die großartig wäre, aber nicht notwendig ist, wenn das Endprodukt eine .exe-Datei sein kann, die leicht verteilt und auf Windows-Computern ausgeführt werden kann.

Danke vielmals!

P.S. Natürlich hat dieses Programm nicht wirklich mit den spezialisierten Crays 8-Prozessoren funktioniert, und das ist ein ganz anderes Thema ... Wenn Sie wissen, was Code-Optimierung für die Arbeit mit 90er Cray-Supercomputern ermöglicht, geben Sie uns ebenfalls Bescheid!

76
bag-man

Wenn Sie 100% CPU benötigen, müssen Sie mehr als einen Kern verwenden. Dazu benötigen Sie mehrere Threads.

Hier ist eine parallele Version, die OpenMP verwendet:

Ich musste das Limit auf 1000000 erhöhen, damit es auf meinem Computer mehr als 1 Sekunde dauert.

#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 deine 100% CPU:

enter image description here

80
Mysticial

Sie führen einen Prozess auf einer Multi-Core-Maschine aus - also nur auf einem Core.

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, mit dem Ihr Programm NUM_OF_CORES-mal parallel ausgeführt wird. Es ist POSIXy-Code - er verwendet fork -, also sollten Sie ihn unter Linux ausführen. Wenn das, was ich über 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
23
cha0site

wir wollen wirklich sehen, wie schnell es geht!

Ihr Algorithmus zur Generierung von Primzahlen ist sehr ineffizient. Vergleichen Sie es mit primegen , das die 50847534-Primzahlen in nur 8 Sekunden auf einem Pentium II-350 in nur 8 Sekunden erzeugt.

Um alle CPUs problemlos zu nutzen, können Sie ein peinlich paralleles Problem z. B. rechnen Mandelbrot-Set oder die genetische Programmierung für Paint Mona Lisa in mehreren Threads (Prozessen) verwenden.

Ein anderer Ansatz besteht darin, ein vorhandenes Benchmark-Programm für den Cray-Supercomputer zu übernehmen und auf einen modernen PC zu portieren.

8
jfs

Der Grund, warum Sie 15% für einen Hex-Core-Prozessor erhalten, ist, dass Ihr Code zu 100% aus einem Kern besteht. 100/6 = 16,67%, was bei Verwendung eines gleitenden Durchschnitts mit Prozessplanung (Ihr Prozess würde unter normaler Priorität ablaufen würde) leicht als 15% gemeldet werden. 

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 diese Skala bis zu den vielen Prozessoren Ihrer Cray-Maschine :)

4
Carl

Seien Sie sich auch bewusst wie Sie die CPU laden. Eine CPU kann viele verschiedene Aufgaben erledigen, und obwohl viele davon als "100% Laden der CPU" gemeldet werden, können sie jeweils 100% verschiedener Teile der CPU verwenden. Mit anderen Worten, es ist sehr schwierig, zwei verschiedene CPUs und insbesondere zwei unterschiedliche CPU-Architekturen zu vergleichen. Die Ausführung von Aufgabe A kann eine CPU gegenüber einer anderen bevorzugen, während Aufgabe B leicht ausgeführt werden kann (da die beiden CPUs intern unterschiedliche Ressourcen haben und Code sehr unterschiedlich ausführen können).

Aus diesem Grund ist Software für die optimale Leistung von Computern genauso wichtig wie Hardware. Dies gilt in der Tat auch für "Supercomputer".

Ein Maß für die CPU-Leistung können Anweisungen pro Sekunde sein, aber Anweisungen werden auf unterschiedlichen CPU-Architekturen nicht gleich erstellt. Eine andere Maßnahme könnte Cache IO Leistung sein, aber die Cache-Infrastruktur ist ebenfalls nicht gleich. Eine Maßnahme könnte dann die Anzahl der Anweisungen pro Watt sein, da die Leistungsabgabe und -ableitung beim Entwerfen eines Cluster-Computers häufig ein begrenzender Faktor ist.

Ihre erste Frage sollte also lauten: Welcher Leistungsparameter ist für Sie wichtig? Was möchtest du messen? Wenn Sie sehen möchten, welcher Computer die meisten FPS aus Quake 4 generiert, ist die Antwort einfach. Ihr Spielgerät wird es tun, da Cray das Programm überhaupt nicht ausführen kann ;-)

Prost,. Steen

2
Steen Schmidt

TLDR; Die akzeptierte Antwort ist sowohl ineffizient als auch inkompatibel. Das folgende Algo arbeitet 100x schneller.

Der auf MAC verfügbare gcc-Compiler kann omp nicht ausführen. Ich musste llvm (brew install llvm ) installieren. Aber ich sah nicht, dass CPU-Leerlauf ausgefallen ist, während die OMP-Version ausgeführt wurde. 

Hier ist ein Screenshot, während die OMP-Version ausgeführt wurde .  enter image description here

Alternativ habe ich den grundlegenden POSIX-Thread verwendet, der mit einem beliebigen c-Compiler ausgeführt werden kann und sah fast die gesamte CPU ausgenutzt, 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 genutzt wird. -  enter image description here

P.S. - Wenn Sie die Anzahl der Threads erhöhen, sinkt die tatsächliche CPU-Auslastung (Versuchen Sie, keine Threads zu verwenden = 20.), Da das System beim Kontextwechsel mehr Zeit in Anspruch nimmt als die tatsächliche Datenverarbeitung.

Mein Rechner ist übrigens nicht so fleischig wie @mystical (Akzeptierte Antwort). Aber meine Version mit grundlegenden POSIX-Threads funktioniert viel schneller als die von OMP. Hier ist das Ergebnis -

 enter image description here

P.S. Erhöhen Sie die Thread-Auslastung auf 2,5 Millionen, um die CPU-Auslastung anzuzeigen, da sie in weniger als einer Sekunde abgeschlossen ist.

0
sapy

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

0
mikithskegg

Versuchen Sie einfach, eine große Datei zu komprimieren und zu entpacken.

0
Nima Mohammadi

Um einen Kern schnell zu verbessern, entfernen Sie Systemaufrufe, um den Kontextwechsel zu reduzieren. Entfernen Sie diese Zeilen:

system("clear");
printf("%d prime numbers calculated\n",primes);

Der erste ist besonders schlecht, da bei jeder Wiederholung ein neuer Prozess erzeugt wird.

0
Joel