Как получить 100% загрузки процессора из программы на C - PullRequest
77 голосов
/ 12 февраля 2012

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

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

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

#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 или Linux с 100% загрузкой процессора?Другая вещь, которая была бы хороша, но не обязательна, - это если конечный продукт может быть одним .exe, который можно легко распространять и запускать на компьютерах с Windows.

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

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

Ответы [ 9 ]

81 голосов
/ 12 февраля 2012

Если вы хотите 100% CPU, вам нужно использовать более 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% процессор:

enter image description here

23 голосов
/ 12 февраля 2012

Вы запускаете один процесс на многоядерном компьютере, поэтому он работает только на одном ядре.

Решение достаточно простое, поскольку вы просто пытаетесь привязать процессор - если у вас N ядер, запустите вашу программу N раз (параллельно, конечно).

Пример

Вот код, который запускает вашу программу NUM_OF_CORES раз параллельно. Это код POSIXy - он использует fork - поэтому вы должны запустить его под Linux. Если то, что я читаю о Cray, верно, возможно, будет проще перенести этот код, чем код OpenMP в другом ответе.

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

Выход

$ ./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
8 голосов
/ 12 февраля 2012

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

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

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

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

5 голосов
/ 12 февраля 2012

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

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

2 голосов
/ 29 апреля 2013

Также помните как вы загружаете процессор.Процессор может выполнять множество различных задач, и хотя многие из них будут отмечены как «загрузка процессора на 100%», каждый из них может использовать 100% различных частей процессора.Другими словами, очень сложно сравнивать два разных ЦП по производительности, особенно две разные архитектуры ЦП.Выполнение задачи A может отдать предпочтение одному ЦП над другим, тогда как при выполнении задачи B это может быть легко наоборот (так как два ЦП могут иметь разные ресурсы внутри и могут выполнять код очень по-разному).

Это причинаПрограммное обеспечение так же важно для обеспечения оптимальной работы компьютеров, как и оборудование.Это действительно очень верно и для «суперкомпьютеров».

Одним из показателей производительности процессора могут быть инструкции в секунду, но, опять же, инструкции не создаются равными на разных архитектурах процессора.Другой мерой может быть производительность кэширования ввода-вывода, но инфраструктура кэша также не одинакова.Тогда мерой может быть количество использованных инструкций на ватт, поскольку при проектировании кластерного компьютера подача и рассеяние энергии часто являются ограничивающим фактором.

Итак, ваш первый вопрос должен быть: Какой параметр производительности важен для вас?Что вы хотите измерить?Если вы хотите увидеть, какая машина получает больше всего FPS из Quake 4, ответ прост;ваша игровая установка будет работать, так как Cray вообще не может запустить эту программу; -)

Cheers, Steen

1 голос
/ 12 февраля 2018

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

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

Вот скриншот, когда работала версия OMP. enter image description here

В качестве альтернативы я использовал базовый поток POSIX, который можно запустить с помощью любого компилятора c, и увидел, что почти весь ЦП израсходовал , когда nos of thread = no of cores = 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);
}

Обратите внимание, как израсходован весь процессор - enter image description here

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

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

enter image description here

P.S. Увеличьте нагрузку на поток до 2,5 миллионов, чтобы увидеть загрузку процессора, поскольку она завершается менее чем за секунду.

0 голосов
/ 12 февраля 2018

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

0 голосов
/ 16 февраля 2012

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

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

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

0 голосов
/ 12 февраля 2012

Попробуйте распараллелить вашу программу, используя, например, OpenMP. Это очень простая и эффективная структура для создания параллельных программ.

...