インテル® クイック・シンク・ビデオのパフォーマンスの最適化(第2回)

インテル® GPAインテル® System Studio

第1回 第2回 第3回

インテル® GPA によるメディア・トレース・キャプチャー

前回は、インテル® GPA 4.1のメディア・パフォーマンス・アナライザー機能を利用して、2つの異なる構成の H.264/AVC エンコード・サンプル・アプリケーションの実行の様子をリアルタイム・モニター上で確認した。今一度アプリケーションの構成と測定結果をまとめておこう。

本稿で利用するサンプル・アプリケーションの構成 測定結果
構成1 EncodeFrameAsync API とSyncOperation APIをフレーム単位で同期して実行 GPU全般の利用率: 68% 

エンコード速度: 約41 FPS

構成2 フレームプールを利用し、常に3枚先行して EncodeFrameAsync API を実行 GPU全般の利用率: 98% 

エンコード速度: 約108FPS

ここでは、パフォーマンスが問題となっていると思われる構成1のアプリケーションを、さらにトレース・キャプチャー機能を利用して詳細に掘り下げてみよう。まずアプリケーションを実行できる状態にした上で、[Media Performance] ウィンドウ上で、[Open trace after capture] をチェックしておく。キャプチャー時間は、アプリケーションがエンコードするフレーム数にもよるが、数十フレームもキャプチャーできれば十分なので、2000ms ほどに設定しておけばよいだろう。

なお、このトレース・キャプチャー機能はシステムワイドで機能するため、実行状態にあるすべてのスレッドをキャプチャーしてしまう。そのため、あまり長い実行時間を設定すると後の GUI の動作が極めて重くなるので注意しよう。準備が完了したら、[Media Performance] ウィンドウの [Capture] ボタンをクリックし、直後にアプリケーションを実行してみよう。現状、アプリケーションの実行直後にトレース・キャプチャーを自動的に開始するような機能が提供されていないため、手動で両者を実行しなければならないのが手間ではあるが、それでも、メディア・ハードウェア・アクセラレーションのプロファイリングの目的には十分な機能を提供する。

[Capture] ボタンを押した後、設定したキャプチャー時間が経過したら、自動的に図1のようなインテル® GPA プラットフォーム・アナライザーが起動するはずだ(なお、自動起動せずに、別途インテル® GPA プラットフォーム・アナライザーを起動し後からトレースを選択することも可能だ。)

図 1 Intel GPA Platform Analyzer

インテル® GPA プラットフォーム・アナライザー上では、キャプチャーされた各スレッドの実行状態が視覚的に表示されていることが分かるだろう。ここでは水平にスレッドの実行状態を視覚化している部分をトラックと呼ぶ。前述のようにシステム全般のキャプチャーであるため、多くのスレッド(トラック)が生成されてしまうが、メディア・アクセラレーションのプロファイリングに重要なトラックはそう多くない。具体的には、対象アプリケーションのプロセス内にある、MFX_EncodeFrameAsync および MFX_SyncOperation を含むトラックと Enocode Submit および Encode_Query を含むトラック、および、GPU:ENCODE と表示されている2つのトラックの合計4つのトラックである。

なお、インテル® GPA プラットフォーム・アナライザーは、マウスホイールまたは”-“、”=” キーでズームイン・アウトできるほか、各トラックを非表示にしたり、トラックの垂直位置をドラッグで移動できる。こうしてトラックを整理したのが図2である。また、トラック上で、詳細に見たい部分をクリックすると、右上の [Top Level Task Statistics] ペインに実行時間などの統計情報が、右下の [Summary] ペインに関数名などの情報が表示される。実行時間が短すぎて画面上で確認しづらい場合などは、この方法で確認するのが有効である(図2では MFX_SyncOperation が選択されている)。

図 2 Intel GPA Platform Analyzerのトラックを整理したところ

ハードウェア・アクセラレーションによるエンコードの詳細

それでは、ハードウェア・アクセラレーションによるエンコードの様子を詳細に解析してみよう。ズームインして1フレーム分を中心に配置したのが図3である。

図 3 H.264/AVCエンコードにおける1フレーム分の処理

MFX_SyncOperation を含む最初のトラックは、アプリケーション内で実行された MSDK の API にそのまま対応する。そこで、これを便宜上アプリケーション・トラックと呼ぶことにしよう。続く Encode Submit  を含むトラックはアプリケーションにリンクされた MSDK のライブラリーの実行の様子を表す。ここではこれを MSDK トラックと呼ぶことにする。GPU:ENCODE と表示されている2つのトラックは、実際の GPU のエンコード・ハードウェア・アクセラレーションの動きを表し、この2つのトラックがもっとも重要なハードウェア・アクセラレーションの実体である。

