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

Lesezeit: 12 Minuten

Benutzeravatar von bag-man
Beutelmann

Das ist eine ziemlich interessante Frage, also lassen Sie mich die Szene setzen. 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 er sein kann!

Wir entschieden, dass der beste Weg, dies zu tun, darin bestand, ein einfaches C-Programm zu schreiben, das Primzahlen berechnet und anzeigt, wie lange es dafür gedauert hat, und dann das Programm 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, 100 % CPU-Auslastung erreichte und etwa 10 Minuten dauerte. Als ich nach Hause kam, beschloss ich, es auf meinem modernen Hex-Core-Gaming-PC auszuprobieren, und hier bekommen wir unsere ersten Ausgaben.

Ich habe den Code zunächst so angepasst, dass er unter Windows läuft, da der Gaming-PC diesen verwendete, war aber traurig, als ich feststellte, dass der Prozess nur etwa 15 % der CPU-Leistung erhielt. Ich dachte mir, dass Windows Windows sein muss, also bootete ich in eine Live-CD von Ubuntu und dachte, dass Ubuntu es ermöglichen würde, dass der Prozess mit seinem vollen Potenzial ausgeführt wird, wie es zuvor auf meinem Laptop der Fall war.

Allerdings habe ich nur 5% Nutzung! Meine Frage ist also, wie kann ich das Programm so anpassen, dass es auf meinem Spielautomaten entweder unter Windows 7 oder Live-Linux bei 100% CPU-Auslastung läuft? Eine andere Sache, die großartig, aber nicht notwendig wäre, wäre, wenn das Endprodukt eine .exe-Datei sein könnte, die einfach verteilt und auf Windows-Computern ausgeführt werden könnte.

Danke vielmals!

PS Natürlich funktionierte dieses Programm nicht wirklich mit den Crays 8-Spezialprozessoren, und das ist ein ganz anderes Problem … Wenn Sie etwas über die Optimierung von Code für die Arbeit auf Cray-Supercomputern der 90er Jahre wissen, rufen Sie uns auch an!

  • Ich kann nicht glauben, dass es keine gibt Unicos Schild. 😉

    – Eduard Thomson

    11. Februar 2012 um 22:15 Uhr

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

    – mikithskegg

    11. Februar 2012 um 22:15 Uhr

  • Bin ich der einzige, der diese Frage überhaupt nicht interessant findet? Kommen Sie, ein Single-Thread-Programm auf einem n-Core-Rechner auszuführen und zu fragen, warum es 1/n der CPU verwendet, ist nur … egal, ich stimme nur ab 🙂

    – Günther Piez

    12. Februar 2012 um 2:04 Uhr

  • @drhirsch Nun, die Frage zeigt den Forschungsaufwand. Ich habe dafür +1 gegeben – auch wenn dem OP etwas Grundlegendes zum Multi-Core-Computing fehlt.

    – Mystisch

    12. Februar 2012 um 2:14 Uhr

  • @drhirsch Es gibt viele uninteressante Fragen auf der Seite. Interessant oder nicht ist jedoch subjektiv. Ihm fehlen vielleicht die Grundlagen und das ist nicht subjektiv. Wie Mystical sagte, zeigt es Forschungsaufwand und ist nicht so einfach zu beantworten, wie es scheint.

    – Carl

    12. Februar 2012 um 23:48 Uhr

Benutzeravatar von Mystical
Mystisch

Wenn Sie 100 % CPU wollen, 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 Rechner länger 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 Ihre 100% CPU:

