Jak duży jest n? Domyślne harmonogramowanie dla dyrektywy OpenMP parallel for
jest zależne od implementacji. Wygląda na to, że w GOMP (implementacja OpenMP używana przez gcc) domyślnie jest to (dynamic,1)
zgodnie z documentation here. Oznacza to, że każdy wątek uzyskuje dostęp (w lokalizacji i-1
i i+1
) do pamięci, które są ładowane przez sąsiednie wątki, co może prowadzić do złego wykorzystania pamięci podręcznej. W nowoczesnych architekturach procesorów takich operacje wzorcowania są często związane z pamięcią i są wrażliwe na buforowanie. Można spróbować określający harmonogram z większymi kawałkami, na przykład z:
#pragma omp parallel for schedule(dynamic,1024)
Ja tylko używając 1024 tutaj jako przykład. W praktyce powinieneś poeksperymentować, aby znaleźć optymalny współczynnik porcji (lub systematyczne wyszukiwanie za pomocą przeciągnięcia parametru, proces często nazywany "auto-tuningiem"). Lub możesz wybrać wartość opartą więcej w teorii, na przykład przez wyprowadzenie go z rozmiaru pamięci podręcznej L1 lub L2 procesora.
Albo możesz zamiast tego spróbować statycznego planowania, ponieważ ilość obliczeń wewnątrz pętli for jest jednakowa dla wątków, a narzuty dynamicznego programu szeregującego mogą powodować wąskie gardło. Jeśli podasz bez rozmiaru porcji, określony zostanie jeden wątek, zostanie mu przypisany pojedynczy fragment o mniej więcej tym samym rozmiarze.
Wreszcie możesz również przypiąć wątki OpenMP do własnych rdzeni procesora. Możesz to zrobić, używając zmiennej środowiskowej GOMP_CPU_AFFINITY.
Edit:
właśnie gry z następującym programem testowym skompilowane z gcc 4.2.1, i myślę, że dokumentacja związana powyżej jest nieprawidłowy. Wygląda na to, że GOMP domyślnie przyjmuje wartość schedule(static)
.
#include <stdio.h>
#include <omp.h>
int main(int argc, char** argv)
{
int i;
#pragma omp parallel for
for (i=0; i<15; i++) {
int id = omp_get_thread_num();
printf("%d assigned to thread %d\n", i, id);
}
}
a wyjście z dwóch nitek jest:
$ ./test_sched | sort -n
0 assigned to thread 0
1 assigned to thread 0
2 assigned to thread 0
3 assigned to thread 0
4 assigned to thread 0
5 assigned to thread 0
6 assigned to thread 0
7 assigned to thread 0
8 assigned to thread 1
9 assigned to thread 1
10 assigned to thread 1
11 assigned to thread 1
12 assigned to thread 1
13 assigned to thread 1
14 assigned to thread 1
kompiluje i uruchomić grzywny z GCC 4.6.1. Którego kompilatora używasz? –
Czy możesz dodać trochę szczegółów na temat kompilatora i systemu operacyjnego? – talonmies
gcc 4.2.1 na systemie Snow Leopard MacOS, dokładnie: 686-apple-darwin10-g ++ - 4.2.1 (GCC) 4.2.1 (Apple Inc. build 5666) (kropka 3) – Chris