Pregunta Cómo obtener el 100% de uso de CPU de un programa C


Esta es una pregunta bastante interesante, así que déjame establecer la escena. Trabajo en el Museo Nacional de Computación, y recién hemos logrado obtener una súper computadora Cray Y-MP EL desde 1992, ¡y realmente queremos ver qué tan rápido puede llegar!

Decidimos que la mejor manera de hacerlo era escribir un programa simple de C que calculó los números primos y mostrar cuánto tiempo tomó hacerlo, luego ejecutar el programa en una computadora de escritorio moderna y rápida y comparar los resultados.

Rápidamente creamos este código para contar números primos:

#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);
}

Lo cual en nuestra computadora portátil dual con Ubuntu (The Cray ejecuta UNICOS), funcionó a la perfección, obteniendo el 100% de uso de la CPU y tomando aproximadamente 10 minutos más o menos. Cuando llegué a casa, decidí probarlo en mi PC de juegos modernos de núcleo hexadecimal, y aquí es donde obtenemos nuestros primeros problemas.

Primero adapté el código para ejecutar en Windows, ya que eso es lo que estaba usando la PC de juegos, pero me entristeció descubrir que el proceso solo obtenía aproximadamente el 15% de la potencia de la CPU. Pensé que Windows debe ser Windows, así que arranqué en un Live CD de Ubuntu pensando que Ubuntu permitiría que el proceso se ejecutara con todo su potencial como lo había hecho anteriormente en mi computadora portátil.

¡Sin embargo, solo obtuve un 5% de uso! Entonces mi pregunta es, ¿cómo puedo adaptar el programa para que se ejecute en mi máquina de juego en Windows 7 o Linux en vivo al 100% de utilización de la CPU? Otra cosa que sería genial pero no necesaria es si el producto final puede ser un .exe que pueda distribuirse fácilmente y ejecutarse en máquinas con Windows.

¡Muchas gracias!

PD Por supuesto, este programa no funcionó realmente con los procesadores especializados de Crays 8, y ese es otro problema ... Si sabes algo sobre la optimización del código para trabajar en las supercomputadoras Cray de los 90, ¡danos un grito también!


76
2018-02-11 22:11


origen


Respuestas:


Si quieres 100% CPU, necesitas usar más de 1 núcleo. Para hacer eso, necesitas múltiples hilos.

Aquí hay una versión paralela usando OpenMP:

Tuve que aumentar el límite para 1000000 para que tome más de 1 segundo en mi máquina.

#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;
}

Salida:

Esta máquina calculó todos los 78498 números primos por debajo de 1000000 en 29.753 segundos

Aquí está tu CPU 100%:

enter image description here


80
2018-02-11 22:27



Está ejecutando un proceso en una máquina de varios núcleos, por lo que solo se ejecuta en un núcleo.

La solución es bastante fácil, ya que solo está tratando de vincular el procesador: si tiene N núcleos, ejecute su programa N veces (en paralelo, por supuesto).

Ejemplo

Aquí hay un código que ejecuta su programa NUM_OF_CORES tiempos en paralelo. Es código POSIXy - usa fork - Entonces deberías ejecutar eso bajo Linux. Si lo que estoy leyendo sobre Cray es correcto, podría ser más fácil ingresar este código que el código OpenMP en la otra respuesta.

#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;
}

Salida

$ ./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
2018-02-11 22:16



¡Realmente queremos ver qué tan rápido puede llegar!

Su algoritmo para generar números primos es muy ineficiente. Compáralo con primo que genera los números primos 50847534 hasta 1000000000 en solo 8 segundos en un Pentium II-350.

Para consumir todas las CPU fácilmente podría resolver un problema embarazosamente paralelo por ejemplo, calcular Conjunto de Mandelbrot o usar programación genética para pintar Mona Lisa en múltiples hilos (procesos).

Otro enfoque es tomar un programa de referencia existente para el superordenador Cray y llevarlo a una PC moderna.


8
2018-02-11 23:06



La razón por la que obtienes un 15% en un procesador de núcleo hexagonal es porque tu código usa 1 núcleo al 100%. 100/6 = 16.67%, que usando una media móvil con la programación del proceso (su proceso se estaría ejecutando bajo la prioridad normal) podría reportarse fácilmente como 15%.

Por lo tanto, para usar 100% de CPU, necesitaría usar todos los núcleos de su CPU: inicie 6 rutas de código de ejecución paralelas para una CPU de núcleo hexagonal y tenga esta escala hasta sin importar cuántos procesadores tenga su máquina Cray :)


4
2018-02-11 22:25



También sé muy consciente cómo estás cargando la CPU. Una CPU puede hacer muchas tareas diferentes, y aunque muchas de ellas serán reportadas como "cargando la CPU al 100%", cada una puede usar el 100% de las diferentes partes de la CPU. En otras palabras, es muy difícil comparar dos CPU diferentes para el rendimiento, y especialmente dos arquitecturas de CPU diferentes. La ejecución de la tarea A puede favorecer a una CPU sobre otra, mientras que la ejecución de la tarea B puede ser fácilmente al revés (ya que las dos CPU pueden tener recursos internos diferentes y pueden ejecutar código de forma muy diferente).

Esta es la razón por la que el software es tan importante para hacer que las computadoras funcionen de manera óptima como lo es el hardware. Esto también es muy cierto para los "supercomputadores".

Una medida para el rendimiento de la CPU podría ser instrucciones por segundo, pero de nuevo las instrucciones no se crean iguales en diferentes arquitecturas de CPU. Otra medida podría ser el rendimiento de IO de caché, pero la infraestructura de caché tampoco es igual. Entonces, una medida podría ser el número de instrucciones por vatio utilizado, ya que la entrega y la disipación de potencia suelen ser un factor limitante al diseñar una computadora en clúster.

Entonces, su primera pregunta debería ser: ¿Qué parámetro de rendimiento es importante para usted? ¿Qué quieres medir? Si desea ver qué máquina obtiene la mayor cantidad de FPS de Quake 4, la respuesta es fácil; tu plataforma de juego lo hará, ya que Cray no puede ejecutar ese programa en absoluto ;-)

Aclamaciones, Steen


2
2018-04-29 07:56



Intente paralelizar su programa usando, por ejemplo, OpenMP. Es un marco muy simple y efectivo para crear programas paralelos.


0
2018-02-11 22:20



Para una mejora rápida en un núcleo, elimine las llamadas al sistema para reducir el cambio de contexto. Eliminar estas líneas:

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

El primero es particularmente malo, ya que generará un nuevo proceso en cada iteración.


0
2018-02-15 23:09



Simplemente intente comprimir y descomprimir un archivo grande, nada como operaciones pesadas de E / S pueden usar la CPU.


0
2018-02-12 06:11