Geben Sie hier die Bildbeschreibung ein

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

    – Mystisch

    11. Februar 2012 um 22:54 Uhr


  • @Mystical Offtopic: Welche Hardware verwendest du? Mein Hex-Core AMD @ 3,2 GHz hat es in 92 Sekunden geschafft …

    – Beutelmann

    11. Februar 2012 um 23:15 Uhr


  • @Owen: Er hat einen Core i7 2600K … ich bin neidisch.

    – cha0site

    11. Februar 2012 um 23:23 Uhr

  • Aug! Zu … viel … rosa!

    – Mateen Ulhaq

    12. Februar 2012 um 3:54 Uhr

  • @MohammadFadin en.wikipedia.org/wiki/Parallel_computing Grundsätzlich müssen Sie in der Lage sein, mehrere Aufgaben parallel zu bearbeiten, um einen Mehrkernrechner nutzen zu können.

    – Mystisch

    13. Februar 2012 um 20:06 Uhr

Benutzeravatar von cha0site
cha0site

Sie führen einen Prozess auf einem Multi-Core-Rechner aus – er läuft also nur auf einem Kern.

Die Lösung ist einfach genug, da Sie nur versuchen, den Prozessor zu koppeln – wenn Sie N Kerne haben, führen Sie Ihr Programm N Mal aus (natürlich parallel).

Beispiel

Hier ist ein Code, der Ihr Programm ausführt NUM_OF_CORES Mal parallel. Es ist POSIXy-Code – er verwendet fork – Sie sollten das also 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

  • Ah, wie wenn Sie Prime95 ausführen müssen, Sie haben mehrere Instanzen davon … Sicherlich gibt es eine Möglichkeit für einen Prozess, mehrere Kerne zu verwenden? Wie es Hash-Cracking-Programme tun.

    – Beutelmann

    11. Februar 2012 um 22:19 Uhr

  • Nun, ein Prozess könnte Threads verwenden, um Multiprocessing durchzuführen, aber ich glaube nicht, dass Sie das gemeint haben, da ein Thread in diesem Kontext fast ein separater Prozess ist. Worüber wir hier wirklich sprechen, sind „Ausführungsköpfe“, seien es Threads oder Prozesse. Also, nein, es gibt keine Möglichkeit, ein Single-Threaded-Programm auf mehreren Kernen laufen zu lassen, Sie müssen es umschreiben. Und manchmal ist es Ja wirklich schwer. Und manchmal ist es tatsächlich unmöglich.

    – cha0site

    11. Februar 2012 um 22:23 Uhr

  • Nun, ich schätze, 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 mich verraten hat: P), wo wäre ein guter Anfang?

    – Beutelmann

    11. Februar 2012 um 22:26 Uhr

  • @Owen: Nun, UNICOS sieht so aus, als ob es Unix etwas ähnlich ist (Wikipedia lässt es sowieso glauben), also hat es wahrscheinlich fork(). Du solltest lernen, wie man damit umgeht, denke ich.

    – cha0site

    11. Februar 2012 um 22:32 Uhr


  • Oooh! +1 jetzt, wo Sie das Beispiel haben. 🙂

    – Mystisch

    12. Februar 2012 um 18:52 Uhr

Wir wollen wirklich sehen, wie schnell es gehen kann!

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

Um alle CPUs einfach zu verbrauchen, könnten Sie eine lösen peinlich paralleles Problem zB berechnen Mandelbrot-Menge oder verwenden genetische Programmierung, um Mona Lisa zu malen in mehreren Threads (Prozessen).

