Zapisz datę! Google I / O powraca w dniach 18-20 maja Zarejestruj się teraz
Ta strona została przetłumaczona przez Cloud Translation API.
Switch to English

Zoptymalizuj wydajność GPU TensorFlow za pomocą TensorFlow Profiler

Przegląd

Ten przewodnik jest przeznaczony dla użytkowników TensorFlow korzystających z procesorów graficznych w celu poprawy wydajności modelu. Korzystając z TensorFlow Profiler jako głównego narzędzia do uzyskiwania wglądu w wydajność, ten przewodnik pomoże Ci debugować, gdy co najmniej jeden z procesorów graficznych jest niewykorzystany. Przewodnik Szybki start dotyczący TensorFlow Profiler można znaleźć w samouczku TensorFlow Profiler , a dodatkowe sposoby uzyskiwania profilu są udokumentowane w Optymalizacji wydajności TensorFlow przy użyciu przewodnika Profiler .

Należy pamiętać, że odciążanie obliczeń na GPU może nie zawsze być korzystne, szczególnie w przypadku małych modeli. Występują narzuty związane z transferem danych między hostem (CPU) a urządzeniem (GPU), a także narzuty związane z opóźnieniem związanym z uruchomieniem jądra GPU przez hosta. Dobra wydajność jest osiągana, gdy host skutecznie utrzymuje zajęcie GPU przez odciążenie wystarczającej ilości pracy.

Przepływ pracy optymalizacji wydajności

W tym przewodniku opisano, jak debugować problemy z wydajnością, zaczynając od jednego GPU, a następnie przechodząc do jednego hosta z wieloma procesorami GPU. Zaleca się debugowanie problemów z wydajnością w tej kolejności. Na przykład, jeśli używasz strategii dystrybucji TensorFlow do trenowania modelu na jednym hoście z wieloma GPU i zauważysz nieoptymalne wykorzystanie GPU, powinieneś najpierw zoptymalizować i debugować wydajność dla 1 GPU, przed debugowaniem systemu z wieloma GPU. Zalecana kolejność jest następująca:

  1. Optymalizuj i debuguj wydajność na 1 GPU
    1. Sprawdź, czy potok wejściowy jest wąskim gardłem
    2. Wydajność debugowania 1 GPU
    3. Włącz fp16 i opcjonalnie włącz XLA
  2. Optymalizuj i debuguj wydajność na jednym hoście z wieloma GPU

Jako podstawa do uzyskania wydajnego kodu na GPU, ten przewodnik zakłada, że ​​używasz już tf.function . Interfejs API kompilacji / dopasowania Keras automatycznie wykorzysta tf.function Pod maską. Pisząc niestandardową pętlę treningową, zapoznaj się z tym przewodnikiem, jak włączyć tf.function .

W następnych sekcjach omówiono sugerowane podejścia dla każdego z powyższych scenariuszy, aby pomóc zidentyfikować i naprawić wąskie gardła wydajności.

Zoptymalizuj wydajność na 1 GPU

W idealnym przypadku Twój program powinien mieć wysokie wykorzystanie procesora graficznego, minimalną komunikację między procesorem (hostem) a GPU (urządzeniem) oraz brak narzutu z potoku wejściowego. Pierwszym krokiem do analizy wydajności jest uzyskanie profilu modelu działającego z jednym GPU.

Strona przeglądu TensorFlow Profiler zawiera wyobrażenie o tym, jak daleko Twój program jest od idealnego scenariusza.

TensorFlow Profiler Overview Page

Kluczowe liczby, na które należy zwrócić uwagę na stronie przeglądu, to:

  1. Ile czasu kroku pochodzi od rzeczywistego wykonania urządzenia
  2. Odsetek operacji umieszczonych na urządzeniu w porównaniu z hostem
  3. Ile jąder używa fp16

Osiągnięcie optymalnej wydajności oznacza maksymalizację tych liczb we wszystkich trzech przypadkach. Aby uzyskać dogłębne zrozumienie programu, należy zapoznać się z przeglądarką śledzenia TensorFlow Profiler. W poniższych sekcjach przedstawiono niektóre typowe wzorce przeglądarki danych śledzenia, na które należy zwrócić uwagę podczas diagnozowania wąskich gardeł wydajności.

