このセクションでは、ベクトル・プログラミングの一般的な問題を簡単な例を用いていくつか紹介します。
この例はベクトルのコピー操作を行うループですが、コンパイラが 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 バイト以上のデータ構造体または配列は、ベースアドレスが 16 の倍数になるように、各構造体要素または配列要素の先頭をアライメントしなければなりません。
「16 バイト境界をまたがる間違ってアライメントされたデータ」(下図) は、間違ってアライメントされたデータが原因でデータ・キャッシュ・ユニット (DCU) が分割した場合の影響を示したものです。アライメントの合っていないデータをロードすると 16 バイト境界にまたがるため、メモリアクセスが 1 回余分に発生し、その結果、6〜12 サイクルのストールが発生します。データがアライメントされていることがわかっており、このアライメントを使用するよう指定すれば、このストールを回避できます。
16 バイト境界にまたがる間違ってアライメントされたデータ |
ベクトル化の実行後、下の図に示すように、ループが実行されます。
ベクトルおよびスカラのクリーンアップ反復処理 |
要素 A(1) と B(1) が両方とも 16 バイトでアライメントされている場合、ベクトル反復 A(1:4) = B(1:4); と A(5:8) = B(5:8); はどちらも、アライメントされた移動によって実現できます。
注不正なアライメント・オプションでベクトライザを指定すると、コンパイラの動作は予測がつかなくなります。
特に、アライメントの合っていないデータに対してアライメントされた移動を使用すると、不正命令例外が発生します。
コンパイラは、コンパイル時にデータ構造のアライメントがわからない場合に備えて、いくつかのアライメント手法を持っています。簡単な例を下に示します (これ以外にもいくつかの手法がサポートされています)。次の例のループにおいて、A のアライメントがわかっていない場合、コンパイラはプレリュード・ループを生成し、ほとんどの場合に発生する配列参照がアライメントされたアドレスにヒットするまでループを繰返します。これにより、A のアライメント・プロパティが判明し、そのプロパティに応じてベクトルループが最適化されます。この場合、ベクトライザはインテル® Fortran 特有の機能であるダイナミックなループ・ピーリングを実行します。
データのアライメントの例:
元のループ
SUBROUTINE DOIT(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 will apply dynamic loop peeling as follows:
SUBROUTINE DOIT(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