Это довольно интересный вопрос, поэтому позвольте мне задать сцену. Я работаю в Национальном музее вычислительной техники, и нам только что удалось получить суперкомпьютер 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 (Cray работает под UNICOS), работало отлично, получая 100% загрузку ЦП и занимая около 10 минут. Вернувшись домой, я решил попробовать его на своем современном игровом ПК с шестиядерным процессором, и именно здесь мы столкнулись с нашими первыми проблемами.
Сначала я адаптировал код для работы в Windows, так как это то, что использовал игровой ПК, но был опечален, обнаружив, что процесс потреблял только около 15% мощности процессора. Я решил, что Windows должна быть Windows, поэтому я загрузился в Live CD с Ubuntu, думая, что Ubuntu позволит процессу работать с его полным потенциалом, как это было раньше на моем ноутбуке.
Однако я получил только 5% использования! Итак, мой вопрос: как я могу адаптировать программу для работы на моей игровой машине в Windows 7 или Live Linux при 100% загрузке процессора? Еще одна вещь, которая была бы замечательной, но не обязательной, - это если бы конечным продуктом мог быть один .exe, который можно было бы легко распространять и запускать на машинах Windows.
Большое спасибо!
PS Конечно, эта программа на самом деле не работала со специализированными процессорами Crays 8, и это уже совсем другая проблема ... Если вы что-нибудь знаете об оптимизации кода для работы на суперкомпьютерах Cray 90-х, сообщите нам тоже!
Ответы:
Если вам нужен 100% процессор, вам нужно использовать более одного ядра. Для этого вам понадобится несколько потоков.
Вот параллельная версия с использованием 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; }
Вывод:
Вот ваш 100% процессор:
источник
Вы запускаете один процесс на многоядерной машине, поэтому он работает только на одном ядре.
Решение достаточно простое, поскольку вы просто пытаетесь привязать процессор - если у вас 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
источник
UNICOS
похоже, он чем-то похож на Unix (Википедия все равно так думает), так что, вероятно, так оно и былоfork()
. Думаю, тебе стоит научиться этим пользоваться.Ваш алгоритм генерации простых чисел очень неэффективен. Сравните это с Primegen, который генерирует простые числа от 50847534 до 1000000000 всего за 8 секунд на Pentium II-350.
Чтобы легко использовать все процессоры, вы можете решить неприятно параллельную задачу, например, вычислить множество Мандельброта или использовать генетическое программирование, чтобы нарисовать Мона Лизу в нескольких потоках (процессах).
Другой подход - взять существующую программу тестирования производительности суперкомпьютера Cray и перенести ее на современный ПК.
источник
Причина, по которой вы получаете 15% на шестиядерном процессоре, заключается в том, что ваш код использует 1 ядро на 100%. 100/6 = 16,67%, что при использовании скользящего среднего с планированием процессов (ваш процесс будет работать с нормальным приоритетом) можно легко сообщить как 15%.
Следовательно, чтобы использовать 100% ЦП, вам нужно будет использовать все ядра вашего ЦП - запустить 6 параллельных путей выполнения кода для шестиядерного ЦП и иметь этот масштаб вплоть до того количества процессоров, которое есть на вашей машине Cray :)
источник
Также будьте очень внимательны как вы загружаете процессор. ЦП может выполнять множество различных задач, и хотя о многих из них будет сообщаться как «загрузка ЦП на 100%», каждая из них может использовать 100% различных частей ЦП. Другими словами, очень сложно сравнивать два разных процессора по производительности, и особенно две разные архитектуры процессоров. При выполнении задачи A один ЦП может быть предпочтительнее другого, в то время как при выполнении задачи Б может быть легко наоборот (поскольку два ЦП могут иметь разные внутренние ресурсы и могут выполнять код по-разному).
По этой причине программное обеспечение так же важно для обеспечения оптимальной работы компьютеров, как и оборудование. Это действительно верно и для «суперкомпьютеров».
Одним из показателей производительности ЦП может быть количество инструкций в секунду, но опять же, инструкции не одинаковы для разных архитектур ЦП. Другой мерой может быть производительность ввода-вывода кеша, но инфраструктура кеширования также не одинакова. Тогда мерой может быть количество инструкций на используемый ватт, поскольку подача и рассеяние мощности часто являются ограничивающим фактором при проектировании кластерного компьютера.
Итак, ваш первый вопрос должен быть таким: какой параметр производительности важен для вас? Что вы хотите измерить? Если вы хотите узнать, какая машина показывает наибольшее количество кадров в секунду в Quake 4, ответ прост; ваша игровая установка будет, поскольку Cray вообще не может запускать эту программу ;-)
Ура, Стин
источник
TL; DR; Принятый ответ одновременно неэффективен и несовместим. Следующие алгоритмы работают в 100 раз быстрее.
Компилятор gcc, доступный на MAC, не может работать
omp
. Пришлось установить llvm(brew install llvm )
. Но я не видел, чтобы простоя процессора снижалась при работе версии OMP.Вот снимок экрана во время работы версии OMP.
В качестве альтернативы я использовал базовый поток POSIX, который можно запустить с помощью любого компилятора c, и увидел, что почти весь ЦП израсходован при
nos of thread
=no of cores
= 4 (MacBook Pro, Intel Core i5 2,3 ГГц). Вот программа -#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 - Если вы увеличиваете количество потоков, фактическое использование ЦП снижается (попробуйте сделать no of thread = 20), поскольку система использует больше времени на переключение контекста, чем на фактические вычисления.
Кстати, моя машина не такая мощная, как @mystical (принятый ответ). Но моя версия с базовым потоком POSIX работает намного быстрее, чем версия OMP. Вот результат -
PS Увеличьте нагрузку на поток до 2,5 миллионов, чтобы увидеть использование ЦП, поскольку процесс завершается менее чем за секунду.
источник
Попробуйте распараллелить вашу программу, например, с помощью OpenMP. Это очень простой и эффективный фреймворк для создания параллельных программ.
источник
Чтобы быстро улучшить работу одного ядра, удалите системные вызовы, чтобы уменьшить переключение контекста. Удалите эти строки:
system("clear"); printf("%d prime numbers calculated\n",primes);
Первый особенно плох, так как он будет порождать новый процесс на каждой итерации.
источник
Просто попробуйте заархивировать и разархивировать большой файл, ничего, поскольку тяжелые операции ввода-вывода не могут использовать процессор.
источник