Pomoc chronić Wielkiej Rafy Koralowej z TensorFlow na Kaggle Dołącz Wyzwanie

Zoptymalizuj wydajność GPU TensorFlow za pomocą TensorFlow Profiler

Przegląd

Ten przewodnik pokaże Ci, jak korzystać z TensorFlow Profiler z TensorBoard, aby uzyskać wgląd i uzyskać maksymalną wydajność swoich procesorów graficznych oraz debugować, gdy jeden lub więcej procesorów graficznych nie jest w pełni wykorzystanych.

Jeśli jesteś nowy w Profiler:

Należy pamiętać, że odciążanie obliczeń na GPU może nie zawsze być korzystne, szczególnie w przypadku małych modeli. Może wystąpić obciążenie z powodu:

  • Transfer danych między hostem (CPU) a urządzeniem (GPU); i
  • Ze względu na opóźnienie związane z uruchomieniem jądra GPU przez hosta.

Proces optymalizacji wydajności optimization

W tym przewodniku opisano, jak debugować problemy z wydajnością, zaczynając od pojedynczego procesora GPU, a następnie przechodząc do jednego hosta z wieloma procesorami GPU.

Zaleca się debugowanie problemów z wydajnością w następującej kolejności:

  1. Zoptymalizuj i debuguj wydajność na jednym GPU:
    1. Sprawdź, czy rurociąg wejściowy jest wąskim gardłem.
    2. Debuguj wydajność jednego GPU.
    3. Włączanie mieszany precyzji (z fp16 (float16)) i ewentualnie umożliwić XLA .
  2. Optymalizuj i debuguj wydajność na pojedynczym hoście obsługującym wiele procesorów graficznych.

Na przykład, jeśli używasz TensorFlow strategię dystrybucji do trenowania modelu na jednym komputerze z wieloma procesorami graficznymi i zauważ nieoptymalne wykorzystanie GPU, należy najpierw zoptymalizować wydajność i debugowania dla jednego GPU przed debugowania systemu multi-GPU.

Jako punkt odniesienia dla uzyskania kodu na wydajnych procesorów graficznych, ten poradnik zakłada już używasz tf.function . Keras Model.compile i Model.fit API będzie wykorzystywać tf.function automatycznie pod maską. Pisząc pętlę zwyczaj szkoleniowy z tf.GradientTape , odnoszą się do wydajności lepiej tf.function na temat włączania tf.function s.

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.

1. Zoptymalizuj wydajność na jednym GPU

W idealnym przypadku program powinien mieć wysokie wykorzystanie procesora GPU, minimalną komunikację między procesorem (host) a procesorem graficznym (urządzenie) i brak obciążenia z potoku wejściowego.

Pierwszym krokiem w analizie wydajności jest uzyskanie profilu dla modelu działającego z jednym GPU.

TensorBoard za Profiler strona Przegląd -co pokazuje widok z najwyższego poziomu, jak dany model wykonano podczas profil run-może zapewnić pomysł, jak daleko twój program jest ze scenariusza idealnego.

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ądra użyć fp16

Osiągnięcie optymalnej wydajności oznacza maksymalizację tych liczb we wszystkich trzech przypadkach. Aby uzyskać dogłębne zrozumienie swojego programu, trzeba znać Profiler TensorBoard w śladowej widza . W poniższych sekcjach przedstawiono niektóre typowe wzorce przeglądarki śledzenia, których należy szukać podczas diagnozowania wąskich gardeł wydajności.

Poniżej znajduje się obraz widoku śledzenia modelu działającego na jednym GPU. Z zakresu TensorFlow Imię i TensorFlow Ops sekcjach można zidentyfikować różne części modelu, jak piłkę do przodu, do tyłu, funkcja strat pass / obliczenia gradientu i aktualizacji wagi optymalizator. Można również mieć OPS uruchomionych na GPU obok każdego Stream, które odnoszą się do strumieni CUDA. Każdy strumień jest używany do określonych zadań. W tym śladu, Stream # 118 jest używany do uruchamiania jądra obliczeniowe i urządzenie-urządzenie kopii. Strumień 119 jest używany do kopiowania gospodarza na urządzeniu i strumień # 120 dla urządzenia do kopiowania gospodarza.

Poniższy ślad pokazuje wspólne cechy wydajnego modelu.

image

Na przykład, timeline obliczeniowej GPU (strumień 118) wygląda „zajęty” z nielicznymi przerwami. Istnieje minimalna kopie z hosta do urządzenia (strumień 119) i z urządzenia przyjmującego (strumień nr 120), jak również minimalne odstępy pomiędzy stopniami. Po uruchomieniu programu Profiler może nie być możliwe zidentyfikowanie tych idealnych cech w widoku śledzenia. Pozostała część tego przewodnika obejmuje typowe scenariusze i sposoby ich rozwiązywania.

1. Debuguj potok wejściowy

Pierwszym krokiem w debugowaniu wydajności GPU jest ustalenie, czy program jest powiązany z danymi wejściowymi. Najprostszym sposobem, aby dowiedzieć się tego jest użycie profilera jest analizator Input-rurociągu , na TensorBoard, który stanowi przegląd czasu spędzonego w rurociągu wejściowym.

image

Możesz wykonać następujące potencjalne działania, jeśli potok danych wejściowych znacząco wpływa na czas kroku:

  • Można użyć tf.data specyficznych dla instrukcji , aby dowiedzieć się, jak debugować rurociągu wejściowego.
  • 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ą wstępnego przetwarzania. Oto przykład zastosowania tej techniki dla modelu ResNet. Jeśli potok wejściowy jest optymalny, powinieneś doświadczyć podobnej wydajności z rzeczywistymi danymi i wygenerowanymi losowymi/syntetycznymi danymi. Jedynym obciążeniem w przypadku danych syntetycznych będzie kopiowanie danych wejściowych, które ponownie można wstępnie pobrać i zoptymalizować.

Ponadto odnosi się do najlepszych praktyk w celu optymalizacji rurociągu danych wejściowych .

2. Debuguj wydajność jednego GPU

Istnieje kilka czynników, które mogą przyczynić się do niskiego wykorzystania GPU. Poniżej przedstawiono kilka scenariuszy powszechnie obserwowane, gdy patrząc na widza śladowej i potencjalnych rozwiązań.

1. Przeanalizuj luki między krokami

Częstą obserwacją, gdy Twój program nie działa optymalnie, są przerwy między etapami treningu. Na poniższym obrazie widoku śladu widać dużą przerwę między krokami 8 i 9, co oznacza, że ​​procesor graficzny jest w tym czasie bezczynny.

image

Jeśli przeglądarka ś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 mogą występować przerwy między końcem jednego kroku a początkiem drugiego z powodu rywalizacji o wątki procesora. tf.data korzysta z wątków tła parallelize przetwarzanie potokowe. Te wątki mogą zakłócać aktywność GPU po stronie hosta, która ma miejsce na początku każdego kroku, na przykład kopiowanie danych lub planowanie operacji GPU.

Jeśli zauważysz duże luki po stronie hosta, który rozkłady tych OPS na GPU, można ustawić zmienną środowiskową TF_GPU_THREAD_MODE=gpu_private . Gwarantuje to, że jądra GPU uruchamiane z własnych dedykowanych wątków, a nie dostać w kolejce za tf.data pracy.

Szczeliny pomiędzy etapami mogą być również spowodowane przez obliczeń metrycznych, callbacków Keras lub ops poza tf.function które działają na gospodarza. Te operacje nie mają tak dobrej wydajności, jak operacje na wykresie 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żysz 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). W tym scenariuszu zaleca się amortyzację kosztów tych operacji, wykonując je po określonej liczbie kroków zamiast po każdym kroku. Podczas korzystania z compile metody w tf.keras API, ustawienie experimental_steps_per_execution flagę robi to automatycznie. Pętle zwyczaj szkoleniowych, stosowania tf.while_loop .

2. Osiągnij wyższe wykorzystanie urządzenia

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

