Przeglądając odpowiedzi i komentarze na pytania CUDA oraz na wiki tagu CUDA , często widzę, że często zaleca się sprawdzanie, czy w statusie zwrotu każdego wywołania API nie ma błędów. Dokumentacja API zawiera funkcje takie jak cudaGetLastError
, cudaPeekAtLastError
i cudaGetErrorString
, ale jaki jest najlepszy sposób, aby je połączyć, aby niezawodnie wychwytywać i zgłaszać błędy bez konieczności posiadania dodatkowego kodu?
cuda
error-checking
szpony
źródło
źródło
getLastCudaError
icheckCudaErrors
które wykonują prawie wszystko, co opisano w zaakceptowanej odpowiedzi . Zobacz próbki do demonstracji. Wystarczy zainstalować próbki wraz z zestawem narzędzi, a będziesz go mieć.Odpowiedzi:
Prawdopodobnie najlepszym sposobem sprawdzenia błędów w kodzie API środowiska wykonawczego jest zdefiniowanie funkcji obsługi stylu assert i makra opakowania w następujący sposób:
Następnie można zawinąć każde wywołanie interfejsu API za pomocą
gpuErrchk
makra, które przetworzy status powrotu zawiniętego wywołania interfejsu API, na przykład:Jeśli wystąpi błąd w wywołaniu, zostanie wysłany komunikat tekstowy opisujący błąd oraz plik i wiersz w kodzie, w którym wystąpił błąd,
stderr
i aplikacja zostanie zamknięta. Można sobie wyobrazić modyfikację,gpuAssert
aby zgłosić wyjątek, zamiast wywoływaćexit()
bardziej wyrafinowane aplikacje, jeśli byłyby one wymagane.Drugim powiązanym pytaniem jest, jak sprawdzić błędy w uruchomieniach jądra, których nie można bezpośrednio zawrzeć w wywołaniu makra, takim jak standardowe wywołania API środowiska wykonawczego. W przypadku jąder coś takiego:
najpierw sprawdzi, czy argument uruchamiania jest nieprawidłowy, a następnie zmusi host do czekania, aż jądro się zatrzyma i sprawdzi błąd wykonania. Synchronizację można wyeliminować, jeśli wystąpi kolejne blokujące wywołanie interfejsu API:
w takim przypadku
cudaMemcpy
wywołanie może zwrócić albo błędy, które wystąpiły podczas wykonywania jądra, albo błędy z samej kopii pamięci. Może to być mylące dla początkujących i zaleciłbym użycie jawnej synchronizacji po uruchomieniu jądra podczas debugowania, aby łatwiej zrozumieć, gdzie mogą wystąpić problemy.Należy pamiętać, że podczas korzystania z równoległego dynamicznego interfejsu CUDA bardzo podobna metodologia może i powinna być stosowana do każdego użycia interfejsu API środowiska wykonawczego CUDA w jądrach urządzeń, a także po uruchomieniu dowolnego jądra urządzenia:
źródło
cudaDeviceReset()
przed wyjściem? I klauzula dotycząca zwolnienia pamięci?Powyższa odpowiedź talonmies to świetny sposób na przerwanie aplikacji w
assert
stylu.Czasami możemy chcieć zgłosić błąd i usunąć go w kontekście C ++ jako część większej aplikacji.
Oto rozsądnie zwięzły sposób, aby to zrobić, zgłaszając wyjątek C ++ wynikający z
std::runtime_error
użyciathrust::system_error
:Spowoduje to włączenie nazwy pliku, numeru wiersza i opisu w języku angielskim
cudaError_t
elementu zgłoszonego wyjątku.what()
:Wyjście:
Klient
some_function
może w razie potrzeby odróżnić błędy CUDA od innych rodzajów błędów:Ponieważ
thrust::system_error
jest tostd::runtime_error
, możemy alternatywnie obsłużyć to w ten sam sposób, co szeroką klasę błędów, jeśli nie wymagamy precyzji z poprzedniego przykładu:źródło
<thrust/system/cuda_error.h>
jest teraz skutecznie<thrust/system/cuda/error.h>
.C ++ - kanoniczny sposób: nie sprawdzaj błędów ... użyj powiązań C ++, które generują wyjątki.
Byłem zirytowany tym problemem; i kiedyś miałem rozwiązanie z funkcją makro-cum-wrapper, tak jak w Talonmies i odpowiedziach Jareda, ale szczerze? To sprawia, że korzystanie z interfejsu API środowiska wykonawczego CUDA jest jeszcze bardziej brzydkie i podobne do C.
Podszedłem więc do tego w inny i bardziej fundamentalny sposób. Oto przykładowy
vectorAdd
przykład CUDA - z pełnym sprawdzaniem błędów każdego wywołania API środowiska wykonawczego:Ponownie - wszystkie potencjalne błędy są sprawdzane, a wyjątek występuje, jeśli wystąpił błąd (zastrzeżenie: jeśli jądro spowodowało jakiś błąd po uruchomieniu, zostanie przechwycone po próbie skopiowania wyniku, a nie wcześniej; aby upewnić się, że jądro zakończyło się powodzeniem, zrobiłbyś to trzeba sprawdzić pod kątem błędu między uruchomieniem a kopią za pomocą
cuda::outstanding_error::ensure_none()
polecenia).Powyższy kod używa mojego
Cienkie opakowania Modern-C ++ dla biblioteki API środowiska wykonawczego CUDA (Github)
Należy zauważyć, że wyjątki zawierają zarówno wyjaśnienie ciągu, jak i kod stanu środowiska wykonawczego CUDA po nieudanym wywołaniu.
Kilka linków do automatycznego sprawdzania błędów CUDA za pomocą tych opakowań:
źródło
Omówione tutaj rozwiązanie działało dla mnie dobrze. To rozwiązanie wykorzystuje wbudowane funkcje cuda i jest bardzo łatwe do wdrożenia.
Odpowiedni kod jest kopiowany poniżej:
źródło