Poniżej znajduje się obraz widoku śledzenia modelu działającego na 1 GPU. W sekcjach Zakres nazwy Tensorflow i Operacje Tensorflow można zidentyfikować różne części modelu, takie jak przejście do przodu, funkcja straty, przejście do tyłu / obliczenie gradientu oraz aktualizacja wagi optymalizatora. Możesz również zobaczyć operacje wykonywane na GPU obok każdego strumienia , które odnoszą się do strumieni CUDA. Każdy strumień jest używany do określonych zadań. W tym śladzie strumień 118 jest używany do uruchamiania jąder obliczeniowych i kopii z urządzenia na urządzenie. Strumień nr 119 jest używany do kopiowania z hosta do urządzenia, a strumień nr 120 do kopiowania z urządzenia do hosta.

image

Ten ślad pokazuje wspólne cechy wydajnego modelu. Na przykład oś czasu obliczeń GPU ( strumień 118 ) wygląda na zajętą ​​i zawiera bardzo niewiele luk. Istnieje minimalna liczba kopii z hosta na urządzenie ( strumień nr 119 ) i z urządzenia na host ( strumień nr 120 ), a także minimalne przerwy między krokami. Po uruchomieniu programu TensorFlow Profiler w swoim programie te idealne cechy mogą nie być widoczne w widoku śledzenia. W pozostałej części tego przewodnika omówiono typowe scenariusze i sposoby ich rozwiązywania.

Potok wejściowy debugowania

Pierwszym krokiem w debugowaniu wydajności GPU jest określenie, czy program jest powiązany z danymi wejściowymi. Najłatwiejszym sposobem rozwiązania tego problemu jest użycie analizatora potoku wejściowego TensorFlow Profiler, który zapewnia przegląd czasu spędzonego w potoku wejściowym.

image

Poniżej przedstawiono potencjalne działania, które możesz podjąć, jeśli Twój potok wejściowy znacząco wpływa na czas kroku:

  • Zapoznaj się z przewodnikiem dotyczącym tf.data aby dowiedzieć się, jak debugować potok wejściowy.
  • Innym szybkim sposobem sprawdzenia, czy potok wejściowy jest wąskim gardłem, jest użycie losowo generowanych danych wejściowych, które nie wymagają żadnego przetwarzania wstępnego. Oto przykład użycia tej techniki w modelu ResNet. Jeśli potok wejściowy jest optymalny, powinieneś zobaczyć podobną wydajność z danymi rzeczywistymi i wygenerowanymi danymi losowymi / syntetycznymi. Jedyny narzut w przypadku danych syntetycznych będzie wynikał z kopiowania danych wejściowych, które ponownie można wstępnie pobrać i zoptymalizować.

Wydajność debugowania 1 GPU

Istnieje kilka czynników, które mogą przyczynić się do niskiego wykorzystania procesora graficznego. Poniżej przedstawiono kilka scenariuszy często obserwowanych podczas przeglądania przeglądarki danych śledzenia i potencjalnych rozwiązań.

Przeanalizuj luki między krokami

Częstą obserwacją, gdy program nie działa optymalnie, są przerwy między etapami treningu. Na poniższym obrazku jest duża przerwa między krokami 8 i 9, co oznacza, że ​​w tym czasie procesor graficzny jest bezczynny.

image

Jeśli przeglądarka danych śledzenia pokazuje duże przerwy między krokami, może to wskazywać, że program jest powiązany z danymi wejściowymi. W takim przypadku powinieneś zapoznać się z poprzednią sekcją dotyczącą debugowania potoku wejściowego, jeśli jeszcze tego nie zrobiłeś. Jednak nawet przy zoptymalizowanym potoku wejściowym nadal można zobaczyć przerwy między końcem jednego kroku a początkiem innego z powodu rywalizacji wątków procesora. tf.data wykorzystuje wątki w tle do równoległego przetwarzania potoków. Wątki te mogą kolidować z działaniami po stronie hosta GPU, które mają miejsce na początku każdego kroku, takich jak kopiowanie danych lub planowanie operacji GPU.

Jeśli widzisz duże luki po stronie hosta, który planuje te operacje na GPU, możesz ustawić zmienną środowiskową TF_GPU_THREAD_MODE=gpu_private . Gwarantuje to, że jądra GPU są uruchamiane z własnych dedykowanych wątków i nie są umieszczane w kolejce za pracą tf.data .

