2015-01-12 14 views
5

Wyodrębniłem tę prostą funkcję członka z większego programu 2D, wszystko, co robi, to pętla for uzyskująca dostęp z trzech różnych tablic i wykonująca operację matematyczną (splot 1D). I zostały badania z użyciem OpenMP, aby ta szczególna funkcja szybciej:Dlaczego ta pętla nie działa szybciej przy użyciu OpenMP?

void Image::convolve_lines() 
{ 
    const int *ptr0 = tmp_bufs[0]; 
    const int *ptr1 = tmp_bufs[1]; 
    const int *ptr2 = tmp_bufs[2]; 
    const int width = Width; 
#pragma omp parallel for 
    for (int x = 0; x < width; ++x) 
    { 
    const int sum = 0 
     + 1 * ptr0[x] 
     + 2 * ptr1[x] 
     + 1 * ptr2[x]; 
    output[x] = sum; 
    } 
} 

Jeśli używam gcc 4.7 na debian/wheezy amd64 ogólna programowe wykonuje dużo wolniej na maszynie 8 procesorów. Jeśli używam gcc 4.9 na debian/jessie amd64 (tylko 4 procesory na tym komputerze) ogólny program działa z bardzo niewielką różnicą.

Korzystanie time porównać: pojedynczy bieg rdzeń:

$ ./test black.pgm out.pgm 94.28s user 6.20s system 84% cpu 1:58.56 total 

wielo prowadzony rdzeń:

$ ./test black.pgm out.pgm 400.49s user 6.73s system 344% cpu 1:58.31 total 

Gdzie:

$ head -3 black.pgm 
P5 
65536 65536 
255 

Więc Width jest ustawiony na 65536 w trakcie realizacji.

przypadku tej sprawy, używam cmake do kompilacji:

add_executable(test test.cxx) 
set_target_properties(test PROPERTIES COMPILE_FLAGS "-fopenmp" LINK_FLAGS "-fopenmp") 

I CMAKE_BUILD_TYPE jest ustawiony na:

CMAKE_BUILD_TYPE:STRING=Release 

co oznacza -O3 -DNDEBUG

Moje pytanie, dlaczego jest to for pętla nie szybciej przy użyciu wielordzeniowych? W macierzy nie ma nakładania się, openmp powinien równo dzielić pamięć. Nie wiem, skąd się bierze wąskie gardło?

EDIT: jak to pisano, zmieniłem plik wejściowy:

$ head -3 black2.pgm 
P5 
33554432 128 
255 

Więc Width jest teraz ustawiony na 33554432 trakcie realizacji (powinny być uważane za mało). Teraz czas ujawnia:

pojedynczy bieg rdzeń:

$ ./test ./black2.pgm out.pgm 100.55s user 5.77s system 83% cpu 2:06.86 total 

wielo prowadzony rdzeń (z jakiegoś powodu cpu% była zawsze poniżej 100%, które wskazują na brak tematów w ogóle):

$ ./test ./black2.pgm out.pgm 117.94s user 7.94s system 98% cpu 2:07.63 total 
+2

ogólnie, nieprawdziwe współdzielenie/zablokowanie rywalizacji. Jak duża jest "szerokość"? – sehe

+0

@ sorry, zapomniałem o tym wspomnieć. – malat

+0

Jak to przetestowałeś? Wątpię, żeby pojedyncza pętla 64k, którą dałeś, zabierała tyle czasu. – ElderBug

Odpowiedz

2

Mam kilka uwag ogólnych:

1. Przed zoptymalizowaniem kodu upewnij się, że dane są wyrównane do 16 bajtów. Jest to niezwykle ważne dla optymalizacji, którą chcesz zastosować. A jeśli dane są podzielone na 3 części, lepiej jest dodać kilka fałszywych elementów, aby adresy początkowe 3 elementów były wyrównane do 16 bajtów. W ten sposób procesor może łatwo załadować dane do linii pamięci podręcznej.

2. Upewnij się, że prosta funkcja jest wektoryzowana przed wdrożeniem openMP. Większość przypadków, przy użyciu zestawów instrukcji AVX/SSE, powinna zapewnić porządny ulepszenie pojedynczego wątku od 2 do 8X.W twoim przypadku jest to bardzo proste: utwórz stały rejestr mm256 i ustaw go na wartość 2, a następnie załaduj 8 liczb całkowitych do trzech rejestrów mm256. Dzięki procesorowi Haswell jeden dodatek i jedno mnożenie można wykonać razem. Teoretycznie, pętla powinna przyspieszyć o czynnik 12, jeśli rurociąg AVX może zostać wypełniony!

3. Czasami zrównoleglanie może obniżyć wydajność: Nowoczesny procesor potrzebuje kilkaset tysięcy cykli zegarowych na rozgrzewkę, wchodząc stany wysokiej wydajności i zwiększenia częstotliwości. Jeśli zadanie nie jest wystarczająco duże, jest bardzo prawdopodobne, że zadanie zostanie wykonane, zanim procesor się rozgrzeje i nie będzie można uzyskać zwiększenia prędkości przez równoległe działanie. I nie zapominaj, że openMP ma również narzut: tworzenie wątków, synchronizacja i usuwanie. Innym przykładem jest słabe zarządzanie pamięcią. Dostęp do danych jest tak rozproszony, że wszystkie rdzenie procesora są bezczynne i czekają na dane z pamięci RAM.

Moja sugestia:

Możecie spróbować intel MKL, nie wyważać otwartych drzwi. Biblioteka jest zoptymalizowana do ekstremum i nie ma zmarnowanego cyklu zegara. Można połączyć się z biblioteką szeregową lub wersją równoległą, gwarantowane jest zwiększenie prędkości, jeśli jest to możliwe poprzez równoległe działanie.

Powiązane problemy