Ein anderer Ansatz besteht darin, ein vorhandenes Benchmark-Programm für den Cray-Supercomputer zu nehmen und es 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 könnte sogar die Ergebnisse ruinieren, wenn er so gut ist, dass er absichtlich moderne CPU-Funktionen/Macken ausnutzt.

    – Numeron

    10. Oktober 2019 um 3:43 Uhr


  • @Numeron: Das Geschwindigkeitsverhältnis zwischen der Integer-Division ALU und der Bandbreite des Speichers (oder L3-Cache) für ein Sieb von Eratosthenes könnte auf alten und neuen Maschinen sehr unterschiedlich sein, daher ist es tatsächlich interessant, sich beide Benchmarks anzusehen. (z.B Sieb von Eratosthenes in x86-Montage hat einige Diskussionen darüber, dass die Speicherbandbreite begrenzt ist, wenn Primzahlen nahe oder größer als etwa 512 durchgestrichen werden (der Schritt geht also um eine ganze Cache-Zeile, wenn ein bitgepacktes Sieb verwendet wird, unter der Annahme eines effizienten Bitzugriffs durch sorgfältige Optimierung).

    – Peter Cordes

    gestern

Der Grund, warum Sie 15 % für einen Hex-Core-Prozessor erhalten, liegt darin, dass Ihr Code 1 Kern zu 100 % verwendet. 100/6 = 16,67 %, was bei Verwendung eines gleitenden Durchschnitts mit Prozessplanung (Ihr Prozess würde unter normaler Priorität laufen) leicht als 15 % angegeben werden könnte.

Um 100% CPU zu nutzen, müssten Sie daher alle Kerne Ihrer CPU verwenden – starten Sie 6 parallele Ausführungscodepfade für eine Hex-Core-CPU und skalieren Sie diese bis zu der Anzahl von Prozessoren, die Ihre Cray-Maschine hat 🙂

Seien Sie auch sehr bewusst wie Sie laden die CPU. Eine CPU kann viele verschiedene Aufgaben ausführen, und während viele von ihnen als „die CPU zu 100 % laden“ gemeldet werden, können sie jeweils 100 % verschiedener Teile der CPU verwenden. Mit anderen Worten, es ist sehr schwierig, zwei verschiedene CPUs hinsichtlich der Leistung zu vergleichen, insbesondere zwei verschiedene CPU-Architekturen. Die Ausführung von Aufgabe A kann eine CPU gegenüber einer anderen bevorzugen, während die Ausführung von Aufgabe B leicht umgekehrt sein 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önnten Befehle pro Sekunde sein, aber andererseits werden Befehle auf verschiedenen CPU-Architekturen nicht gleich erstellt. Ein weiteres Maß könnte die Cache-IO-Leistung sein, aber die Cache-Infrastruktur ist auch nicht gleich. Dann könnte ein Maß die Anzahl der Befehle pro verwendetem Watt sein, da die Leistungsabgabe und -ableitung oft ein begrenzender Faktor beim Entwerfen eines Cluster-Computers ist.

Ihre erste Frage sollte also lauten: Welcher Leistungsparameter ist Ihnen wichtig? Was willst du 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 😉

Tschüss Steen

TLDR; Die akzeptierte Antwort ist sowohl ineffizient als auch inkompatibel. Folgende Algo funktioniert 100x Schneller.

Der auf MAC verfügbare gcc-Compiler kann nicht ausgeführt werden omp. Ich musste llvm installieren (brew install llvm ). Aber ich Ich habe nicht gesehen, dass der CPU-Leerlauf nach unten ging beim Ausführen der OMP-Version.

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 und ausgeführt werden kann sah fast die gesamte CPU aufgebraucht 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
        pr
        rc = pthread_create(&threads
        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
        if (rc) {
            printf("Error:unable to join, %d" ,rc);
            exit(-1);
        }
        totalPrimesFound += pr
    }
    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 wird –
Geben Sie hier die Bildbeschreibung ein

PS – Wenn Sie die Anzahl der Threads erhöhen, sinkt die tatsächliche CPU-Auslastung (Versuchen Sie, die Anzahl der Threads auf 20 zu setzen.), da das System mehr Zeit für die Kontextumschaltung benötigt als für die eigentliche Berechnung.

Übrigens ist meine Maschine nicht so kräftig wie @mystical (akzeptierte Antwort). Aber meine Version mit grundlegendem POSIX-Threading funktioniert viel schneller als die von OMP. Hier ist das Ergebnis –

Geben Sie hier die Bildbeschreibung ein

PS Erhöhen Sie die Threadlast auf 2,5 Millionen, um die CPU-Auslastung zu sehen, da sie in weniger als einer Sekunde abgeschlossen ist.

Benutzeravatar von mikithskegg
mikithskegg

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

1418410cookie-checkSo erhalten Sie 100% CPU-Auslastung von einem C-Programm

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy