To dość interesujące pytanie, więc pozwól mi ustawić odpowiednią scenę. Pracuję w National Museum of Computing i właśnie udało nam się uruchomić super komputer Cray Y-MP EL z 1992 roku i naprawdę chcemy zobaczyć, jak szybko może działać!
Zdecydowaliśmy, że najlepszym sposobem na zrobienie tego jest napisanie prostego programu w C, który obliczałby liczby pierwsze i pokazywałby, ile czasu to zajęło, a następnie uruchomienie programu na szybkim, nowoczesnym komputerze stacjonarnym i porównanie wyników.
Szybko wymyśliliśmy ten kod, aby policzyć liczby pierwsze:
#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);
}
Który na naszym dwurdzeniowym laptopie z systemem Ubuntu (Cray działa z UNICOS) działał idealnie, uzyskując 100% wykorzystanie procesora i zajmując około 10 minut. Kiedy wróciłem do domu, zdecydowałem się wypróbować go na moim nowoczesnym komputerze do gier z sześciordzeniowym rdzeniem i tutaj pojawiają się nasze pierwsze problemy.
Po raz pierwszy dostosowałem kod do uruchamiania w systemie Windows, ponieważ właśnie tego używał komputer do gier, ale ze smutkiem stwierdziłem, że proces pobierał tylko około 15% mocy procesora. Pomyślałem, że to musi być Windows to Windows, więc uruchomiłem Live CD Ubuntu, myśląc, że Ubuntu pozwoli na uruchomienie procesu z pełnym potencjałem, tak jak to miało miejsce wcześniej na moim laptopie.
Jednak mam tylko 5% wykorzystania! Więc moje pytanie brzmi: jak mogę dostosować program do działania na moim automacie do gier w systemie Windows 7 lub Linux na żywo przy 100% wykorzystaniu procesora? Inną rzeczą, która byłaby świetna, ale niekonieczna, jest to, że produktem końcowym może być jeden plik .exe, który można łatwo dystrybuować i uruchamiać na komputerach z systemem Windows.
Wielkie dzięki!
PS Oczywiście ten program tak naprawdę nie działał ze specjalistycznymi procesorami Crays 8 i to jest zupełnie inna kwestia… Jeśli wiesz cokolwiek o optymalizacji kodu do pracy na super komputerach Cray z lat 90-tych, daj nam znać!
Odpowiedzi:
Jeśli chcesz mieć 100% procesor, musisz użyć więcej niż 1 rdzenia. Aby to zrobić, potrzebujesz wielu wątków.
Oto wersja równoległa korzystająca z OpenMP:
Musiałem zwiększyć limit, aby
1000000
na moim komputerze zajęło to więcej niż 1 sekundę.#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; }
Wynik:
Oto Twój 100% procesor:
źródło
Uruchamiasz jeden proces na maszynie wielordzeniowej - więc działa tylko na jednym rdzeniu.
Rozwiązanie jest dość łatwe, ponieważ po prostu próbujesz przypiąć procesor - jeśli masz N rdzeni, uruchom program N razy (oczywiście równolegle).
Przykład
Oto kod, który uruchamia program
NUM_OF_CORES
równolegle. Jest to kod POSIXy - używafork
- więc powinieneś go uruchomić pod Linuksem. Jeśli to, co czytam o Cray, jest poprawne, może być łatwiej przenieść ten kod niż kod OpenMP w drugiej odpowiedzi.#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; }
Wynik
$ ./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
źródło
UNICOS
wygląda na to, że jest trochę podobny do Uniksa (Wikipedia tak uważa), więc prawdopodobnie takfork()
. Myślę, że powinieneś się nauczyć, jak tego używać.Twój algorytm generowania liczb pierwszych jest bardzo nieefektywny. Porównaj to z primegenem, który generuje 50847534 liczby pierwsze do 1000000000 w zaledwie 8 sekund na Pentium II-350.
Aby łatwo zużyć wszystkie procesory, możesz rozwiązać kłopotliwie równoległy problem, np. Obliczyć zbiór Mandelbrota lub użyć programowania genetycznego, aby pomalować Mona Lisę w wielu wątkach (procesach).
Innym podejściem jest pobranie istniejącego programu porównawczego dla superkomputera Cray i przeniesienie go na nowoczesny komputer PC.
źródło
Powodem, dla którego otrzymujesz 15% na szesnastkowym procesorze rdzeniowym, jest to, że twój kod wykorzystuje 1 rdzeń w 100%. 100/6 = 16,67%, co przy użyciu średniej ruchomej z harmonogramowaniem procesu (proces działałby z normalnym priorytetem) można łatwo określić jako 15%.
Dlatego, aby użyć 100% procesora, musiałbyś użyć wszystkich rdzeni swojego procesora - uruchom 6 równoległych ścieżek kodu wykonawczego dla procesora z rdzeniem szesnastkowym i uzyskaj tę skalę aż do liczby procesorów, które ma twoja maszyna Cray :)
źródło
Bądź również bardzo świadomy sposobu ładowania procesora. Procesor może wykonywać wiele różnych zadań i chociaż wiele z nich jest zgłaszanych jako „ładowanie procesora w 100%”, każdy z nich może wykorzystywać w 100% różne części procesora. Innymi słowy, bardzo trudno jest porównać dwa różne procesory pod kątem wydajności, a zwłaszcza dwie różne architektury procesorów. Wykonanie zadania A może faworyzować jeden procesor nad innym, podczas wykonywania zadania B może być łatwo odwrotnie (ponieważ dwa procesory mogą mieć różne zasoby wewnętrzne i mogą wykonywać kod w bardzo różny sposób).
To jest powód, dla którego oprogramowanie jest tak samo ważne dla optymalnego działania komputerów, jak sprzęt. Jest to rzeczywiście bardzo prawdziwe również w przypadku „superkomputerów”.
Jedną miarą wydajności procesora mogą być instrukcje na sekundę, ale z drugiej strony instrukcje nie są tworzone jednakowo na różnych architekturach procesora. Inną miarą może być wydajność pamięci podręcznej we / wy, ale infrastruktura pamięci podręcznej też nie jest równa. Wtedy miarą mogłaby być liczba instrukcji przypadających na jeden zużywany wat, ponieważ dostarczanie i rozpraszanie mocy jest często czynnikiem ograniczającym podczas projektowania komputera klastrowego.
Twoje pierwsze pytanie powinno więc brzmieć: który parametr wydajności jest dla Ciebie ważny? Co chcesz zmierzyć? Jeśli chcesz zobaczyć, która maszyna uzyskuje najwięcej FPS w Quake 4, odpowiedź jest prosta; Twój sprzęt do gier to zrobi, ponieważ Cray nie może w ogóle uruchomić tego programu ;-)
Pozdrawiam, Steen
źródło
TLDR; Przyjęta odpowiedź jest zarówno nieefektywna, jak i niekompatybilna. Podążanie za algo działa 100x szybciej.
Nie można uruchomić kompilatora gcc dostępnego na MAC
omp
. Musiałem zainstalować llvm(brew install llvm )
. Ale nie widziałem, aby bezczynność procesora spadała podczas uruchamiania wersji OMP.Oto zrzut ekranu podczas działania wersji OMP.
Alternatywnie, użyłem podstawowego wątku POSIX, który można uruchomić za pomocą dowolnego kompilatora c i zobaczyłem prawie cały procesor zużyty, gdy
nos of thread
=no of cores
= 4 (MacBook Pro, Intel Core i5 2,3 GHz). Oto program -#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); }
Zwróć uwagę, jak zużywa się cały procesor -
PS - Jeśli zwiększysz liczbę wątków, rzeczywiste użycie procesora spadnie (spróbuj utworzyć liczbę wątków = 20), ponieważ system zużywa więcej czasu na przełączanie kontekstu niż rzeczywiste przetwarzanie.
Nawiasem mówiąc, moja maszyna nie jest tak mocna jak @mystical (zaakceptowana odpowiedź). Ale moja wersja z podstawową obsługą wątków POSIX działa znacznie szybciej niż wersja OMP. Oto wynik -
PS Zwiększ obciążenie wątków do 2,5 miliona, aby zobaczyć zużycie procesora, ponieważ trwa ono krócej niż sekundę.
źródło
Spróbuj zsynchronizować swój program używając np. OpenMP. Jest to bardzo proste i skuteczne ramy tworzenia programów równoległych.
źródło
Aby szybko ulepszyć jeden rdzeń, usuń wywołania systemowe, aby ograniczyć przełączanie kontekstów. Usuń te linie:
system("clear"); printf("%d prime numbers calculated\n",primes);
Pierwsza jest szczególnie zła, ponieważ w każdej iteracji będzie generować nowy proces.
źródło
Po prostu spróbuj spakować i rozpakować duży plik, nic tak jak ciężkie operacje we / wy nie mogą używać procesora.
źródło