Host umieszcza jądra w kolejce do uruchomienia na GPU, ale występuje opóźnienie (około 20-40 μs), zanim jądra zostaną faktycznie wykonane na GPU. W idealnym przypadku host umieszcza w kolejce wystarczającą liczbę jąder na GPU, aby GPU spędzał większość czasu na wykonywaniu, zamiast czekać na hosta, aby umieścić w kolejce więcej jąder.

Profilera za przegląd strona na pokazach TensorBoard ile czasu GPU był bezczynny ze względu na oczekiwanie na hosta do jąder nośnych. Na poniższym obrazku GPU jest bezczynny przez około 10% czasu kroku, czekając na uruchomienie jądra.

image

Widz ślad za ten sam program pokazuje małe luki między jądrami gdzie gospodarzem jest zajęty jądra wodowania na GPU.

image

Uruchamiając wiele małych operacji na GPU (takich jak na przykład dodanie skalarne), host może nie nadążać za GPU. TensorFlow Statystyki narzędziem TensorBoard dla tego samego profilu pokazy 126,224 operacji Mul biorących 2,77 sekundy. W ten sposób 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 uruchamiania) i może potencjalnie skutkować opóźnieniami uruchamiania jądra hosta.

image

Jeśli twoje widza śladowe pokazuje wiele małych szczelin pomiędzy OPS na GPU jak na obrazku powyżej, można:

  • Połącz małe tensory i użyj wektoryzacji operacji lub użyj większego rozmiaru wsadu, aby każde uruchomione jądro wykonało więcej pracy, co pozwoli na dłuższe zajęcie GPU.
  • Upewnij się, że używasz tf.function tworzyć wykresy TensorFlow, tak, że nie są uruchomione ops w czystym trybie chętny. Jeśli używasz Model.fit (jak sprzeciwiać się do pętli niestandardowe szkolenia z tf.GradientTape ), następnie tf.keras.Model.compile automatycznie zrobi to za Ciebie.
  • Bezpiecznik jądra używając XLA z tf.function(jit_compile=True) lub automatycznego grupowania. Aby uzyskać więcej informacji, przejdź do Włącz mieszaną precyzję i XLA sekcję poniżej, aby dowiedzieć się, jak włączyć XLA, aby uzyskać wyższą wydajność. Ta funkcja może prowadzić do wysokiego wykorzystania urządzenia.
2. Rozmieszczenie operacji TensorFlow

