午前9時PSTでMLシンポジウム、この10月19日(火曜日)の最初の女性の中にチューン今すぐ登録

TensorFlowプロファイラーを使用してTensorFlowGPUのパフォーマンスを最適化する

概要概要

このガイドでは、TensorBoardでTensorFlowプロファイラーを使用して、GPUを洞察し、最大のパフォーマンスを引き出し、1つ以上のGPUが十分に活用されていない場合にデバッグする方法を説明します。

プロファイラーを初めて使用する場合:

計算をGPUにオフロードすることは、特に小さなモデルの場合、必ずしも有益であるとは限らないことに注意してください。次の理由でオーバーヘッドが発生する可能性があります。

  • ホスト(CPU)とデバイス(GPU)間のデータ転送。そして
  • ホストがGPUカーネルを起動するときに伴う遅延のため。

パフォーマンス最適化ワークフロー

このガイドでは、単一のGPUから始めて、複数のGPUを備えた単一のホストに移行することでパフォーマンスの問題をデバッグする方法の概要を説明します。

次の順序でパフォーマンスの問題をデバッグすることをお勧めします。

  1. 1つのGPUでパフォーマンスを最適化およびデバッグします。
    1. 入力パイプラインがボトルネックになっていないかどうかを確認します。
    2. 1つのGPUのパフォーマンスをデバッグします。
    3. (と混合精度を有効fp16 (float16))および任意に有効XLAを
  2. マルチGPUシングルホストのパフォーマンスを最適化してデバッグします。

たとえば、あなたがTensorFlowの使用している場合は、流通戦略を複数のGPUと予告次善のGPUを利用して、単一のホスト上でモデルを訓練するために、あなたが最初に最適化する必要があり、マルチGPUシステムをデバッグする前に、1つのGPUのパフォーマンスをデバッグします。

GPU上でパフォーマンスの高いコードを取得するためのベースラインとして、このガイドでは、すでに使用していることを前提とtf.function 。 Keras Model.compileModel.fit APIが利用されますtf.functionボンネットの下に自動的に。でカスタムトレーニングループ書くときtf.GradientTape 、参照tf.functionとの優れた性能を有効にする方法についてtf.function秒。

次のセクションでは、パフォーマンスのボトルネックを特定して修正するために、上記の各シナリオで推奨されるアプローチについて説明します。

1.1つのGPUでパフォーマンスを最適化する

理想的なケースでは、プログラムのGPU使用率が高く、CPU(ホスト)からGPU(デバイス)への通信が最小限であり、入力パイプラインからのオーバーヘッドがない必要があります。

パフォーマンスを分析する最初のステップは、1つのGPUで実行されているモデルのプロファイルを取得することです。

TensorBoardのプロファイラー概要ページ、あなたのモデルは、プロファイル中に実行する方法のトップレベルのビューを示し-which実行は、することができ、あなたのプログラムは、理想的なシナリオからどれだけ離れているかのアイデアを提供しています。

TensorFlow Profiler Overview Page

概要ページに注意を払うべき重要な数字は次のとおりです。

  1. 実際のデバイス実行からのステップ時間はどれくらいですか
  2. デバイスとホストに配置された操作の割合
  3. どのように多くのカーネルが使用fp16

最適なパフォーマンスを達成するということは、3つのケースすべてでこれらの数値を最大化することを意味します。あなたのプログラムの深い理解を得るために、あなたはTensorBoardのプロファイラに精通している必要がありますトレースビューア。以下のセクションでは、パフォーマンスのボトルネックを診断するときに探す必要のある一般的なトレースビューアのパターンをいくつか示します。

以下は、1つのGPUで実行されているモデルトレースビューの画像です。 TensorFlow名前スコープTensorFlowオプスのセクションからは、あなたが、往路と同様に、損失関数をモデルの異なる部分を特定し、復路/勾配の計算、およびオプティマイザ重み更新することができます。あなたはまた、CUDAストリームを参照して、各ストリームの隣のGPU上で実行されているOPSを持つことができます。各ストリームは特定のタスクに使用されます。このトレースでは、ストリーム#118は、計算カーネルとデバイス間のコピーを起動するために使用されます。ストリーム#119は、ホストコピーにデバイスのホスト-デバイスのコピー及びストリーム#120のために使用されます。

