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



windows linux (6)

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

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

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

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

Это довольно интересный вопрос, поэтому позвольте мне установить сцену. Я работаю в Национальном музее вычислительной техники, и с 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 суперкомпьютеры, дайте нам тоже крик!


Answer #1

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 миллионов, чтобы увидеть использование ЦП, так как оно завершается менее чем за секунду.


Answer #2

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

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

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


Answer #3

Если вам нужен 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% процессор:


Answer #4

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

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


Answer #5

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





cray