Profilera stronie Przegląd pokazuje odsetek ops umieszczane na komputerze w porównaniu z urządzeniem (można również zweryfikować umieszczenia określonych ops patrząc na widza śladowego . Tak jak na obrazku poniżej, chcesz procentu ops na hoście być bardzo mały w porównaniu do urządzenia.

image

W idealnym przypadku większość operacji wymagających dużej mocy obliczeniowej powinna być umieszczona na GPU.

Aby dowiedzieć się, które urządzenia operacje i tensory w modelu są przypisane, zestaw tf.debugging.set_log_device_placement(True) jako pierwszego zestawienia programu.

Należy pamiętać, że w niektórych przypadkach, nawet jeśli podasz op do umieszczenia na danym urządzeniu, jego realizacji może przesłonić tego warunku (przykład: tf.unique ). Nawet na szkolenie pojedynczego GPU, określając strategię dystrybucji, takich jak tf.distribute.OneDeviceStrategy , może skutkować bardziej deterministyczny umieszczenie ops na urządzeniu.

Jednym z powodów umieszczania większości operacji na GPU jest zapobieganie nadmiernym kopiom 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 kopiowaniem pokazano w widoku śladowych poniżej na graficznego strumieni # 167, # 168 i # 169.

image

Te kopie mogą czasami obniżyć wydajność, jeśli blokują wykonywanie jąder GPU. Pamięć operacje kopiowania w ślad widza uzyskać więcej informacji na temat programów operacyjnych, które są źródłem tych skopiowanych tensorów, ale nie zawsze może być łatwo skojarzyć memCopy z op. W takich przypadkach warto przyjrzeć się pobliskim operatorom, aby sprawdzić, czy kopia pamięci dzieje się w tym samym miejscu na każdym kroku.

3. Wydajniejsze jądra na GPU

Gdy wykorzystanie procesora graficznego przez program jest akceptowalne, następnym krokiem jest przyjrzenie się zwiększeniu wydajności jąder GPU poprzez wykorzystanie rdzeni tensorowych lub fusing ops.

1. Wykorzystaj rdzenie tensorów

Nowoczesne procesory graficzne NVIDIA® specjalizuje Tensor rdzeni , które mogą znacznie poprawić wydajność kwalifikowanych jąder.

Można użyć TensorBoard za statystyk jądra GPU do wizualizacji, które GPU jądra są Tensor Core kwalifikowane, a które jądra używasz Tensor rdzeni. Włączanie fp16 (patrz sekcja Włączanie Mixed Precision poniżej) jest jednym ze sposobów, aby uczynić Państwa programu Ogólny Matrix Multiply (GEMM) jądra (matmul ops) wykorzystują tensora Core. Jądra GPU używać tensora Cores efektywnie, gdy precyzja jest FP16 i wymiary wejścia / wyjścia tensor jest podzielna przez 8 lub 16 (dla int8 ).

Dla innych szczegółowych zaleceń dotyczących sposobu, aby jądra efektywne dla GPU, patrz NVIDIA® głęboka nauka wydajności przewodnika.

2. Operacje bezpieczników

Zastosowanie tf.function(jit_compile=True) bezpiecznik mniejsze ops tworząc większe jądra prowadzące do znacznego przyrostu wydajności. Aby dowiedzieć się więcej, zapoznaj się z XLA przewodnika.

3. Włącz mieszaną precyzję i XLA

Po wykonaniu powyższych kroków włączenie mieszanej precyzji i XLA to dwa opcjonalne kroki, które można wykonać, aby jeszcze bardziej poprawić wydajność. Sugerowanym podejściem jest włączenie ich pojedynczo i sprawdzenie, czy korzyści z wydajności są zgodne z oczekiwaniami.

1. Włącz mieszaną precyzję

W TensorFlow Mixed precyzyjne pokazy guide jak włączyć fp16 precyzję na GPU. Włącz AMP na GPU NVIDIA® używać tensora rdzeni i zrealizować do 3x ogólnych speedups w porównaniu z użyciem tylko fp32 (float32) Precyzja na Volta i nowszych architektury GPU.

Upewnij się, że wymiary macierzy/tensorów spełniają wymagania dotyczące wywoływania jąder korzystających z rdzeni tensorów. Jądra GPU efektywnie wykorzystują rdzenie tensorowe, gdy precyzja wynosi fp16, a wymiary wejścia/wyjścia są podzielne przez 8 lub 16 (dla int8).

Należy zauważyć, że w przypadku cuDNN w wersji 7.6.3 i nowszych wymiary splotu będą automatycznie dopełniane w razie potrzeby w celu wykorzystania rdzeni tensorowych.

Postępuj zgodnie z najlepszymi praktykami poniżej w celu maksymalizacji korzyści wydajnościowe fp16 precyzją.

1. Używaj optymalnych jąder fp16

Z fp16 włączona, mnożenie macierzy Twojego programu (GEMM) jądra, należy użyć odpowiedniego fp16 wersji, która wykorzystuje tensora rdzeni. Jednak w niektórych przypadkach, to nie zdarza i nie doświadczyć oczekiwanego przyspieszenie od umożliwiając fp16 , a program wraca do nieskutecznego wdrażania zamiast.

image

The Kernel GPU stronie Statystyki pokazuje, które są ośrodkami ops kwalifikowalne i które Tensor rdzeniowe są faktycznie przy użyciu wydajnego tensora Core. Przewodnik NVIDIA® na głębokim wydajności uczenia zawiera dodatkowe propozycje, w jaki sposób wykorzystać Tensor rdzeni. Dodatkowo, zalety korzystania fp16 pokaże także w jądrach, które były poprzednio pamięć związana, jak teraz ops zajmie połowę czasu.

2. Dynamiczne a statyczne skalowanie strat

Skalowanie strata jest konieczne przy korzystaniu fp16 aby zapobiec niedomiar ze względu na niską precyzją. Istnieją dwa rodzaje strat skalowania, dynamicznych i statycznych, z których oba są objaśnione w mieszanym przewodnika Precision . Można użyć mixed_float16 politykę automatycznie włączyć skalowanie utrata zasięgu optymalizator Keras.

Próbując zoptymalizować wydajność, należy pamiętać, że dynamiczne skalowanie strat może wprowadzić dodatkowe operacje warunkowe działające na hoście i prowadzić do przerw, które będą widoczne między krokami w przeglądarce śledzenia. Z drugiej strony skalowanie strat statycznych nie wiąże się z takimi kosztami ogólnymi i może być lepszą opcją pod względem wydajności z haczykiem, który należy określić poprawną wartość skali strat statycznych.

2. Włącz XLA za pomocą tf.function(jit_compile=True) lub automatycznego klastrowania

Ostatnim krokiem do uzyskania najlepszej wydajności przy użyciu pojedynczego procesora graficznego może być eksperymentowanie z włączeniem XLA, które połączy operacje i doprowadzi do lepszego wykorzystania urządzeń i mniejszego zużycia pamięci. Szczegółowe informacje na temat sposobu włączania XLA w programie z tf.function(jit_compile=True) lub automatycznego grupowania, patrz XLA przewodnika.

Można ustawić globalny poziom JIT do -1 (wyłączone), 1 lub 2 . Wyższy poziom jest bardziej agresywny i może zmniejszać równoległość i zużywać więcej pamięci. Ustawić wartość 1 , jeśli masz ograniczenia pamięci. Należy zauważyć, że XLA nie działa dobrze w przypadku modeli ze zmiennymi kształtami tensora wejściowego, ponieważ kompilator XLA musiałby kompilować jądra za każdym razem, gdy napotka nowe kształty.

2. Zoptymalizuj wydajność na pojedynczym hoście z wieloma procesorami graficznymi

tf.distribute.MirroredStrategy API może być używany do skalowania szkolenia model z jednego graficznego do wielu procesorów graficznych w jednym gospodarzu. (Aby dowiedzieć się więcej o tym, jak zrobić rozproszoną szkolenia z TensorFlow, patrz Ukazuje szkolenia z TensorFlow , używać GPU i Korzystanie z TPU przewodników i Ukazuje szkolenia z Keras tutorialu).

Chociaż przejście z jednego procesora graficznego na wiele procesorów graficznych powinno być skalowalne po wyjęciu z pudełka, czasami mogą wystąpić problemy z wydajnością.

W przypadku przechodzenia ze szkolenia z jednym procesorem GPU do wielu procesorów GPU na tym samym hoście najlepiej byłoby doświadczyć skalowania wydajności tylko z dodatkowym obciążeniem komunikacji gradientowej i zwiększonym wykorzystaniem wątków hosta. Z powodu tego narzutu nie będziesz mieć dokładnego 2-krotnego przyspieszenia, jeśli na przykład przejdziesz z 1 do 2 procesorów graficznych.

Poniższy widok śledzenia pokazuje przykład dodatkowego obciążenia komunikacyjnego podczas uczenia na wielu procesorach graficznych. Jest trochę narzutu na łączenie gradientów, przekazywanie ich między replikami i dzielenie ich przed aktualizacją wagi.

image

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

  1. Spróbuj zmaksymalizować wielkość partii, co doprowadzi do większego wykorzystania urządzeń i amortyzacji kosztów komunikacji między wieloma procesorami graficznymi. Korzystanie z profilera pamięci pomaga zorientować się, jak blisko program jest wykorzystanie pamięci szczyt. Należy zauważyć, że chociaż większa wielkość partii może wpływać na zbieżność, zwykle przewyższa to korzyści w zakresie wydajności.
  2. Przechodząc 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 pod kątem niepotrzebnych wywołań AllReduce, ponieważ powoduje to synchronizację na wszystkich urządzeniach. W widoku śledzenia przedstawionego powyżej, AllReduce odbywa się poprzez NCCL jądra, a tam jest tylko jedno połączenie NCCL na każdym GPU dla gradientów na każdym kroku.
  4. Sprawdź, czy nie ma niepotrzebnych operacji kopiowania D2H, H2D i D2D, które można zminimalizować.
  5. Sprawdź czas kroku, aby upewnić się, że każda replika wykonuje tę samą pracę. Na przykład, może się zdarzyć, że jeden GPU (zazwyczaj GPU0 ) są przekroczone, ponieważ gospodarz błędnie kończy się wprowadzenie więcej pracy na nim.
  6. Na koniec sprawdź krok uczenia na wszystkich procesorach graficznych w widoku śledzenia pod kątem wszelkich operacji, które są wykonywane sekwencyjnie. Zwykle dzieje się tak, gdy program zawiera zależności sterowania z jednego GPU do drugiego. W przeszłości debugowanie wydajności w tej sytuacji było rozwiązywane indywidualnie dla każdego przypadku. Jeśli można zaobserwować to zachowanie w programie, złożyć GitHub problem z obrazami widoku śladowego.

1. Optymalizuj 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 dzieje po obliczeniu gradientu na każdym urządzeniu, a przed optymalizator aktualizuje modelu ciężarów.

Procesorach pierwszy skleja gradienty w poprzek warstw modelu, łączy się je przez GPU wykorzystujących tf.distribute.CrossDeviceOps ( tf.distribute.NcclAllReduce domyślnie), a następnie powraca gradientów po redukcji na warstwę.

Optymalizator użyje tych zredukowanych gradientów do aktualizacji wag modelu. Idealnie, ten proces powinien odbywać się w tym samym czasie na wszystkich procesorach graficznych, aby zapobiec wszelkim obciążeniom.

Czas do AllReduce powinien być w przybliżeniu taki sam jak:

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

To obliczenie jest przydatne jako szybkie sprawdzenie, czy wydajność, którą masz podczas uruchamiania rozproszonego zadania szkoleniowego, jest zgodna z oczekiwaniami, czy też konieczne jest dalsze debugowanie wydajności. Można uzyskać liczbę parametrów w modelu z Model.summary .

Należy zauważyć, że każdy z parametrów modelu jest 4 bajtów od TensorFlow wykorzystuje fp32 (float32) do komunikacji gradienty. Nawet gdy masz fp16 włączona, NCCL AllReduce wykorzystuje fp32 parametry.

Aby uzyskać korzyści ze skalowania, czas kroku musi być znacznie wyższy w porównaniu z tymi kosztami ogólnymi. 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 obciążenie komunikacyjne.

2. Rywalizacja o wątki hosta GPU

Podczas uruchamiania wielu procesorów graficznych zadaniem procesora jest utrzymywanie wszystkich urządzeń zajętych 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ć się na użycie wielu wątków hosta, aby utrzymać zajęty jeden GPU, a następnie uruchomić jądra na innym GPU w niedeterministycznej kolejności . Może to spowodować przekrzywienie lub ujemne skalowanie, co może negatywnie wpłynąć na wydajność.

Widz ślad poniżej przedstawia napowietrznych gdy CPU zatacza uruchamia jądra GPU nieefektywnie, jak GPU1 jest bezczynny, a następnie uruchamia ops po GPU2 rozpoczęła.

image

Widok ślad na pokazy hosta host uruchamia jądra na GPU2 przed wprowadzeniem ich na GPU1 (uwaga, że niżej tf_Compute* ops nie są orientacyjne wątków CPU).

image

W przypadku wystąpienia tego rodzaju rozłożenia jąder GPU w widoku śledzenia programu zalecanym działaniem jest:

  • Ustaw TensorFlow zmienną środowiskową TF_GPU_THREAD_MODE do gpu_private . Ta zmienna środowiskowa poinformuje hosta o zachowaniu prywatności wątków dla GPU.
  • Domyślnie TF_GPU_THREAD_MODE=gpu_private ustawia liczbę wątków do 2, która jest wystarczająca w większości przypadków. Jednak liczba ta może być zmieniona przez ustawienie zmiennej środowiskowej TensorFlow TF_GPU_THREAD_COUNT do pożądanej liczby wątków.