以下、それぞれのトラックを見ていこう。

アプリケーション・トラック

アプリケーション・トラック上では、同期実行する構成1のサンプル・アプリケーションの場合は、その実行状態を表す横棒が1フレーム分のエンコードを表していると考えてよい。画面上ではほとんどわからないが、MFX_SyncOperation だけでなく、その直前に、MFX_EncodeFrameAsync が実行されており、アプリケーションの実行と対応している。

参考までに、統計情報を確認すると、MFX_EncodeFrameAsync が0.0237ms、その後、MFX_SyncOperation に20.7505msかかっていることが分かる。つまり、アプリケーションはハードウェアによるエンコードが終了するまでずっとウエイトしているということである。このような同期実行は、ファイルの入出力やGPUへの入力フレームの転送などのレイテンシーを隠蔽できないので、アプリケーションの最適化上は望ましくないのはいうまでもない。しかし、解析ツール上で動作を解析する最初のステップとしては、トラック同士の関係が把握しやすいため、有用である。

MSDKトラック

“Encode Submit” を含むトラックが MSDK トラックで、アプリケーション・トラックの実行直後に動作が開始されているのがわかるだろう。アプリケーション・トラック上で、MSDK  の EncodeFrameAsync() API が呼ばれると、そこで引数として指定された入力フレームが GPU 側にコピーされ、その後、エンコードコマンドが発行され、GPUトラック上で、実際にエンコードが開始される。

図 4 MSDKトラック

ここでは、Encode Submit の下段に、その内部動作を表すサブトラックが表示されているのでそこに注目してみよう。実際の画面上ではサブトラック各部分の実行時間が短すぎて把握しにくいため、ここでは分かりやすくするために図式化してみた(図5)。さらにズームインしたり、クリックして選択するなどすれば、右下の[Summary] ペインで実行関数名が表示されるので参考にして確認するとよいだろう。

図 5 MSDKトラック上のEncode_Submitトラックの解析

Encode Submit 内のサブトラックは、まず LockFrame で始まり、1.5ms 程度開けて UnlockFrame、その後、DXVA2_BeginFrame、DXVA2_Execute、DXVA2_EndFrame と続いている。これらは大きく2つの内容に分類することができる。1つは、アプリケーションから引数として渡された入力フレームの GPU への転送であり、もう一つは実際のハードウェアに対するエンコードコマンドのセットアップ発行である。

LockFrame から UnlockFrame まででは、入力フレームが Lock され、GPU への転送完了後、そのメモリー領域が Unlock されている。もちろん、この Lock がされている間、アプリーションはこのメモリー領域を参照することは許されない。実際の入力フレームの転送そのものが空白になっているのは、その実行が CPU 上のインストラクションとしてCPUが主体となって行われるため、GPU 上でトレースされないためである。実際は、Lock および Unlock さえキャプチャーできれば、その間がフレーム転送に要する時間と考えてよく、解析には支障はないだろう。

この入力フレームの転送時間はテスト・アプリケーションでは1.5msほどで、これは入力フレームサイズ、言い換えれば転送されるメモリーの量に依存する。1920×1080 サイズの YUV フレームの場合は1.5ms 前後だが、より小さいサイズであればより速くなるはずである。その後の DXVA2_BeginFrame、DXVA2_Execute、DXVA2_EndFrame までが一連のエンコードコマンドの発行と考えてよい。DXVA2_Execute の時間は、処理するフレームがイントラピクチャーなのか、P/B  ピクチャーなのかにもより異なるが、相対的に長く見えるのは(とはいえ 0.2ms ほどであるが)、ここで実際にエンコードに必要なさまざま情報をドライバーが管理し、アタッチしているためである。必要となる参照ピクチャーの管理や、出力ビットストリームのメモリーや必要なサーフェスがここでアタッチされる。

2つのGPU:ENCODEトラックエンコード・ハードウェア・アクセラレーションの中核

図 6 2つのGPU:ENCODEトラック