Luki między krokami mogą być również spowodowane obliczeniami metrycznymi, wywołaniami zwrotnymi Keras lub tf.function poza tf.function które działają na hoście. Te operacje nie mają tak dobrej wydajności jak operacje wewnątrz wykresu TensorFlow. Ponadto niektóre z tych operacji działają na procesorze i kopiują tensory tam iz powrotem z GPU.

Jeśli po zoptymalizowaniu potoku wejściowego nadal zauważasz przerwy między krokami w przeglądarce śledzenia, powinieneś spojrzeć na kod modelu między krokami i sprawdzić, czy wyłączenie wywołań zwrotnych / metryk poprawia wydajność. Niektóre szczegóły tych operacji znajdują się również w przeglądarce śledzenia (zarówno po stronie urządzenia, jak i hosta) .Zalecenie w tym scenariuszu polega na zamortyzowaniu narzutu tych operacji przez wykonanie ich po ustalonej liczbie kroków zamiast po każdym kroku. Podczas korzystania z metody compile w interfejsie API tf.keras ustawienie flagi experimental_steps_per_execution robi to automatycznie. W przypadku niestandardowych pętli treningowych użyj tf.while_loop .

Osiągnij wyższe wykorzystanie urządzeń

Małe jądra GPU i opóźnienia uruchamiania jądra hosta

Host kolejkuje jądra, które mają być uruchomione na GPU, ale istnieje opóźnienie (około 20-40 μs), zanim jądra zostaną faktycznie uruchomione na GPU. W idealnym przypadku host umieszcza w kolejce wystarczającą liczbę jąder na GPU, tak że GPU spędza większość czasu na wykonywaniu, zamiast czekać, aż host umieści w kolejce więcej jąder.

W TensorFlow Profiler na stronie Przegląd pokazuje, ile czasu GPU był bezczynny ze względu na oczekiwanie na hosta do jąder nośnych. Na poniższym obrazku GPU jest bezczynne przez około 10% czasu oczekiwania na uruchomienie jądra.

image

Przeglądarka śladów dla tego samego programu pokazuje małe przerwy między jądrami, w których host jest zajęty uruchamianiem jąder na GPU.

image

Uruchamiając wiele małych operacji na GPU (takich jak na przykład dodawanie skalarne), host może nie nadążać za GPU. Strona Statystyki Tensorflow dla tego samego profilu TensorFlow pokazuje 126 224 operacji Mul trwających 2,77 sekundy. W związku z tym każde jądro ma około 21,9 μs, co jest bardzo małe (mniej więcej w tym samym czasie co opóźnienie uruchomienia) i może potencjalnie powodować opóźnienia w uruchamianiu jądra hosta.

image

Jeśli przeglądarka danych śledzenia pokazuje wiele małych przerw między operacjami na GPU, jak na powyższym obrazku, możesz:

  • Połącz małe tensory i użyj operacji zwektoryzowanych lub użyj większego rozmiaru partii, aby każde uruchomione jądro wykonało więcej pracy, co sprawi, że GPU będzie dłużej zajęty.
  • Upewnij się, że używasz tf.function do tworzenia wykresów TF i nie tf.function operacji w czystym trybie zachłannym (robi to automatycznie za pomocą tf.keras.Model.compile ).
  • Połącz jądra używając XLA. Aby uzyskać więcej informacji, zapoznaj się z sekcją poniżej dotyczącą włączania XLA w celu uzyskania wyższej wydajności. Jest to funkcja eksperymentalna, ale prowadzi do wysokiego wykorzystania urządzenia.
Umieszczenie operacji Tensorflow

TensorFlow Profiler Przegląd Page opowiada o procencie ops umieszczonej na hoście vs urządzenia (można również zweryfikować umieszczenia określonych ops patrząc na widza śladowego). Podobnie jak na poniższym obrazku, chcesz, aby procent operacji na hoście był bardzo mały w porównaniu z urządzeniem.

image

W idealnym przypadku większość operacji wymagających dużej mocy obliczeniowej powinna być umieszczona na GPU. Aby dowiedzieć się, do których urządzeń przypisane są operacje i tensory w Twoim modelu, ustaw tf.debugging.set_log_device_placement(True) jako pierwszą instrukcję programu. Zauważ, że w niektórych przypadkach, nawet jeśli określisz op do umieszczenia na określonym urządzeniu, jego implementacja może zastąpić ten warunek (przykład: tf.unique ). Nawet w przypadku szkolenia z jednym GPU określenie strategii dystrybucji, takiej jak tf.distribute.OneDeviceStrategy , może skutkować bardziej deterministycznym rozmieszczeniem operacji na urządzeniu.

Jednym z powodów umieszczenia większości operacji na GPU jest zapobieganie nadmiernej ilości kopii pamięci między hostem a urządzeniem (oczekuje się kopii pamięci dla danych wejściowych / wyjściowych modelu między hostem a urządzeniem). Przykład nadmiernego kopiowania można zobaczyć w widoku śledzenia poniżej na strumieniach GPU # 167 , # 168 i # 169 .

image

Kopie te mogą czasami obniżyć wydajność, jeśli blokują jądro GPU przed uruchomieniem. Operacje kopiowania pamięci w przeglądarce śledzenia zawierają więcej informacji o operacjach, które są źródłem tych skopiowanych tensorów, ale skojarzenie memCopy z operacją może nie zawsze być łatwe. W takich przypadkach warto przyjrzeć się operacjom znajdującym się w pobliżu, aby sprawdzić, czy kopia pamięci odbywa się w tym samym miejscu na każdym kroku.

Bardziej wydajne jądra na procesorach graficznych

Gdy wykorzystanie procesora GPU w programie jest do przyjęcia, następnym krokiem jest zbadanie zwiększenia wydajności jądra GPU poprzez wykorzystanie rdzeni Tensor lub fusing ops.

Wykorzystanie rdzeni tensorowych

Nowoczesne procesory graficzne mają wyspecjalizowane rdzenie tensorowe, które mogą znacznie poprawić wydajność kwalifikujących się jąder. Strona statystyk jądra GPU wskazuje, które jądra GPU są obsługiwane przez Tensor Core i które jądra używają Tensor Core. Włączenie fp16 (patrz sekcja Włączanie dokładności mieszanej poniżej) jest jednym ze sposobów, aby jądra programu General Matrix Multiply (GEMM) (matmul ops) wykorzystywały rdzeń Tensor.

Aby uzyskać inne szczegółowe zalecenia, jak zwiększyć wydajność jądra dla procesorów graficznych, zapoznaj się z przewodnikiem NVIDIA Deep Learning Performance , który obejmuje różne techniki, z którymi można eksperymentować, takie jak używanie formatu NCHW vs NHWC do reprezentowania danych wejściowych lub dostosowywanie wymiarów wejściowych do wielokrotność 8.

Fusing Ops

Z tf.xla.experimental_compile funkcji TensorFlow może bezpiecznik mniejsze ops tworząc większe jądra prowadzące do znacznego przyrostu wydajności. Więcej szczegółów omówiono w sekcji Włącz XLA poniżej.

Włącz fp16 i XLA

Po wykonaniu powyższych czynności, włączenie precyzji mieszanej i XLA to dwa opcjonalne kroki, które można podjąć w celu dalszej poprawy wydajności. Sugerowane podejście polega na włączaniu ich pojedynczo i sprawdzaniu, czy korzyści w zakresie wydajności są zgodne z oczekiwaniami.

Włączanie mieszanej precyzji

Przewodnik TensorFlow Mixed Precision pokazuje, jak włączyć precyzję fp16 na procesorach graficznych. Jeśli chodzi o uświadomienie sobie korzyści płynących z wydajności fp16, należy pamiętać o kilku kwestiach.

Korzystanie z optymalnych jąder fp16

Po włączeniu fp16 jądra programu z mnożeniem macierzy (GEMM) powinny używać odpowiedniej wersji fp16, która wykorzystuje rdzenie Tensor. Jednak w niektórych przypadkach tak się nie dzieje i nie widać oczekiwanego przyspieszenia po włączeniu fp16, ponieważ zamiast tego program wraca do nieefektywnej implementacji.

image

Strona statystyk jądra GPU pokazuje, które operacje są obsługiwane przez Tensor Core i które jądra faktycznie używają wydajnego Tensor Core. Przewodnik firmy NVIDIA dotyczący wydajności uczenia głębokiego zawiera dodatkowe sugestie dotyczące wykorzystania rdzeni Tensor. Ponadto zalety fp16 będą widoczne również w jądrach, które wcześniej były związane z pamięcią, ponieważ teraz operacja zajmie połowę czasu.

Dynamiczne i statyczne skalowanie strat

