Mam aplikację, w której dzielę obciążenie przetwarzania pomiędzy procesory GPU w systemie użytkownika. Zasadniczo istnieje wątek procesora na GPU, który inicjuje interwał przetwarzania GPU po uruchomieniu okresowym przez główny wątek aplikacji.Słaba wydajność podczas wywoływania cudaMalloc z dwoma procesorami graficznymi jednocześnie
Weź pod uwagę następujące zdjęcie (wygenerowane przy użyciu narzędzia profilującego CUDA NVIDIA) na przykładzie interwału przetwarzania GPU - tutaj aplikacja korzysta z pojedynczego procesora graficznego.
Jak widać duża część czasu przetwarzania GPU jest zużywana przez dwie operacje sortowania i używam biblioteki Thrust dla tego (oporowej :: sort_by_key). Poza tym wygląda na to, że ciąg :: sort_by_key wywołuje kilka cudaMallocs pod maską, zanim rozpocznie właściwy sort.
Rozważmy teraz ten sam przedział przetwarzanie gdzie aplikacja rozłożone obciążenie przetwarzania ponad dwa GPU:
W idealnym świecie można oczekiwać odstęp przetwarzania 2 GPU być dokładnie połowę z pojedyncze GPU (ponieważ każdy GPU wykonuje połowę pracy). Jak widać, nie dzieje się tak częściowo dlatego, że cudaMalloki wydają się trwać dłużej, gdy są wywoływane jednocześnie (czasami 2-3 razy dłużej) z powodu jakiegoś problemu z rywalizacją. Nie rozumiem, dlaczego tak musi być, ponieważ przestrzeń alokacji pamięci dla 2 procesorów graficznych jest całkowicie niezależna, więc nie powinno istnieć blokada na cały system cudaMalloc - blokada per-GPU byłaby bardziej uzasadniona.
Aby udowodnić moją hipotezę, że problem dotyczy jednoczesnych wywołań cudaMalloc, stworzyłem niewiarygodnie prosty program z dwoma wątkami CPU (dla każdego GPU), z których każdy wywołuje cudaMalloc kilka razy. Po raz pierwszy prowadził ten program tak, że oddzielne wątki nie nazywają cudaMalloc w tym samym czasie:
Widać to trwa ~ 175 mikrosekund na przydział. Następnie wpadłem program z nitki nazywając cudaMalloc jednocześnie:
Tutaj każde wywołanie trwało ~ 538 mikrosekund lub 3 razy dłuższe niż w poprzednim przypadku! Nie trzeba dodawać, że to bardzo spowalnia moją aplikację i jest oczywiste, że problem będzie tylko gorszy, gdy pojawi się więcej niż 2 GPU.
Zauważyłem to zachowanie w systemach Linux i Windows. W Linuksie używam sterownika Nvidia w wersji 319.60, a w Windows używam wersji 327.23. Używam zestawu narzędzi CUDA 5.5.
Możliwe przyczyny: Używam GTX 690 w tych testach. Ta karta to w zasadzie 2 680-podobne procesory graficzne umieszczone w tej samej jednostce. Jest to jedyna instalacja "multi-GPU", więc może problem z cudaMalloc ma związek z zależnościami sprzętowymi pomiędzy procesorami graficznymi 690?
Typowym zaleceniem dla kodu o wysokiej wydajności jest usunięcie operacji malloc z dowolnej pętli wydajności. Zdaję sobie sprawę, że to nie jest banalna sprawa, ponieważ używasz ciągu.Istnieją biblioteki sortowania o wysokiej wydajności, które mogą zastąpić ciąg sort_by_key, co pozwoli ci wykonać alokacje z wyprzedzeniem i ponownie użyć ich do operacji sortowania. [CUB] (http://nvlabs.github.io/cub/), [b40c] (http://code.google.com/p/back40computing/) i [MGPU] (http: //nvlabs.github .io/moderngpu /) to wszystkie możliwości. –
Tak, zajrzałem do CUB i b40c (strona b40c mówi, że projekt jest przestarzały). Zanim wykonam pracę, aby usunąć ciąg, chciałbym zobaczyć wykresy porównania między bibliotekami. Czy możesz wskazać mi jakieś numery wydajności? Którą bibliotekę polecasz? ... Wygląda na to, że ciąg nie jest bardzo wysoką wydajnością, na przykład, wyłączyłem już kilka wywołań pchnięć :: reduce i reduce_by_key z własnymi niestandardowymi jądrami - dzięki temu zmniejszyłem czas przetwarzania o połowę. Bez żartów. – rmccabe3701
Ciąg faktycznie jest oparty na konkretnym wariancie b40c (lub był używany). W przypadku równoważnych przypadków testowych nie było dużej różnicy w moich testach między b40c i MGPU. W jednym teście, który przeprowadziłem, sortowałem tylko około 22 bitów o wartości 32-bitowej. MGPU miał tarczę, którą mogłem skręcić, aby sortować tylko na 22bitach, i zaobserwowałem, że robi to z 40% przyspieszeniem nad ciągiem. Nie użyłem wiele CUB. Jeśli przeglądasz te linki, możesz znaleźć dane o skuteczności. Na przykład niektóre dane perfekcji MGPU [tutaj] (http://nvlabs.github.io/moderngpu/performance.html#performance) –