以下のトレースは、パフォーマンスモデルの一般的な特性を示しています。

image

例えば、GPUの計算タイムライン(ストリーム#118)は非常に少ないギャップを持つ「忙しい」に見えます。最小ホストからデバイスへのコピー(ストリーム#119)、デバイスからホスト(ストリーム#120)に、同様にステップ間の最小の隙間があります。プログラムのプロファイラーを実行すると、トレースビューでこれらの理想的な特性を識別できない場合があります。このガイドの残りの部分では、一般的なシナリオとそれらを修正する方法について説明します。

1.入力パイプラインをデバッグします

GPUパフォーマンスデバッグの最初のステップは、プログラムが入力にバインドされているかどうかを判断することです。このアウトを理解する最も簡単な方法は、プロファイラの使用することです入力・パイプライン・アナライザを入力パイプラインに費やされた時間の概要を説明しTensorBoard、上、。

image

入力パイプラインがステップ時間に大きく寄与する場合は、次の潜在的なアクションを実行できます。

  • あなたは使用することができますtf.data固有のガイドをご入力パイプラインをデバッグする方法を学びます。
  • 入力パイプラインがボトルネックであるかどうかを確認するもう1つの簡単な方法は、前処理を必要としないランダムに生成された入力データを使用することです。ここでは一例であるResNetモデルのため、この技術を使用します。入力パイプラインが最適な場合、実際のデータと生成されたランダム/合成データで同様のパフォーマンスが得られるはずです。合成データの場合の唯一のオーバーヘッドは、入力データのコピーによるものであり、これもプリフェッチして最適化することができます。

また、参照入力データパイプラインを最適化するためのベストプラクティス

2.1つのGPUのパフォーマンスをデバッグします

GPU使用率の低下に寄与する可能性のあるいくつかの要因があります。下記のを見たときに一般的に観察いくつかのシナリオですトレースビューアおよび潜在的な解決策は。

1.ステップ間のギャップを分析します

プログラムが最適に実行されていない場合の一般的な観察は、トレーニングステップ間のギャップです。下のトレースビューの画像では、ステップ8と9の間に大きなギャップがあります。これは、その間、GPUがアイドル状態であることを意味します。

image

トレースビューアにステップ間に大きなギャップが表示される場合、これはプログラムが入力バウンドであることを示している可能性があります。その場合、まだ行っていない場合は、入力パイプラインのデバッグに関する前のセクションを参照する必要があります。

ただし、最適化された入力パイプラインを使用しても、CPUスレッドの競合により、1つのステップの終了と別のステップの開始の間にギャップが生じる可能性があります。 tf.dataパイプライン処理を並列化するためにバックグラウンドスレッドを使用しています。これらのスレッドは、データのコピーやGPU操作のスケジューリングなど、各ステップの開始時に発生するGPUホスト側のアクティビティに干渉する可能性があります。

あなたはGPU上でのスケジュールこれらのOPSホスト側の大きなギャップを、気付いた場合は、環境変数を設定することができますTF_GPU_THREAD_MODE=gpu_private 。 GPUカーネルが自分専用のスレッドから起動され、そして後にキューイングされませんので、この性を保証tf.data作品。

ステップ間のギャップはまた、メトリック計算、Kerasコールバック、または外のOPSが原因で発生することができtf.functionホスト上で実行すること。これらの操作は、TensorFlowグラフ内の操作ほど優れたパフォーマンスを発揮しません。さらに、これらの操作の一部はCPUで実行され、GPUからテンソルを前後にコピーします。

入力パイプラインを最適化した後もトレースビューアのステップ間にギャップがある場合は、ステップ間のモデルコードを確認し、コールバック/メトリックを無効にするとパフォーマンスが向上するかどうかを確認する必要があります。これらの操作の詳細の一部は、トレースビューア(デバイス側とホスト側の両方)にもあります。このシナリオでの推奨事項は、すべてのステップではなく、固定数のステップの後に実行することにより、これらの操作のオーバーヘッドを償却することです。使用する場合はcompileのメソッドtf.keras設定、APIをexperimental_steps_per_executionフラグを自動的にこれを行います。カスタムトレーニングループの場合、使用tf.while_loop

2.より高いデバイス使用率を実現します

1.小さなGPUカーネルとホストカーネルの起動遅延

ホストはGPUで実行されるカーネルをキューに入れますが、カーネルが実際にGPUで実行されるまでには、待ち時間(約20〜40μs)が伴います。理想的なケースでは、ホストがGPUに十分な数のカーネルをエンキューするため、GPUは、ホストがより多くのカーネルをエンキューするのを待つのではなく、ほとんどの時間を実行に費やします。

プロファイラの概要ページGPUが原因打ち上げカーネルへのホスト上で待機するアイドルだったどのくらいの時間TensorBoardショーに。下の画像では、GPUは、カーネルの起動を待機しているステップ時間の約10%の間アイドル状態です。

image

トレースビューア、この同じプログラム番組ホストはGPU上で忙しい起動カーネルであるカーネル間の小さなギャップのために。

image

GPUで多くの小さな操作(たとえば、スカラー加算など)を起動すると、ホストがGPUに追いついていない可能性があります。 TensorFlow統計2.77秒を取って同じプロファイルショー126224のムル操作のためのTensorBoardでツール。したがって、各カーネルは約21.9μsであり、これは非常に小さく(起動待ち時間とほぼ同じ時間)、ホストカーネルの起動遅延が発生する可能性があります。

image

あなたの場合は、トレースビューア番組上の画像のようにGPU上のオペレーションの間には多くの小さなギャップ、次のことができます。

  • 小さなテンソルを連結し、ベクトル化されたopsを使用するか、より大きなバッチサイズを使用して、起動された各カーネルにより多くの作業を実行させます。これにより、GPUがより長くビジー状態になります。
  • あなたが使用していることを確認しtf.functionあなたは純粋な熱心なモードでのオペレーションを実行していないのでこと、TensorFlowグラフを作成します。使用している場合はModel.fit (などとカスタムトレーニングループに反対tf.GradientTape )、そしてtf.keras.Model.compile自動的にこれを行います。
  • ヒューズでXLAを使用してカーネルtf.function(jit_compile=True)または自動クラスタリング。詳細については、に行く混合精度を有効にして、XLA高いパフォーマンスを得るためにXLAを有効にする方法については、以下のセクション。この機能により、デバイスの使用率が高くなる可能性があります。
2. TensorFlowopの配置

プロファイラ概要ページショーあなたのデバイス対ホストに配置OPSの割合(あなたもを見ることで、特定のOPSの配置を確認することができ、トレースビューア。同様に下の画像では、ホスト上のOPSの割合が欲しいですデバイスに比べて非常に小さいこと。

image

理想的には、計算集約型の操作のほとんどはGPUに配置する必要があります。

モデル内の操作とテンソルがセットに割り当てられているデバイスを調べるにはtf.debugging.set_log_device_placement(True)プログラムの最初のステートメントとして。

いくつかのケースでは、あなたが特定のデバイスに配置するオペアンプを指定した場合でも、その実装は、この条件(例:オーバーライドする場合がありますtf.unique )。でも、シングルGPUの訓練のために、のような流通戦略、指定tf.distribute.OneDeviceStrategy 、あなたのデバイス上のOPSのより確定的配置をもたらす可能性があります。

オペレーションの大部分をGPUに配置する理由の1つは、ホストとデバイス間の過剰なメモリコピーを防ぐためです(ホストとデバイス間のモデル入出力データのメモリコピーが必要です)。過剰なコピーの一例は、GPU上で以下のトレース図に示されている#167、#168、及び#169をストリーム。

image

これらのコピーは、GPUカーネルの実行をブロックすると、パフォーマンスが低下する場合があります。メモリコピー操作のトレースビューアは、これらのコピーされたテンソルの源であるOPSに関する詳細な情報を持っているが、それは常にオペアンプでmemCopyを関連付けることは容易ではないかもしれません。このような場合は、近くのopsを調べて、すべてのステップでメモリコピーが同じ場所で発生するかどうかを確認すると便利です。

3.GPU上のより効率的なカーネル

プログラムのGPU使用率が許容範囲内になったら、次のステップは、Tensorコアを利用するかopsを融合することにより、GPUカーネルの効率を高めることを検討することです。

1.テンソルコアを利用する

現代のNVIDIA®GPUは特化してテンソルコア大幅に適格なカーネルのパフォーマンスを向上させることができます。

あなたはTensorBoardの使用できるGPUカーネルの統計をテンソルコア適格であり、カーネルはテンソルコアを使用しているどのGPUカーネル視覚化します。有効fp16 (以下ミックス精密セクションの有効化を参照してください)あなたのプログラムの一般的な行列の乗算(GEMM)のカーネル(MATMUL OPS)はテンソルコアを利用させるための一つの方法です。精度がFP16であり、入力/出力テンソルの寸法は(8または16で割り切れるである場合GPUカーネルを効率的テンソルコアを使用int8 )。

GPUのためのカーネルが効率化する方法についての他の詳細な推奨事項については、を参照してくださいNVIDIA®深い学習パフォーマンスガイド。

2.ヒューズ操作

使用tf.function(jit_compile=True)パフォーマンスの大幅な向上につながる大きなカーネルを形成するために、小さなOPSを融合させます。もっと知るには、を参照してくださいXLAのガイド。

3.混合精度とXLAを有効にします

上記の手順を実行した後、混合精度とXLAを有効にすることは、パフォーマンスをさらに向上させるために実行できる2つのオプションの手順です。推奨されるアプローチは、それらを1つずつ有効にして、パフォーマンス上の利点が期待どおりであることを確認することです。

1.混合精度を有効にする

TensorFlow混合精度のガイド番組を有効にする方法fp16 GPU上での精度を。有効AMPをテンソルコアを使用し、単に使用と比較して3倍の全体的なスピードアップまでを実現するNVIDIA®GPU上でfp32ボルタおよびそれ以降のGPUアーキテクチャ上(のfloat32)の精度を。

行列/テンソルの次元が、テンソルコアを使用するカーネルを呼び出すための要件を満たしていることを確認してください。 GPUカーネルは、精度がfp16で、入出力次元が8または16(int8の場合)で割り切れる場合に、Tensorコアを効率的に使用します。

cuDNN v7.6.3以降では、Tensorコアを活用するために、必要に応じて畳み込み次元が自動的に埋め込まれることに注意してください。

パフォーマンス上の利点最大化するには、以下のベストプラクティスに従ってくださいfp16精度を。

1.最適なfp16カーネルを使用します

fp16有効になって、あなたのプログラムの行列乗算(GEMM)のカーネルは、対応する使用する必要がありますfp16テンソルコアを利用したバージョンを。しかし、いくつかのケースでは、これは発生しません、あなたは可能から期待されるスピードアップを経験していないfp16あなたのプログラムが代わりに戻って非効率的な実装に下がると、。

image

GPUカーネルOPSはテンソルコア適格とカーネルが実際に効率的なテンソルのコアを使用しています統計ページを示しています。深い学習パフォーマンス上のNVIDIA®ガイドはテンソルコアを活用する方法についての追加の提案が含まれています。また、使用することの利点fp16今OPSは半分の時間がかかるだろうとしても、以前にバインドされたメモリだったのカーネルに表示されます。

2.動的損失と静的損失のスケーリング

使用する際に損失のスケーリングが必要であるfp16伴う低精度にアンダーフローを防止します。詳細に説明されている両方とも損失スケーリング、動的および静的の2種類が存在する混合精密ガイド。あなたは使用することができますmixed_float16自動的Kerasオプティマイザ内の損失のスケーリングを有効にするポリシーを。

パフォーマンスを最適化しようとするとき、動的損失スケーリングはホスト上で実行される追加の条件付き操作を導入し、トレースビューアーのステップ間に表示されるギャップにつながる可能性があることを覚えておくことが重要です。一方、静的損失スケーリングにはそのようなオーバーヘッドがなく、正しい静的損失スケール値を指定する必要があるキャッチのパフォーマンスの観点から、より良いオプションになる可能性があります。

2. tf.function(jit_compile = True)または自動クラスタリングを使用してXLAを有効にします

単一のGPUで最高のパフォーマンスを得る最後のステップとして、XLAを有効にして実験することができます。これにより、操作が融合され、デバイスの使用率が向上し、メモリフットプリントが削減されます。あなたのプログラムでXLAを有効にする方法の詳細についてはtf.function(jit_compile=True)を参照してください、または自動クラスタリングXLAのガイド。

あなたは、グローバルJITレベルを設定することができます-1 (オフ)、 1 、または2 。レベルが高いほど攻撃的であり、並列処理が減り、より多くのメモリを使用する可能性があります。値を設定して1あなたは、メモリの制限がある場合。 XLAコンパイラは、新しい形状に遭遇するたびにカーネルをコンパイルし続ける必要があるため、XLAは可変入力テンソル形状を持つモデルではうまく機能しないことに注意してください。

2.マルチGPUシングルホストのパフォーマンスを最適化します

tf.distribute.MirroredStrategy APIは、単一のホスト上で複数のGPUへの1つのGPUからスケールモデルのトレーニングに使用することができます。 (参照、TensorFlowと分散型のトレーニングを行う方法について詳しく知りたいTensorFlowによる分散訓練GPUを使用して、使用するTPUのガイドとKerasを持つ分散型トレーニングチュートリアル。)

1つのGPUから複数のGPUへの移行は、理想的には箱から出してスケーラブルである必要がありますが、パフォーマンスの問題が発生する場合があります。

単一のGPUを使用したトレーニングから同じホスト上の複数のGPUに移行する場合、理想的には、勾配通信の追加のオーバーヘッドとホストスレッドの使用率の増加のみでパフォーマンスのスケーリングを体験する必要があります。このオーバーヘッドのため、たとえば1から2 GPUに移動した場合、正確に2倍のスピードアップは得られません。

以下のトレースビューは、複数のGPUでトレーニングする場合の追加の通信オーバーヘッドの例を示しています。グラデーションを連結し、レプリカ間で通信し、重みの更新を行う前に分割するためのオーバーヘッドがあります。

image

次のチェックリストは、マルチGPUシナリオでパフォーマンスを最適化するときにパフォーマンスを向上させるのに役立ちます。

  1. バッチサイズを最大化するようにしてください。これにより、デバイスの使用率が高くなり、複数のGPU間の通信コストが償却されます。使用メモリプロファイラは、あなたのプログラムがピークメモリ使用率にどれだけ近いかの感覚を得ることができます。バッチサイズを大きくすると収束に影響を与える可能性がありますが、通常、これはパフォーマンス上の利点よりも重要であることに注意してください。
  2. 単一のGPUから複数のGPUに移行する場合、同じホストでより多くの入力データを処理する必要があります。したがって、(1)の後で、入力パイプラインのパフォーマンスを再チェックし、ボトルネックでないことを確認することをお勧めします。
  3. プログラムのトレースビューでGPUタイムラインをチェックして、不要なAllReduce呼び出しがないか確認します。これにより、すべてのデバイス間で同期が行われます。上に示したトレースビューで、AllReduceを介して行われNCCLのカーネル、各ステップにおけるグラデーションの各GPU上でだけNCCLコールがあります。
  4. 最小化できる不要なD2H、H2D、およびD2Dコピー操作を確認します。
  5. ステップ時間をチェックして、各レプリカが同じ作業を行っていることを確認します。例えば、それが起こることができ、1つのGPU(通常は、 GPU0 、ホストが誤ってそれをより多くの仕事を入れ終わるので)オーバーサブスクライブされています。
  6. 最後に、トレースビューですべてのGPUのトレーニング手順を確認して、順番に実行されている操作を確認します。これは通常、プログラムに1つのGPUから別のGPUへの制御の依存関係が含まれている場合に発生します。これまで、この状況でのパフォーマンスのデバッグは、ケースバイケースで解決されてきました。あなたのプログラムの中で、この動作を観察する場合は、 GitHubの問題を提出あなたのトレースビューのイメージで。

1.グラデーションAllReduceを最適化します

同期戦略でトレーニングする場合、各デバイスは入力データの一部を受け取ります。

モデルを通過する順方向および逆方向のパスを計算した後、各デバイスで計算された勾配を集計して縮小する必要があります。この勾配AllReduceは、各デバイス上の勾配計算の後に起こる、とオプティマイザ前モデルの重みを更新します。

各GPUは、まず、モデルの層を横切って勾配を連結使用のGPUを横切ってそれらを通信tf.distribute.CrossDeviceOpstf.distribute.NcclAllReduceデフォルトである)、次いで層あたりの還元後勾配を返します。

オプティマイザーは、これらの減少した勾配を使用して、モデルの重みを更新します。理想的には、オーバーヘッドを防ぐために、このプロセスはすべてのGPUで同時に実行する必要があります。

AllReduceまでの時間は、ほぼ同じである必要があります。

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

この計算は、分散トレーニングジョブを実行したときのパフォーマンスが期待どおりであるかどうか、またはさらにパフォーマンスのデバッグを行う必要があるかどうかをすばやく確認するのに役立ちます。あなたはからあなたのモデル内のパラメータの数を取得することができModel.summary

各モデルパラメータがTensorFlowを使用するので、サイズが4バイトであることに留意されたいfp32勾配を通信する(のfloat32を)。あなたが持っている場合でも、 fp16有効になって、NCCL AllReduceは利用fp32パラメータを。

スケーリングのメリットを得るには、これらのオーバーヘッドと比較してステップタイムをはるかに長くする必要があります。これを実現する1つの方法は、バッチサイズがステップ時間に影響を与えるが、通信オーバーヘッドには影響を与えないため、より高いバッチサイズを使用することです。

2.GPUホストスレッドの競合

複数のGPUを実行する場合、CPUの仕事は、デバイス間でGPUカーネルを効率的に起動することにより、すべてのデバイスをビジー状態に保つことです。

ただし、CPUが1つのGPUでスケジュールできる独立した操作が多数ある場合、CPUは、ホストスレッドを多数使用して、1つのGPUをビジー状態に保ち、別のGPUでカーネルを非決定的な順序で起動することを決定できます。 。これにより、スキューまたは負のスケーリングが発生し、パフォーマンスに悪影響を与える可能性があります。

トレースビューア非効率的にショーオーバーヘッドCPUずらすGPUカーネルが起動以下、としてGPU1アイドル状態で、その後の後OPSの実行を開始しGPU2開始されました。

image

ホストが上でカーネルを起動しているホストのショーのためのトレースビューGPU2でそれらを起動する前に、 GPU1 (以下ことに注意してくださいtf_Compute* OPSは、CPUスレッドを示すものではありません)。

image

プログラムのトレースビューでこのようなGPUカーネルのずれが発生した場合、推奨されるアクションは次のとおりです。

  • TensorFlow環境変数を設定しTF_GPU_THREAD_MODEするgpu_private 。この環境変数は、GPUのスレッドをプライベートに保つようにホストに指示します。
  • デフォルトによって、 TF_GPU_THREAD_MODE=gpu_privateほとんどの場合に十分である、2スレッドの数を設定します。しかし、その数はTensorFlow環境変数の設定によって変更することができるTF_GPU_THREAD_COUNTスレッドの所望の数に。