このセクションでは、ベクトル・プログラミングの一般的な問題を簡単な例を用いていくつか紹介します。
この例はベクトルのコピー操作を行うループですが、コンパイラーが DEST(A(I)) と DEST(B(I)) の相違を証明できないため、ベクトル化は行われません。
例: 相違を証明できないためにベクトル化不可能なコピー |
---|
SUBROUTINE VEC_COPY(DEST,A,B,LEN) DIMENSION DEST(*) INTEGER A(*), B(*) INTEGER LEN, I DO I=1,LEN DEST(A(I)) = DEST(B(I)) END DO RETURN END |
16 バイト (Linux* および Mac OS* X) または 64 バイト (Windows*) 以上のデータ構造体または配列は、ベースアドレスが 16 (Linux および Mac OS X) または 32 (Windows) の倍数になるように、各構造体要素または配列要素の先頭をアライメントしなければなりません。
下の図は、間違ってアライメントされたデータが原因でデータ・キャッシュ・ユニット (DCU) が分割した場合の影響を示したものです。アライメントの合っていないデータをロードすると 16 バイト境界にまたがるため、メモリーアクセスが 1 回余分に発生し、その結果、6 ~ 12 サイクルのストールが発生します。データがアライメントされていることがわかっており、このアライメントを使用するよう指定すれば、このストールを回避できます。
ベクトル化の実行後、下の図に示すように、ループが実行されます。
要素 A(1) と B(1) が両方とも 16 バイトでアライメントされている場合、ベクトル反復 A(1:4) = B(1:4); と A(5:8) = B(5:8); はどちらも、アライメントされた移動によって実現できます。
不正なアライメント・オプションでベクトライザーを使用すると、コンパイラーの動作は予測がつかなくなります。特に、アライメントの合っていないデータを、アライメントの合っている移動命令で処理すると、不正命令例外が発生します。
コンパイラーは、コンパイル時にデータ構造のアライメントが不明な場合に備えて、いくつかのアライメント手法を用意しています。簡単な例を下に示します (これ以外にもいくつかの手法がサポートされています)。次の例のループにおいて、A のアライメントが不明な場合、コンパイラーはプレリュード・ループを生成し、ほとんどの場合に発生する配列参照がアライメントされたアドレスにヒットするまでループを繰り返します。これにより、A のアライメント・プロパティーが判明し、そのプロパティーに応じてベクトルループが最適化されます。この場合、ベクトライザーはインテル® Fortran 特有の機能であるダイナミックなループピーリングを実行します。
例: 元のループ |
---|
SUBROUTINE SIMLOOP(A) REAL A(100) ! alignment of argument A is unknown DO I = 1, 100 A(I) = A(I) + 1.0 ENDDO END SUBROUTINE |
例: データのアライメント |
---|
! The vectorizer applies dynamic loop peeling as follows: SUBROUTINE SIMLOOP(A) REAL A(100) ! let P be (A%16)where A is address of A(1) IF (P .NE. 0) THEN P = (16 - P)/4 ! determine run-time peeling factor DO I = 1, P A(I) = A(I) + 1.0 ENDDO ENDIF ! Now this loop starts at a 16-byte boundary, and will be ! vectorized accordingly DO I = P + 1, 100 A(I) = A(I) + 1.0 ENDDO END SUBROUTINE |