このセクションでは、スカラーおよびベクター プロセッシング要素のカーネル プログラミングの重要な要素について、概要を説明します。各要素および最適化手法の詳細は、その後のセクションで説明します。
次の例は、スカラー エンジンのみを使用しています。512 個の int32 要素を反復する for ループを示します。各ループ反復は、int32 a と int32 b を乗算を 1 回実行し、結果を c に格納して、出力バッファーに書き込みます。scalar_mul カーネルは、データ input_buffer<int32>
の 2 つの入力ブロック (バッファー) に演算を実行し、データ output_buffer<int32>
の出力バッファーを生成します。
バッファーへのアクセスには、スカラー反復子とベクター反復子が使用されます。バッファー API の詳細は、ストリーミング データ API を参照してください。
void scalar_mul(input_buffer<int32> & data1,
input_buffer<int32> & data2,
output_buffer<int32> & out){
auto pin1 = aie::begin(data1);
auto pin2 = aie::begin(data2);
auto pout = aie::begin(out);
for(int i=0;i<512;i++)
{
int32 a=*pin1++;
int32 b=*pin2++;
int32 c=a*b;
*pout++ = c;
}
}
次の例は、同じカーネルのベクター バージョンです。
void vect_mul(input_buffer<int32> & __restrict data1,
input_buffer<int32> & __restrict data2,
output_buffer<int32> & __restrict out){
auto pin1 = aie::begin_vector<8>(data1);
auto pin2 = aie::begin_vector<8>(data2);
auto pout = aie::begin_vector<8>(out);
for(int i=0;i<64;i++)
chess_prepare_for_pipelining
{
aie::vector<int32,8> va=*pin1++;
aie::vector<int32,8> vb=*pin2++;
aie::accum<acc80,8> vt=mul(va,vb);
aie::vector<int32,8> vc=srs(vt,0);
*pout++ = vc;
}
}
上記のコードでは、データ型 vector<int32,8>
および accum<acc80,8>
が使用されています。バッファー API begin_vector<8>
は 8 個の int32 ベクターに対して反復処理を実行する反復子を返し、変数 va
および vb
に格納します。これらの 2 つの変数はベクター型変数であり、組み込み関数 mul
に渡され、accum<acc80,8>
型の vt
が生成されます。accum<acc80,8>
型は、vector<int32,8>
型変数 vc を返して出力ウィンドウに書き込むシフト/丸め/飽和関数
srs
によってリダクションされます。AI エンジンでサポートされるデータ型の追加の詳細は、この後のセクションで説明します。
vect_mul
関数の入力パラメーターと出力パラメーターに __restrict
キーワードを使用すると、データの独立性が明示的に示され、コンパイラで最適化をより積極的に実行できます。
chess_prepare_for_pipelining
は、カーネル コンパイラでループの最適化パイプラインが実現されるよう指示するコンパイラ プラグマです。
この関数例のスカラー バージョンは 1055 サイクルかかりますが、ベクター バージョンは 99 サイクルしかかかりません。カーネルのベクター バージョンを使用すると、10 倍以上高速になります。ベクター プロセッシング自体は、int32 乗算の 8 倍のスループットを達成しますが、レイテンシが高く、全体的なスループットは 8 倍になりません。ループ最適化を使用すると、10 倍近くになります。この後のセクションでは、使用可能なさまざまなデータ型、使用可能なレジスタ、ループでのソフトウェアのパイプライン処理などの概念や __restrict
などのキーワード使用して AI エンジンで実現可能な最適化について詳しく説明します。