カーネルのキュー追加のオーバーヘッドの削減 - 2022.1 日本語

Vitis 統合ソフトウェア プラットフォームの資料: アプリケーション アクセラレーション開発 (UG1393)

Document ID
UG1393
Release Date
2022-05-25
Version
2022.1 日本語

OpenCL API 実行モデルでは、データ並列とタスク並列のプログラミング モデルがサポートされます。OpenCL ホストは通常、異なるカーネルを複数回呼び出す必要があります。これらの呼び出しは、特定のシーケンスまたは順不同コマンド キューのいずれかでコマンド キューに入れられます。このあと、計算リソースとタスク データがどれだけ使用可能かによって、デバイス上での実行がスケジュールされます。

カーネル呼び出しは、clEnqueueTask を使用してコマンド キューで実行されるようにキューに入ります。送信プロセスがホスト プロセッサで実行されます。送信元が、カーネル引数をデバイス上で実行されているアクセラレータに転送した後、カーネル実行を呼び出します。送信元は、下位レベルのザイリンクス ランタイム (XRT) ライブラリを使用して、カーネル引数を転送し、計算を開始するためのトリガー コマンドを発行します。アクセラレータへのコマンドおよび引数の送信のオーバーヘッドは、カーネルの引数セットの数によって 30 µs ~ 60 µs になります。このオーバーヘッドの影響は、カーネルを実行する必要のある回数と clEnqueueTask への呼び出しを最低限に抑えると減らすことができます。理想的なのは、すべての計算が clEnqueueTask の呼び出し 1 つで終了するようにすることです。

データをバッチ処理してカーネルを 1 回呼び出すと、clEnqueueTask への呼び出しを最小限に抑えることができます。ループは元のインプリメンテーションにラップされ、複数のエンキュー呼び出しのオーバーヘッドを回避できます。また、多数の小さなデータ パケットではなく、少数の大きなデータ パケットを転送することで、ホストとアクセラレータ間のデータ転送パフォーマンスを向上させることもできます。カーネル実行のオーバーヘッド削減の詳細は、カーネル実行 を参照してください。

次の例は、指定された作業またはデータサイズを処理する単純なカーネルを示しています。
#define SIZE 256
extern "C" {
    void add(int *a , int *b, int inc){
        int buff_a[SIZE];
        for(int i=0;i<size;i++)
        {
            buff_a[i] = a[i];
        }
        for(int i=0;i<size;i++)
        {
            b[i] = a[i]+inc;
        }
    }
}
次の例は、バッチ データを処理するように最適化された同じ単純なカーネルを示しています。num_batches 引数によっては、カーネルは 1 回の呼び出しで 256 のサイズの入力を複数処理し、複数の clEnqueueTask 呼び出しのオーバーヘッドを回避できます。ホスト アプリケーションは、データとバッファーを SIZE * num_batches のチャンク単位で割り当てるように変更し、メモリ割り当てとホスト グローバル メモリおよびデバイス メモリ間のデータ転送をバッチ処理します。
#define SIZE 256
extern "C" {
    void add(int *a , int *b, int inc, int num_batches){
        int buff_a[SIZE];
        for(int j=0;j<num_batches;j++)
        {
            for(int i=0;i<size;i++)
            {
                buff_a[i] = a[i];
            }
            for(int i=0;i<size;i++)
            {
                b[i] = a[i]+inc;
            }
       }   
    }
}