そして、Encode Submit 内のコマンド発行に伴い、2つの GPU:ENCODE トラックが順次開始されているのがわかるだろう。前述のように、この2つのトラックが示しているのが GPU によるハードウェア・エンコードの実体である。2つのトラックに分かれているのは、異なる2つのハードウェア・ロジックを利用しているからである。時間的に先行するトラックは、動き補償予測を中心とする動きベクトルの検出やマクロブロックのモードの決定などを行い、続くトラックは、決定されたマクロブロックの情報や抽出された動きベクトルに基づくビットストリームの符号化処理を行っている。算術符号化のアクセラレーションはここに含まれる。ここでは便宜上、GPU:ENCODE の時間的に先行するトラックを動き補償予測トラック、それに続くトラックを符号化トラックと呼ぶことにしよう。これらのトラックの実行状態は、残念ながらアプリケーションからコントロールすることはできないが、より厳密にプロファイル結果をチェックするためには内容を理解しておかなければならない部分があるため、さらに詳しく説明してみよう。

動き補償予測トラック

動き補償予測トラックは、実際には、GPU の汎用実行エンジン上で実行されるプログラムで、これが適応的に GPU 上の動き保障予測ハードウェアのアクセラレーションを利用しながら実行される。アプリケーションからセットされる TargetUsage により処理内容が異なり、パフォーマンスも変わってくる。MFX_TARGETUSAGE_BALANCED モードでは4つのステージに処理が分割されていることを確認しておこう。また、GPU のターボ機能の On/Off で、パフォーマンスの影響を受ける。

符号化トラック

符号化トラックは、GPU 実行エンジンからは独立したビットストリーム符号化用のハードウェア上で実行される。GPU のターボ機能の影響は受けないが、独立したハードウェアであるため、実は前半の動き補償予測機能とは並列・独立に実行可能である。

インテル® GPA プラットフォーム・アナライザー上で、符号化トラックの動作が動き補償予測トラックの動作に続く形でシリアライズされているのは、動きベクトルやマクロブロックのタイプを確定しないと符号化の処理を実行できない、という論理的な依存関係があるからで、これはドライバーソフトウエアによる同期にすぎない。したがって、論理的な依存関係がなければ、ハードウェアとしては、動き補償予測と符号化は並列実行が可能である。やや細かい点であるが、あえてこの点に言及しているのは、これがパフォーマンス最適化上のポイントの一つであるからである。動き補償予測が複数のステージに分割されているのは、後続する符号化トラックと直接の依存関係がなく独立可能な処理を分離させ、パフォーマンスを向上させるためである。Encode の Stage1 および Stage2 までは、符号化モジュールとは並列に実行が可能である。エンコーダー・アクセラレーションを最大限に利用する場合には、これらの点にも留意するのが望ましいだろう。

Encode_Queryとエンコードの終了

そして、SyncOperation API が対応する同期ポイントで実行されていれば、MSDK ライブラリーによりEncode_Query が断続的に発行され、その際、符号化トラックがエンコードを完了していれば、エンコード完了のステータスが MSDK ライブラリーに報告される。その直後に、アプリケーションが指定した出力先のメモリー領域がロックされ、GPU から出力ストリームが転送され、転送完了後 Unlock される。

この終了の様子を拡大したのが図7である。厳密には、ハードウェアに実際に Query を発行しているのは、DXVA2_Execute で(スタータス確認モードで実行している)、実際のビットストリームは Query にかかわらずドライバーによりサーフェスに書き出されている。ここでいう書き戻しは、ドライバーがサーフェスに出力したストリームを、サーフェスからアプリケーション側に転送することを意味する。

図 7 エンコード終了の様子を拡大したところ

なお、この書き戻しは、入力フレームの転送に比べて遥かに高速である(図7上で、書き戻し時間は0.02ms 程度)。これは圧縮後のビットストリームであるためサイズが小さいということもあるが、MSDK のライブラリーレベルで、MOVNTDQA および MFENCE インストラクションによって転送が最適化されているという点も大きい。通常 GPU によるエンコードは、入力フレームの GPU への転送と出力ストリームの GPU からの書き戻しのレイテンシーをいかに隠蔽してスループットを向上させるか、という点に力点がおかれるが、Sandy Bridge では、書き戻しのオーバーヘッドは極めて小さく、むしろ入力フレームの転送のレイテンシーの隠蔽に焦点をあてればよい。

以上を前提に次稿ではパフォーマンス最適化上のポイントをまとめていこう。

著者紹介

岩本 成文 氏

Software Engineer, Software and Services Group, Intel Corporation

タイトルとURLをコピーしました