Skalowanie strat jest konieczne podczas korzystania z fp16, aby zapobiec niedomiarowi spowodowanemu niską precyzją. Istnieją dwa typy skalowania strat, dynamiczne i statyczne, z których oba są szczegółowo wyjaśnione w przewodniku po mieszanej precyzji . Podczas próby optymalizacji wydajności należy pamiętać, że dynamiczne skalowanie strat może wprowadzać dodatkowe operacje warunkowe działające na hoście i prowadzić do luk, które będą widoczne między krokami w przeglądarce śledzenia. Z drugiej strony statyczne skalowanie strat nie ma takich narzutów i może być lepszą opcją pod względem wydajności z połowem, w którym należy określić poprawną wartość skali statycznej straty.

Włączanie XLA

Ostatnim krokiem do uzyskania najlepszej wydajności z pojedynczym procesorem graficznym jest możliwość eksperymentowania z włączeniem XLA, który połączy operacje i doprowadzi do lepszego wykorzystania urządzenia i mniejszego wykorzystania pamięci. Aby uzyskać szczegółowe informacje na temat włączania XLA w programie, zapoznaj się z przewodnikiem XLA: Optimizing Compiler for Machine Learning .

Zoptymalizuj wydajność na jednym hoście z wieloma GPU

tf.distribute.MirroredStrategy API tf.distribute.MirroredStrategy może służyć do skalowania uczenia modelu z 1 GPU do wielu GPU na jednym hoście. Aby dowiedzieć się więcej o tym, jak przeprowadzić szkolenie rozproszone za pomocą Tensorflow, zapoznaj się z przewodnikiem Szkolenie rozproszone z Keras . Chociaż przejście z jednego GPU na wiele GPU powinno być idealnie skalowalne po wyjęciu z pudełka, czasami mogą wystąpić problemy z wydajnością.

Przechodząc od szkolenia z jednym GPU do wielu procesorów GPU na tym samym hoście, najlepiej byłoby zobaczyć skalowanie wydajności tylko z dodatkowym narzutem związanym z komunikacją gradientową i zwiększonym wykorzystaniem wątków hosta. Z powodu tego narzutu nie zobaczysz dokładnego dwukrotnego przyspieszenia, jeśli na przykład zmienisz 1 na 2 GPU. Poniższy widok śledzenia przedstawia przykład dodatkowego obciążenia komunikacyjnego podczas uczenia na wielu procesorach graficznych. Łączenie gradientów, przekazywanie ich między replikami i dzielenie ich przed wykonaniem aktualizacji wagi wiąże się z pewnym obciążeniem.

image

Poniższa lista kontrolna pomoże Ci osiągnąć lepszą wydajność podczas optymalizacji wydajności w scenariuszu z wieloma GPU:

  1. Spróbuj zmaksymalizować rozmiar partii, co doprowadzi do większego wykorzystania urządzenia i zamortyzuje koszty komunikacji między wieloma GPU. Korzystanie z narzędzia do profilowania pamięci pomaga zorientować się, jak blisko jest maksymalne wykorzystanie pamięci przez program. Należy zauważyć, że chociaż większy rozmiar wsadu może wpływać na konwergencję, zwykle przeważają nad tym korzyści związane z wydajnością.
  2. Podczas przechodzenia z jednego procesora graficznego na wiele procesorów graficznych ten sam host musi teraz przetwarzać znacznie więcej danych wejściowych. Dlatego po (1) zaleca się ponowne sprawdzenie wydajności potoku wejściowego i upewnienie się, że nie jest to wąskie gardło.
  3. Sprawdź oś czasu GPU w widoku śledzenia programu, aby zobaczyć, czy występują niepotrzebne wywołania AllReduce, ponieważ powoduje to synchronizację na wszystkich urządzeniach. W widoku śledzenia pokazanym powyżej AllReduce odbywa się za pośrednictwem jądra NCCL, a na każdym GPU jest tylko jedno wywołanie NCCL dla gradientów na każdym kroku.
  4. Sprawdź, czy nie ma niepotrzebnych operacji kopiowania D2H, H2D i D2D i zobacz, czy można je zminimalizować.
  5. Sprawdź czas kroku, aby upewnić się, że każda replika wykonuje tę samą pracę. Może się zdarzyć, że jeden GPU (zazwyczaj GPU0) jest nadsubskrybowany, ponieważ host omyłkowo kładzie nad nim więcej pracy.
  6. Na koniec sprawdź etap szkolenia na wszystkich procesorach GPU w widoku śledzenia pod kątem wszystkich operacji, które są wykonywane sekwencyjnie. Zwykle dzieje się tak, gdy program zawiera zależności sterowania między jednym GPU a drugim. Wydajność debugowania w tej sytuacji była w przeszłości rozwiązywana indywidualnie dla każdego przypadku. Jeśli zauważysz to zachowanie w swoim programie, zgłoś problem na Github z obrazami widoku śledzenia.

