Как получить 100% использование ЦП из программы C




windows linux (6)

Это довольно интересный вопрос, поэтому позвольте мне установить сцену. Я работаю в Национальном музее вычислительной техники, и с 1992 года нам удалось получить суперкомпьютер Cray Y-MP EL, и мы действительно хотим видеть, как быстро он может идти!

Мы решили, что лучший способ сделать это - написать простую программу на языке С, которая будет вычислять простые числа и показывать, сколько времени потребовалось для этого, а затем запустить программу на быстром современном настольном ПК и сравнить результаты.

Мы быстро придумали этот код для подсчета простых чисел:

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

Что на нашем двухъядерном ноутбуке под управлением Ubuntu (The Cray работает UNICOS), отлично работает, получая 100% -ное использование ЦП и занимает около 10 минут или около того. Когда я вернулся домой, я решил попробовать его на своем шестиъядерном современном игровом ПК, и именно здесь мы получаем наши первые проблемы.

Сначала я адаптировал код для работы в Windows, так как это то, что использовал игровой ПК, но было опечалено, обнаружив, что процесс получает только около 15% мощности процессора. Я полагал, что Windows должна быть Windows, поэтому я загрузился в Live CD Ubuntu, думая, что Ubuntu позволит процессу работать с полным потенциалом, как это было раньше на моем ноутбуке.

Однако я получил только 5% использования! Итак, мой вопрос: как я могу адаптировать программу для запуска на своем игровом автомате в Windows 7 или Live Linux при 100% использовании процессора? Другое дело, что было бы здорово, но не обязательно, если конечный продукт может быть одним .exe, который может быть легко распространен и запущен на машинах Windows.

Большое спасибо!

PS Конечно, эта программа действительно не работала с процессорами Crays 8, и это еще одна проблема ... Если вы знаете что-нибудь об оптимизации кода для работы на 90-х годах Cray суперкомпьютеры, дайте нам тоже крик!


мы действительно хотим видеть, как быстро это может пойти!

Ваш алгоритм для генерации простых чисел очень неэффективен. Сравните его с primegen который генерирует 50847534 простых чисел до 1000000000 всего за 8 секунд на Pentium II-350.

Чтобы легко использовать все процессоры, вы можете решить неловко параллельную проблему, например, вычислить набор Мандельброта или использовать генетическое программирование для рисования Mona Lisa в нескольких потоках (процессах).

Другой подход - взять существующую контрольную программу для суперкомпьютера Cray и перенести ее на современный ПК.


TLDR; Принятый ответ неэффективен и несовместим. Следующий алгоритм работает в 100 раз быстрее.

Компилятор gcc, доступный на MAC, не может запускать omp . Мне пришлось установить llvm (brew install llvm ) . Но я не видел, чтобы простаивал CPU при запуске версии OMP.

Вот скриншот, когда была запущена версия OMP.

В качестве альтернативы я использовал основной поток POSIX, который можно запустить с помощью любого компилятора c и увидел почти весь процессор, используемый при nos of thread = no of cores core = 4 (MacBook Pro, 2.3 ГГц Intel Core i5). Вот программа -

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

Обратите внимание, как весь процессор исчерпан -

PS - Если вы увеличиваете количество потоков, то фактическое использование ЦП уменьшается (попробуйте сделать нитки = 20.), Поскольку система использует больше времени при переключении контекста, чем фактические вычисления.

Кстати, моя машина не так мучительна, как @mystical (Accepted answer). Но моя версия с базовой поточной POSIX работает быстрее, чем OMP. Вот результат -

PS Увеличьте поток до 2,5 миллионов, чтобы увидеть использование ЦП, так как оно завершается менее чем за секунду.


Для быстрого улучшения на одном ядре удалите системные вызовы для уменьшения контекстного переключения. Удалите эти строки:

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

Первое особенно плохо, так как оно будет порождать новый процесс на каждой итерации.


Если вам нужен 100% процессор, вам нужно использовать более 1 ядра. Для этого вам нужно несколько потоков.

Вот параллельная версия с использованием OpenMP:

Я должен был увеличить лимит до 1000000 чтобы он занял более 1 секунды на моей машине.

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

Вывод:

Эта машина рассчитала все 78498 простых чисел до 1000000 в 29.753 секунды

Вот ваш 100% процессор:


Причина, по которой вы получаете 15% на шестнадцатеричном процессоре, - это то, что ваш код использует 1 ядро ​​на 100%. 100/6 = 16,67%, что с использованием скользящей средней с планированием процесса (ваш процесс будет работать под обычным приоритетом) можно легко сообщить как 15%.

Поэтому, чтобы использовать 100% -ный процессор, вам нужно будет использовать все ядра вашего процессора - запустить 6 параллельных кодов кода для шестнадцатеричного ядра и иметь этот масштаб вплоть до того, сколько процессоров будет у вашей машины Cray :)


Просто попробуйте Zip и распакуйте большой файл, ничего, поскольку тяжелые операции ввода-вывода могут использовать процессор.





cray