2015-05-26 12 views
7

Próbuję pisać (aktualizować) w tablicy podwójnej w sposób równoległy za pomocą OpenMP. Chodzi o to, że element, który wymaga aktualizacji, może być aktualizowany więcej niż jeden raz, a sam element jest obliczany w locie. To czyni go bardzo podatnym na warunki wyścigowe, chyba że "zablokuję" obszar pamięci odpowiadający elementowi, który jest aktualizowany operacją atomową. W poniższym przykładzie:Aktualizowanie tablicy dublu z operacjami atomowymi

#include<omp.h> 
#include<stdlib.h> 

int main() 
{ 

    double *x=NULL, contribution = 1234.5; 
    size_t xlen=1000, i, n; 

    if ((x = (double *) calloc(xlen,sizeof(double))) == NULL) exit(-1) 

    #pragma omp parallel for shared(x) private(n,contribution) 
    for (i=0; i<xlen; i++) { 
     n = find_n_on_the_fly(i); 

     #pragma omp atomic update 
     x[n]+=contribution; // in the more complicated case contributions are different 

    } 

    return 0; 
} 

Z takim podejściem wciąż jestem w wyścigu. Próbowałem używać krytycznych sekcji, ale całkowicie mnie to zabija, ponieważ tablice są duże, a liczba aktualizacji jest również duża.

Pytanie: co jest nie tak z tym podejściem? Czy istnieje lepszy sposób radzenia sobie z tym?

Uwaga: Aby sobie z tym poradzić, robię coś głupiego, tworząc kopie tablic dla każdego wątku i zmniejszając je później. Ale ograniczenia pamięci nie pozwalają mi iść dalej.

+1

dlaczego nie utworzyć listę par "n"/"wkład" i kontynuować wciskanie w nią, a następnie zredukować je do tablicy "x" lub innego rodzaju rzadkiej reprezentacji? – user3528438

+0

@ user3528438 To jest pomysł i jest zgodny z moją notatką. Czy masz rzeczywisty przykład pracy, który nie ogranicza się do tego, co już robię? –

+0

Z definicji operacja atomowa dla wartości 64-bitowej jest możliwa tylko w 64-bitowej (lub większej) architekturze sprzętowej. Biblioteka zmiennoprzecinkowa może znajdować się w katalogu głównym problemu, który tu widzisz. Czy próbowałeś typowania w "unsigned long long"? Ponadto tablica asocjacyjna byłaby dużo szybsza do wyszukiwania elementów tablicy, niż do przeszukiwania pętli dla każdego elementu. –

Odpowiedz

0

Przedmowie to poprzedzę słowami: "Jestem nowy w dziedzinie przetwarzania równoległego", co może nie być opłacalne w zależności od przypadku użycia.

Jednym z pomysłów, który może być przydatny, jest oddzielenie aspektów obliczeniowych i aktualizacyjnych programu. Ustanowienie procesu nadrzędnego/kontrolnego/wątku, który ma wyłączny dostęp do tablicy i wykonuje aktualizacje w sposób podobny do kolejki na podstawie informacji zebranych z procesów/wątków podrzędnych/obliczeniowych. Może to służyć zminimalizowaniu potrzeby blokowania w tradycyjnym sensie.

Mam nadzieję, że przynajmniej zapewni inny sposób patrzenia na problem.

1

Dla mnie powyższy kod wydaje się być skuteczny.

jednak, masz dwie opcje, aby ją poprawić:

1- użyciu funkcji alokacji pamięci o nazwie _mm_malloc z rozmiaru linii pamięci podręcznej jako jeden z wejścia zamiast calloc które zostało użyte. Największą rzeczą, z którą teraz się spotykasz, jest Fałszywe Udostępnianie. Aby pominąć efekty FS, używając powyższej metody, zasadniczo zmuszasz bazową bibliotekę do alokacji pamięci (lub twojej tablicy) w taki sposób, aby każdy element znajdował się w wierszu pamięci podręcznej. W ten sposób wątki nie będą walczyć o linię pamięci podręcznej, aby pobrać dwie różne zmienne wspólne. Innymi słowy, uniemożliwi przydział dwóch zmiennych w linii pamięci podręcznej. To zwiększy wydajność w wielowątkowym programie. Google _mm_malloc, aby uzyskać więcej informacji. Jednak poniższe są podstawowe użycie. W większości nowoczesnych komputerów, linia pamięci podręcznej ma wartość 64.

#if defined(__INTEL_COMPILER) 
#include <malloc.h> 
#else 
#include <mm_malloc.h> 
#endif 

int CPU_Cache_line_size = 64; 

int xlen = 100; 
double *ptr = _mm_malloc(xlen*sizeof(double), CPU_Cache_line_size); 
/* 
    do something ... 
*/ 
_mm_free(ptr); 

__CPU_CACHE_LINE_SIZE__ można przeszukiwać dla swojego systemu w następujący sposób: komenda

  • Linux:

    getconf LEVEL1_DCACHE_LINESIZE

  • Programowo:

    int CPU_Cache_line_size = sysconf (_SC_LEVEL1_DCACHE_LINESIZE);

  • Szczegółowe informacje dotyczące pamięci podręcznej:

    /sys/devices/system/cpu/cpu0/cache/

2- Wspomniałeś ten sam ale ograniczenia są dotkliwe dla danej sytuacji: przy użyciu tablicy na wątek, a następnie zmniejszyć je. Takie podejście jest bardziej wydajne. Zastanów się, czy użyć tego podejścia, jeśli możesz.

+0

Zwróć uwagę, że '_mm_malloc' oraz' _mm_free' są poprawne tylko w przypadku kompilatorów Intel 'icc' lub' icpc'. Rozważ skorzystanie z innych opcji, jeśli tworzysz program za pomocą innego. Alternatywą POSIX dla wyrównanego przydziału jest 'posix_memalign': https://linux.die.net/man/3/posix_memalign . Standardową wersją C11 jest 'aligned_alloc' (http://en.cppreference.com/w/c/memory/aligned_alloc). –

Powiązane problemy