Zoptymalizuj Gradient AllReduce

Podczas treningu ze strategią synchroniczną każde urządzenie otrzymuje część danych wejściowych. Po obliczeniu przejść do przodu i do tyłu przez model, gradienty obliczone na każdym urządzeniu muszą zostać zagregowane i zredukowane. Ten gradient AllReduce ma miejsce po obliczeniu gradientu na każdym urządzeniu i przed aktualizacją wag modelu przez optymalizator. Każdy GPU najpierw łączy gradienty na warstwach modelu, przekazuje je do GPU za pomocą tf.distribute.CrossDeviceOps ( tf.distribute.NcclAllReduce jest tf.distribute.NcclAllReduce ), a następnie zwraca gradienty po redukcji na warstwę. Optymalizator użyje tych zredukowanych gradientów, aby zaktualizować wagi Twojego modelu. Idealnie byłoby, gdyby ten proces odbywał się w tym samym czasie na wszystkich procesorach graficznych, aby zapobiec jakimkolwiek narzutom. Czas do AllReduce powinien być mniej więcej taki sam, jak:

(number of parameters * 4bytes)/ (communication bandwidth)

To obliczenie jest przydatne jako szybkie sprawdzenie, czy wydajność widoczna podczas uruchamiania rozproszonego zadania szkolenia jest zgodna z oczekiwaniami, czy też konieczne jest dalsze debugowanie wydajności. Możesz pobrać liczbę parametrów w swoim modelu z tf.keras.Model.summary .

Zauważ, że każdy parametr modelu ma 4 bajty, ponieważ Tensorflow używa fp32 do komunikowania gradientów. Nawet jeśli włączysz fp16, NCCL AllReduce wykorzystuje parametry fp32. W przyszłości Tensorflow będzie obsługiwał operacje AllReduce przy użyciu fp16, a także potokowanie gradientu AllReduce, aby pokrywał się z obliczeniem gradientu.

Aby zobaczyć korzyści ze skalowania, czas kroku musi być znacznie wyższy w porównaniu z tymi narzutami. Jednym ze sposobów osiągnięcia tego jest użycie większego rozmiaru partii, ponieważ rozmiar partii wpływa na czas kroku, ale nie wpływa na narzuty komunikacji.

Rywalizacja wątków hosta GPU

Podczas uruchamiania wielu procesorów graficznych zadaniem procesora jest utrzymanie zajętości wszystkich urządzeń poprzez wydajne uruchamianie jąder GPU na urządzeniach. Jednak gdy istnieje wiele niezależnych operacji, które procesor może zaplanować na jednym GPU, procesor może zdecydować o użyciu wielu wątków hosta, aby utrzymać zajęcie jednego GPU, a następnie uruchomić jądra na innym GPU w kolejności niedeterministycznej . Może to spowodować wypaczenie lub ujemne skalowanie, co może negatywnie wpłynąć na wydajność.

Poniższa przeglądarka śledzenia pokazuje narzut, gdy procesor zachodzi w czasie. Jądro GPU uruchamia się nieefektywnie, ponieważ GPU1 jest w stanie bezczynności, a następnie uruchamia operacje po uruchomieniu GPU2.

image

Widok śledzenia dla hosta pokazuje, że host uruchamia jądra na GPU2 przed uruchomieniem ich na GPU1 (zwróć uwagę, że poniższe operacje tf_Compute * nie wskazują na wątki procesora).

image

Jeśli widzisz tę oszałamiającą liczbę jąder GPU w widoku śledzenia programu, zalecaną czynnością jest:

  • Ustaw zmienną środowiskową TF_GPU_THREAD_MODE na gpu_private . Ta zmienna środowiskowa powie hostowi, aby zachował prywatne wątki dla GPU.
  • Domyślnie TF_GPU_THREAD_MODE=gpu_private ustawia liczbę wątków na 2, co jest wystarczające w większości przypadków. Jednak tę liczbę można zmienić, ustawiając zmienną środowiskową TF_GPU_THREAD_COUNT na żądaną liczbę wątków.