PAOS – パックド構造体配列

その他

この記事は、インテル® ソフトウェア・サイトに掲載されている「PAOS – Packed Array Of Structures」(http://software.intel.com/en-us/articles/paos-packed-array-of-structures/) の日本語参考訳です。


はじめに

多くのプログラマーは、SIMD (Single Instruction Multiple Data) と呼ばれる近年のプロセッサーの小さなベクトル演算機能についてはよくご存知でしょう。インテル® アーキテクチャー (IA-32、IA-64、インテル® 64) では、MMX® 以降、SSE、SSSE、SSE2、SSE3、AVX、AVX2 などで SIMD をサポートしています。SIMD の利点は、データの複数の部分で同じ単一命令を同時に実行できることです。SIMD 命令はさまざまなデータのサイズと型 (byte、word、dword、qword、(dqword)、float、double…) をサポートしますが、この記事では Fortran で REAL*8 (DOUBLE PRECISION) を操作する SIMD を使用します。

多くのプロセッサーは SSE2 または SSE3 をサポートしており、最近のインテル® Core™ i7 プロセッサー (開発コード名: Sandy Bridge) では AVX をサポートしています。また、現在開発段階のインテル® プロセッサー (開発コード名: Haswell) は AVX2 をサポートする予定です。プログラマーから見ると、主な違いは SIMD 命令で操作されるデータの幅 (複数データの数のことで、ベクトル長ともいいます) です。高水準言語のプログラマーは、各命令用の最適なコードパスの生成をコンパイラーに任せていると思いますが、SIMD の MD (Multiple Data – 複数のデータ) の幅について知っておくと、データをレイアウトするときに役立ちます。

サポートされている SIMD の幅は次のとおりです。

SSEn 2 要素幅 double (2 x 64 ビット)

AVX 4 要素幅 double (4 x 64 ビット)

AVX2 4 要素幅 double (4 x 64 ビット)

MIC 8 要素幅 double (8 x 64 ビット) (インテル® Xeon Phi™)

プロセッサーの SIMD 機能を活用するには、プログラマーがアプリケーションで使用するデータレイアウトを適切に設計する必要があります。同じ操作で処理するデータがメモリーで隣接しているレイアウトが理想的です。データ配列を使用する場合、プログラマーは多次元配列の配列インデックスの入れ子レベル以外はプログラミングについて心配する必要はありません。この記事では、単一要素の配列以外のデータに関係するプログラミング問題を取り上げます。

これまで、多くの技術文献では、データレイアウトの手法として、次の 2 つのプログラミング手法が議論されてきました。

AOS 構造体配列 (Array of Structure)

SOA 配列構造体 (Structure of Array)

この記事では、AOS と SOA のハイブリッドである、3 番目の手法を紹介します。

PAOS パックド構造体配列 (Packed Array of Structure)

このプログラミング手法は、次のように呼ぶこともできます。

PSOA 分割配列構造体 (Partitioned Structure of Array)

プログラマーは、開発中に構造体配列 (AOS) を好む傾向があります。これは、デバッグの容易性によるものです。AOS では、構造体への単一ポインターまたは参照をデバッガーで提供することにより、プログラマーは構造体のコンテンツ全体を調査できます。これにより、デバッグ中にプログラムのフローを簡単にたどれるので便利です。

デバッグがほぼ完了すると、プログラマーは配列構造体 (SOA) のパフォーマンスが優れているかどうかを評価できます。動作するコードを変更することになるため、AOS から SOA への変換を行うべきかどうか判断することは難しいでしょう。AOS は、構造体内の操作に際してキャッシュの局所性が優れています。SOA は、構造体間の操作におけるキャッシュの局所性が優れています。データを操作するアプリケーションの大部分は AOS でも SOA でもなく、変換によるパフォーマンスへの影響を予測することは困難です。

この記事では FORTRAN のサンプルコードを使用していることに注意してください。C++ のテンプレートを使用して C++ プログラムを PAOS に適応させるほうが簡単です。

FORTRAN (テンプレートなし) では、より多くの既存コードの変更が必要になります。

この記事で紹介しているサンプルコードは、主に物体に接続されたテザー (紐-ひも) に関する研究で使用されるビードモデル (bead model) 合成プログラムの一部です。このプログラムは、「テザーボール」システムをモデル化するために使用されるもので、ポールに紐で結ばれたバレーボールをイメージすると良いでしょう。

このプログラムは当初、スペースシャトルと長い (20 km の) 導電性テザーで接続された小さな衛星による SED 実験におけるテザー衛星のシミュレーション用に開発されました。シミュレーションでは、導電性テザーが地球磁場を移動すると誘導起電力が発生することが予測されました。最新バージョンのシミュレーション・プログラムは、宇宙 (軌道) エレベーター設計の研究に使用されています。宇宙エレベーターは、赤道上から上空に引き伸ばした約 10 万キロの自立式のテザー (軌道) を伝って電動式の昇降機を上下に移動させ、地上と宇宙間でペイロードを輸送するという構想です。

シミュレーション・プログラムでも使用されるテザーは、物体 (スペースシャトル、衛星、地球、その他) に相互に接続される一連の 1 次元のセグメントです。テザーは一連のばねとして見なすことができます。隣接するセグメントの接続点はビード (bead) と呼ばれます。ビードには、属性の位置、速度、質量、その他の変数 (大部分はフォースベクトル: 重力、外力、弾力、その他) が含まれます。セグメントには、変形前の長さ、変形後の長さ、長さの割合、長さの加速度、弾性部径、空気力学的径、抵抗率、ばね定数、ばね減衰定数、温度、その他の属性が含まれます。ボディーには多くの属性が含まれ、さまざまな物体 (テザー、ブーム、スラスター、その他) が接続されています。惑星、衛星 (月、小惑星、彗星) および宇宙船の初期情報の取得には、JPL (ジェット推進研究所) の天体暦データベースを使用しています。シミュレーターは FORTRAN で記述された包括的なモデリングシステムで、25 年以上にわたって開発されてきたものです。

AOS 形式、FORTRAN (C++ の struct)

type TypeFiniteSolution
    SEQUENCE
    ! REAL(8) variables
    ! INTEGRATION INTERVAL & LAPSED TIME OF FINITE TETHER SOLN
    real :: rDELTIS 	! Integration step size
    real :: rTISTIM 	! Integration time
…
    ! NOMINAL INSTANTANEOUS DEPLOYED (UNDEFORMED) TETHER LENGTH
    real :: rEL

    ! rate of deployment +out, -in through the X end
    real :: rELD

    ! rate of rate of deployment +out, -in through the X end
    real :: rELDD
…
    ! POSITION VECTOR BETWEEN ATT PTS (INER FRAME)
    real ::	rELI(3)

    ! DERIVATIVE OF POS VECTOR BETWEEN ATT PTS (INER FRAME)
    real ::	rELDI(3)

    ! 2ND DERIV OF POS VECTOR BETWEEN ATT PTS (INER FRAME)
    real ::	rELDDI(3)
…
    ! Position with respect to IP IF
    real, pointer :: rBeadIPIF(:,:)   	! (3,NumberOfBeads+2)

    ! Velocity with respect to IP IF
    real, pointer :: rBeadDIPIF(:,:)  	! (3,NumberOfBeads+2)

    ! Acceleration with respect to IP IF
    real, pointer :: rBeadDDIPIF(:,:) 	! (3,NumberOfBeads+2)
…
    ! SEGMENT LENGTH AND LENGTH RATE
    real, pointer :: rELBSG(:) 	! (NumberOfBeads+1)
    real, pointer :: rELBSGD(:)	! (NumberOfBeads+1)
…
end type TypeFiniteSolution

次のメモリーレイアウトを見てください。

緑のバーは 64 バイト (8 double) の 1 つのキャッシュラインを表し、青のバーは隣接するキャッシュラインを表しています。最近のプロセッサーの多くは、2 つの隣接するキャッシュラインを同時にフェッチします。SSE を活用するようにレイアウトを再構成すると、メモリーレイアウトは次のようになりました。


最後の積分展開ステップのみ

図中のセルは最適化の可能性を表しています。2 つの隣接する類似したセルがボックスでグループ化された場合に限り、SSE ベクトル化の可能性があります。ビード位置の大きな配列 (rBeadIPIF) は、2 つの構成で示されています。最初のボックスグループは、ビードおよびビード間計算コードの主な部分を表しています。2 番目のボックスグループは、(dV * dT) で位置を進めることができる最後のループ最適化のみを表しています。

上記の図は、AOS 形式のベクトル化の可能性を示しています。

スカラーのベクトル化の可能性はない。
小さな次元のベクトル (3) (3,3) は半分のベクトル化の可能性がある。
大きなベクトル (3,nBeads) の主な部分は半分のベクトル化の可能性がある。
大きなベクトル (3,nBeads) の最後の積分は完全なベクトル化の可能性がある。

上記は仮定にすぎないため、コードサンプルを実際に調べることにしましょう。

単純なループ – AOS を使用したオイラー法

ビードの速度と積分ステップ区間に基づいてビードの位置を進める、積分プロセスの最終段階を調べます。このプロセスはオイラー法として知られています。

次のコードは、AOS に対する PAOS の最適化の可能性が最も少ない部分の 1 つであることに注意してください。

type TypeFiniteSolution
call EULINTxyz(DELTIS, NBEAD, rBeadDIPIF(1:3,1:NBEAD), rBeadIPIF(1:3,1:NBEAD))
...
subroutine EULINTxyz_r_n_r_r(dT, n, dX, X)
    implicit none
    real :: dT
    integer :: n
    real :: dX(1:3,1:n)
    real :: X(1:3,1:n)
    integer :: i,j

    do i=1,n
        do j=1,3
            X(j,i) = X(j,i) + dX(j,i) * dT
        end do
    end do
end subroutine EULINTxyz_r_n_r_r

(上記のコードはジェネリック・インターフェイス署名 “_r_n_r_r” を含んでいます)

オイラー法の AOS アセンブリー・コード

; parameter 1(DT): rcx
; parameter 2(N): rdx
; parameter 3(DX): r8
; parameter 4(X): r9
;;; recursive subroutine EULINTxyz_r_n_r_r(dT, n, dX, X)
        push      rbp				; save stack frame pointer
        sub       rsp, 48			; allocate stack space
        lea       rbp, QWORD PTR [32+rsp]	; set new stack frame pointer
        movsxd    rax, DWORD PTR [rdx]		; rax = n
        mov       QWORD PTR [rbp], rax		; temp0 = n (unnecessary)
        mov       QWORD PTR [8+rbp], rax		; temp1 = n (unnecessary)
;;;     implicit none
;;;     real :: dT
;;;     integer :: n
;;;     real :: dX(1:3,1:n)
;;;     real :: X(1:3,1:n)
;;;     integer :: i,j
;;;
;;;     do i=1,n
        movsxd    r10, DWORD PTR [rdx]		; r10 = n (could have obtained from rax)
        xor       edx, edx			; zero iteration count
        xor       eax, eax			; zero linear index into both arrays
        test      r10, r10			; if(n <= 0)
        jle       .B5.5         ; Prob 3%	;	goto .B5.5
.B5.2::
;;;         do j=1,3
;;;             X(j,i) = X(j,i) + dX(j,i) * dT

        vmovsd    xmm0, QWORD PTR [rcx]		; xmm0= dT (scalar)
        ALIGN     16				; align following code to 16 byte address
.B5.3::						: loop:
        vmulsd    xmm1, xmm0, QWORD PTR [rax+r8]		; xmm1 = dT * dX(index) (scalar)
        inc       rdx					; ++iteration count
        vmulsd    xmm3, xmm0, QWORD PTR [8+rax+r8]	; xmm3 = dT * dX(index+1) (scalar)
        vmulsd    xmm5, xmm0, QWORD PTR [16+rax+r8]	; xmm5 = dT * dX(index+2) (scalar)
        vaddsd    xmm2, xmm1, QWORD PTR [rax+r9]		; xmm2 = dT * dX(index) + X(index) (scalar)
        vaddsd    xmm4, xmm3, QWORD PTR [8+rax+r9] 	; xmm4 = dT * dX(index+1) + X(index+1) (scalar)
        vaddsd    xmm1, xmm5, QWORD PTR [16+rax+r9]	; xmm1 = dT * dX(index+2) + X(index+2) (scalar)
        vmovsd    QWORD PTR [rax+r9], xmm2		; X(index) = dT * dX(index) + X(index) (scalar)
        vmovsd    QWORD PTR [8+rax+r9], xmm4 		; X(index+1) = dT * dX(index+1) + X(index+1) (s)
        vmovsd    QWORD PTR [16+rax+r9], xmm1		; X(index+2) = dT * dX(index+2) + X(index+2) (s)
        add       rax, 24				; index += 3
        cmp       rdx, r10				; if(iteration count < n)
        jb        .B5.3         ; Prob 82%		;    goto loop
.B5.5::
;;;         end do
;;;     end do
;;; end subroutine EULINTxyz_r_n_r_r
        lea       rsp, QWORD PTR [16+rbp]	; restore stack pointer
        pop       rbp				; restore stack frame
        ret

コンパイラーにより生成されたコードに関する最適化サポート:

ループがアラインされました (OK)
内部ループと外部ループが融合されました (OK)
スタックに 2 つの不要な n が保存されました (NG)
計算はすべてスカラーです (最適ではない)

プログラマーは、TRANSFER を使用して (3,NBEAD) 2 次元配列を (3*NBEAD) 1 次元配列にレイアウトし直してこれらの問題に対処することもできます。ただし、テザーのビード (およびセグメント) 情報を処理するループをすべて書き直す必要があるため、プログラム内の非常に多くのループを変更することになります。この記事の目的は、コンパイラーに多くの作業をしてもらえるようなコードに変更することです。

SOA 形式

    ! REAL variables (set compiler option for default real == real(8)
    ! INTEGRATION INTERVAL & LAPSED TIME OF FINITE TETHER SOLN
    real, pointer :: rDELTIS(:)	! (NumberOfTethers)
    real, pointer :: rTISTIM(:)	! (NumberOfTethers)
…
    ! NOMINAL INSTANTANEOUS DEPLOYED (UNDEFORMED) TETHER LENGTH
    real, pointer :: rEL(:)	! (NumberOfTethers)

    ! rate of deployment +out, -in through the X end
    real, pointer :: rELD(:)	! (NumberOfTethers)

    ! rate of rate of deployment +out, -in through the X end
    real, pointer :: rELDD(:)	! (NumberOfTethers)
…
    ! POSITION VECTOR BETWEEN ATT PTS (INER FRAME)
    real, pointer ::	rELI(:,:)	! (3, NumberOfTethers)

    ! DERIVATIVE OF POS VECTOR BETWEEN ATT PTS (INER FRAME)
    real, pointer ::	rELDI(:,:)	! (3, NumberOfTethers)

    ! 2ND DERIV OF POS VECTOR BETWEEN ATT PTS (INER FRAME)
    real, pointer ::	rELDDI(:,:)	! (3, NumberOfTethers)
…
    ! Position with respect to IP IF
! *** note change in index order and number of indexes
    real, pointer :: rBeadIPIF(:,:,:) 	! (NumberOfBeads+2, 3, NumberOfTethers)

    ! Velocity with respect to IP IF
    real, pointer :: rBeadDIPIF(:,:,:)  ! (NumberOfBeads+2, 3, NumberOfTethers)

    ! Acceleration with respect to IP IF
    real, pointer :: rBeadDDIPIF(:,:,:) ! (NumberOfBeads+2, 3, NumberOfTethers)
…
    ! SEGMENT LENGTH AND LENGTH RATE
    real, pointer :: rELBSG(:,:) 	! (NumberOfBeads+1, NumberOfTethers)
    real, pointer :: rELBSGD(:,:)	! (NumberOfBeads+1, NumberOfTethers)
…

または:

real, pointer :: rBeadIPIF(:,:,:) 	! (NumberOfTethers, NumberOfBeads+2, 3)

または:

real, pointer :: rBeadIPIF(:,:,:) 	! (NumberOfTethers, 3, NumberOfBeads+2)

または:

real, pointer :: rBeadIPIF(:,:,:) 	! (3, NumberOfTethers, NumberOfBeads+2)

SOA のインデックス順は重要で、プログラミング要件に応じて変わります。このアプリケーションで特に注意すべき点は、ビード間計算とビード/ビード内計算のパフォーマンスへの影響です。SOA は両方ではなく 1 つの型の計算に適しています。

SOA のデバッグには、個々の配列要素の検査と配列インデックスの同期が必要になります。この処理は AOS 形式よりも困難です。「ポインター」と「割り当て可能」を選択したことは、個人的なプログラミングの好みであることをお知らせしておきます。

この記事で示されたようなアプリケーションでは、プログラマーはスカラーおよび小さなベクトルを AOS 形式で処理し、NumberOfBead サイズの割り当てが大きなベクトルにのみ SOA 変換を行うことになるでしょう。プログラマーがインデックス順の影響を判断するのは困難です。また、異なるインデックス順で複数の変換を行うには、順序を変更して、それぞれパフォーマンス・テストを実行しなければなりません。アプリケーションの SOA 変換は、計算の一部にのみ部分的に実装されます。

PAOS 形式

パックド構造体配列 (PAOS) には、いくつかの表現があります。ここでは、1 つの SSE 表現 (2 要素幅 double) と 2 つの AVX/AVX2 表現 (4 要素幅 double および 3 要素幅 double) の 3 つの表現を使用します。

! AVX four-wide double vector
type TypeYMM
    SEQUENCE
    real(8) :: v(0:3)
end type TypeYMM

! AVX three-wide double vector
type TypeYMM02
    SEQUENCE
    real(8) :: v(0:2)
end type TypeYMM02

! SSE two-wide double vector
type TypeXMM
    SEQUENCE
    real(8) :: v(0:1)
end type TypeXMM

type TypeFiniteSolution
SEQUENCE
    ! REAL variables (4-wide AVX)
    ! INTEGRATION INTERVAL & LAPSED TIME OF FINITE TETHER SOLN
    type(TypeYMM) :: rDELTIS	! Integration step size
    type(TypeYMM) :: rTISTIM 	! Integration time
…
    ! NOMINAL INSTANTANEOUS DEPLOYED (UNDEFORMED) TETHER LENGTH
    type(TypeYMM) :: rEL
    ! rate of deployment +out, -in through the X end
    type(TypeYMM) :: rELD
    ! rate of rate of deployment +out, -in through the X end
    type(TypeYMM) :: rELDD
…
    ! POSITION VECTOR BETWEEN ATT PTS (INER FRAME)
    type(TypeYMM) :: rELI(3)
    ! DERIVATIVE OF POS VECTOR BETWEEN ATT PTS (INER FRAME)
    type(TypeYMM) :: rELDI(3)
    ! 2ND DERIV OF POS VECTOR BETWEEN ATT PTS (INER FRAME)
    type(TypeYMM) :: rELDDI(3)
…
    ! Position with respect to IP IF
    type(TypeYMM), pointer :: rBeadIPIF(:,:)   	! (3,NumberOfBeads+2)
    ! Velocity with respect to IP IF
    type(TypeYMM), pointer :: rBeadDIPIF(:,:)  	! (3,NumberOfBeads+2)
    ! Acceleration with respect to IP IF
    type(TypeYMM), pointer :: rBeadDDIPIF(:,:) 	! (3,NumberOfBeads+2)
…
    ! SEGMENT LENGTH AND LENGTH RATE
    type(TypeYMM), pointer :: rELBSG(:) 	! (NumberOfBeads+1)
    type(TypeYMM), pointer :: rELBSGD(:)	! (NumberOfBeads+1)
…
end type TypeFiniteSolution

PAOS のデータ構成は、変数型「real」を「type(TypeYMM)」に変更する点を除いて AOS と同じです。配列のインデックス・スキームは同じままです。PAOS のデバッグも、AOS とほぼ同様に行うことができます。4 要素幅変数を調べるには、SIMD ベクトルを分解しなければなりません。

インメモリー 4 要素幅 YMM のレイアウトは次のとおりです。

インメモリー 2 要素幅 XMM のレイアウトは次のとおりです。

すべてのテザーを集約する際、すべての参照に完全な SIMD ベクトル化が利用可能であることに注意してください。個々のアクセスを行うコードの部分はキャッシュラインを別途ロードする影響を受けますが、計算の主な部分はテザーの集約に対して行われます。

単純なループ – PAOS を使用したオイラー法

call EULINTxyz(DELTIS, NBEAD, rBeadDDIPIF(1:3,1:NBEAD), rBeadDIPIF(1:3,1:NBEAD))
...
subroutine EULINTxyz_ymm_n_ymm_ymm(dT, n, dX, X)
  USE MOD_ALL
  implicit none
  type(TypeYMM) :: dT
  integer :: n
  type(TypeYMM) :: dX(1:3,1:n)
  type(TypeYMM) :: X(1:3,1:n)
  integer :: i,j

  do i=1,n
    do j=1,3
      !DEC$ VECTOR ALWAYS
      X(j,i).v = X(j,i).v + dX(j,i).v * dT.v
    end do
  end do
end subroutine EULINTxyz_ymm_n_ymm_ymm

(ジェネリック・インターフェイスが署名サフィックス “_ymm_n_ymm_ymm” を選択していることに注意してください。)

PAOS アセンブリー・コード

; parameter 1(DT): rcx
; parameter 2(N): rdx
; parameter 3(DX): r8
; parameter 4(X): r9
;;; recursive subroutine EULINTxyz_ymm_n_ymm_ymm(dT, n, dX, X)
        push      rbp				; save stack frame pointer
        sub       rsp, 48			; allocate stack space
        lea       rbp, QWORD PTR [32+rsp]	; set new stack frame pointer
        movsxd    rax, DWORD PTR [rdx]		; rax = n
        mov       QWORD PTR [rbp], rax		; temp0 = n (unnecessary)
        mov       QWORD PTR [8+rbp], rax		; temp1 = n (unnecessary)
;;;     type(TypeYMM) :: dT
;;;     integer :: n
;;;     type(TypeYMM) :: dX(1:3,1:n)
;;;     type(TypeYMM) :: X(1:3,1:n)
;;;     integer :: i,j
;;;
;;;     do i=1,n
movsxd    r10, DWORD PTR [rdx]			; r10 = n (could have obtained from rax)
xor       edx, edx				; zero loop iteration count
xor       eax, eax				; zero linear index into dX and X
test      r10, r10				; if(n <= 0)
jle       .B6.5         ; Prob 3%		;     goto .B6.6
.B6.2::
;;;         do j=1,3
;;;             !DEC$ VECTOR ALWAYS
;;;             X(j,i).v = X(j,i).v + dX(j,i).v * dT.v
vmovupd   xmm1, XMMWORD PTR [rcx]		; xmm1 = dT.v(0:1)
vmovupd   xmm0, XMMWORD PTR [16+rcx]		; xmm0 = dT.v(2:3)
.B6.3::						; loop:
vmulpd    xmm2, xmm1, XMMWORD PTR [rax+r8]	; xmm2=dT(index).v(0:1) * dX(index).v(0:1)
vmulpd    xmm4, xmm0, XMMWORD PTR [16+rax+r8] 	; xmm4=dT(index).v(2:3) * dX(index).v(2:3)
vaddpd    xmm3, xmm2, XMMWORD PTR [rax+r9]	; xmm3=dT(index).v(0:1)*dX(index).v(0:1)+X(index).v(0:1)
vaddpd    xmm5, xmm4, XMMWORD PTR [16+rax+r9] 	; xmm5=dT(index).v(2:3)*dX(index).v(2:3)+X(index).v(2:3)
vmulpd    xmm2, xmm1, XMMWORD PTR [32+rax+r8]	; xmm2 = dT.v(0:1) * dX(index+1).v(0:1)
; X(index).v(0:1)= dT(index).v(0:1) * dX(index).v(0:1) + X(index).v(0:1)
vmovupd   XMMWORD PTR [rax+r9], xmm3
; X(index).v(2:3)= dT(index).v(2:3) * dX(index).v(2:3) + X(index).v(2:3)
vmovupd   XMMWORD PTR [16+rax+r9], xmm5
vmulpd    xmm4, xmm0, XMMWORD PTR [48+rax+r8]	; xmm4 = dT.v(2:3) * dX(index+1).v(2:3)
; xmm3 = dT.v(0:1) * dX(index+1).v(0:1) + X(index+1).v(0:1)
vaddpd    xmm3, xmm2, XMMWORD PTR [32+rax+r9]
; xmm5 = dT.v(2:3) * dX(index+1).v(2:3) + X(index+1).v(2:3)
vaddpd    xmm5, xmm4, XMMWORD PTR [48+rax+r9]
vmulpd    xmm2, xmm1, XMMWORD PTR [64+rax+r8]	; xmm2 = dT(index+2).v(0:1) * dX(index+2).v(0:1)
; X(index+1).v(0:1)= dT(index+1).v(0:1) * dX(index+1).v(0:1) + X(index+1).v(0:1)
vmovupd   XMMWORD PTR [32+rax+r9], xmm3
; X(index+1).v(2:3)= dT(index+1).v(2:3) * dX(index+1).v(2:3) + X(index+1).v(2:3)
vmovupd   XMMWORD PTR [48+rax+r9], xmm5
vmulpd    xmm4, xmm0, XMMWORD PTR [80+rax+r8]	; xmm4 = dT(index+2).v(2:3) * dX(index+2).v(2:3)
;xmm3=dT(index+2).v(0:1)*dX(index+2).v(0:1)+X(index+2).v(0:1)
vaddpd    xmm3, xmm2, XMMWORD PTR [64+rax+r9]
; xmm5 = dT(index+2).v(2:3) * dX(index+2).v(2:3) + X(index+2).v(2:3)
vaddpd    xmm5, xmm4, XMMWORD PTR [80+rax+r9]
; X(index+2).v(0:1)= dT(index+2).v(0:1) * dX(index+2).v(0:1) + X(index+2).v(0:1)
vmovupd   XMMWORD PTR [64+rax+r9], xmm3
; X(index+2).v(2:3)= dT(index+2).v(2:3) * dX(index+2).v(2:3) + X(index+2).v(2:3)
vmovupd   XMMWORD PTR [80+rax+r9], xmm5
inc       rdx					; ++loop count
add       rax, 96				; index += 3
cmp       rdx, r10				; if(loop count < n)
jb        .B6.3         ; Prob 82%		;     goto loop
.B6.5::
;;;         end do
;;;     end do
;;; end subroutine EULINTxyz_ymm_n_ymm_ymm

アセンブリー・コードを調べると、xmm レジスターとともに先頭の文字が v で始まる命令 (vaddpd など) が使用されていることが分かります。これらの命令は、2 要素幅のパックドダブルを表します。AVX は 4 要素幅のパックドダブルをサポートしますが、Sandy Bridge アーキテクチャーは 2 要素幅のフェッチとストアを実行します。4 要素幅のフェッチやストアを行うこともできますが、この場合、隣接するフェッチとストアを同時に行うことになります。万一これらの 2 要素幅のフェッチやストアのデータが同じキャッシュラインに及ぶと、パイプラインはストールします。2 要素幅のフェッチとストアを使用することにより、コード最適化でフェッチやストアと同時に計算を行う命令を交互に配置することができます。

vmulpd    xmm2, xmm1, XMMWORD PTR [rax+r8]	; xmm2=dT(index).v(0:1) * dX(index).v(0:1)
vmulpd    xmm4, xmm0, XMMWORD PTR [16+rax+r8] 	; xmm4=dT(index).v(2:3) * dX(index).v(2:3)
vaddpd    xmm3, xmm2, XMMWORD PTR [rax+r9]	; xmm3=dT(index).v(0:1)*dX(index).v(0:1)+X(index).v(0:1)
vaddpd    xmm5, xmm4, XMMWORD PTR [16+rax+r9] 	; xmm5=dT(index).v(2:3)*dX(index).v(2:3)+X(index).v(2:3)
------------ または -------------
vmulpd    ymm2, ymm1, XMMWORD PTR [rax+r8]	; ymm2=dT(index).v(0:3) * dX(index).v(0:3)
vaddpd    ymm3, ymm2, XMMWORD PTR [rax+r9]	; ymm3=dT(index).v(0:3)*dX(index).v(0:3)+X(index).v(0:3)

Sandy Bridge アーキテクチャーでは、例えば、.v(0:1) と .v(2:3) は異なるキャッシュラインに存在するために、フェッチがキャッシュラインで分割される場合を除いて、2 つはほぼ同時に実行されます。キャッシュラインが分割された状況では、2 要素幅命令はより高速に実行されます。

Ivy Bridge アーキテクチャーでは、4 要素幅のフェッチとストア操作を 4 要素幅の計算と一緒に実行できるため、この制限はなくなります。

コードの多くの場所で、コンパイラーは 4 要素幅のフェッチとストアを実行します (後の例を参照)。

一般的な問題

先に調べたオイラー展開コードは、AOS に対する PAOS の最も望ましくない例であり、ループの処理も変則的でした。一般的なループを調べることにしましょう。次のループは、ばねの張力を計算します。

ループの AOS バージョン

!***********************************************************
  ! FORM SEGMENT VECTOR BETWEEN ADJACENT BEADS (IN INER FRAME)
  !***********************************************************
  do i=1,3
      ! Determine TUI at 1/2 integration step from now
      ! using current velocity and acceleration
      ! Position of X-End of segment
      TOSVX1(i) = rBeadIPIF(i,JSEG-1) &
        & + (rBeadDIPIF(i,JSEG-1) * (rDELTIS/2.0_8)) &
        & + (rBeadDDIPIF(i,JSEG-1) * (rDELTIS**2/4.0_8))
      ! Position of Y-End of segment
      TOSVX2(i) = rBeadIPIF(i,JSEG).v &
        & + (rBeadDIPIF(i,JSEG) * (rDELTIS/2.0_8)) &
        & + (rBeadDDIPIF(i,JSEG) * (rDELTIS**2/4.0_8))
      ! SUI X->Y
      SUI(i) = TOSVX2(i) - TOSVX1(i)
  end do

  ! CALC DIST BETWEEN BEADS ADJACENT TO THIS SEGMENT
  !-------------------------------------------------
  DUMEL = dsqrt(SUI(1)**2 + SUI(2)**2 + SUI(3)**2)
  rELBSG(JSEG) = DUMEL

  ! TALLY TOTAL SPATIAL ARC LENGTH
  ARCTOTlocal = ARCTOTlocal + DUMEL

  !---------------------------------------------
  ! POPULATE THE JSEG ELEMENT OF THE BBTUI ARRAY
  !---------------------------------------------
  ! FORM UNIT VECTOR FROM X-END TO Y-END OF BEAD SEGMENT
  if(DUMEL .ne. 0.0_8) then
    TUI = SUI / DUMEL
  else
    call RandomUnitVector(TUI)
  endif
  rBBTUI(1:3,JSEG) = TUI(1:3)
  ! recompute velocity of Bead 0
  if(JSEG .eq. 1) then
    rBeadDIPIF(:,0) = rAVI + (TUI * rELD)
  endif

  ! ARC LENGTH WR/T UNDEFORMED TETHER
  !--------------------------------------------
  ! (NOTE, NEGATIVE SIGN REFLECTS MU DEFINITION FROM FAR END)
  rDSDU(JSEG)  = - DUMEL/ELBis

  !**************************************************************
  ! FORM DERIVATIVE OF SEGMENT VECTOR (INER FRAME)
  !**************************************************************
  ! compute equivelent to
  ! SUDI = BUDI(JB1:JB1+2) - BUDI(JB1-3:JB1-1) + ELSGX_x_UITDI_ELSGXD_x_UITI
  SUDI = rBeadDIPIF(:,JSEG) - rBeadDIPIF(:,JSEG-1)

  ! CALC DERIV OF DIST BETWEEN BEADS ADJACENT TO SEGMENT
  !-----------------------------------------------------
  DUMELD = DOT(TUI,SUDI)
  rELBSGD(JSEG) = DUMELD

  !--------------------------------------------------------
  ! POPULATE THE SEGMENT DATA ARRAY'S FOR THIS JSEG ELEMENT
  ! (FOR AVERAGING AND NUMERICAL DIFF IN DEPLOY FLOW CALCS)
  !--------------------------------------------------------
  !  BBTUDI(1:3,JSEG) = (SUDI / DUMEL) + (SUI * -DUMELD/DUMEL**2)
  if(DUMEL .ne. 0.0_8) then
    rBBTUDI(1:3,JSEG) =((SUDI) + (SUI * -DUMELD/DUMEL)) / DUMEL
  else
    rBBTUDI(1:3,JSEG) = 0.0_8
  endif

  ! FINALLY, ARC LENGTH DERIV WR/T UNDEFORMED TETHER, AND ITS TIME DERIV
  !---------------------------------------------------------------------
  ! (NOTE, NEGATIVE SIGN REFLECTS MU DEFINITION FROM FAR END)
  rDSDUD(JSEG) = - ( DUMELD - DUMEL*ELDoverEL )/ELBis
end do

(コードが読みやすいようにポインターが削除されていることに注意してください)

ループの PAOS バージョン

DO JSEG = 1,JSEGlast
!***********************************************************
! FORM SEGMENT VECTOR BETWEEN ADJACENT BEADS (IN INER FRAME)
!***********************************************************
  do i=1,3
      ! Determine TUI at 1/2 integration step from now
      ! using current velocity and acceleration
      ! Position of X-End of segment
      TOSVX1(i).v = rBeadIPIF(i,JSEG-1).v &
        & + (rBeadDIPIF(i,JSEG-1).v * (rDELTIS.v/2.0_8)) &
        & + (rBeadDDIPIF(i,JSEG-1).v * (rDELTIS.v**2/4.0_8))
      ! Position of Y-End of segment
      TOSVX2(i).v = rBeadIPIF(i,JSEG).v &
        & + (rBeadDIPIF(i,JSEG).v * (rDELTIS.v/2.0_8)) &
        & + (rBeadDDIPIF(i,JSEG).v * (rDELTIS.v**2/4.0_8))
      ! SUI X->Y
      SUI(i).v = TOSVX2(i).v - TOSVX1(i).v
  end do
  ! CALC DIST BETWEEN BEADS ADJACENT TO THIS SEGMENT
  !-------------------------------------------------
  DUMEL.v = dsqrt(SUI(1).v**2 + SUI(2).v**2 + SUI(3).v**2)
  rELBSG(JSEG) = DUMEL

  ! TALLY TOTAL SPATIAL ARC LENGTH
  ARCTOTlocal.v = ARCTOTlocal.v + DUMEL.v

  !---------------------------------------------
  ! POPULATE THE JSEG ELEMENT OF THE BBTUI ARRAY
  !---------------------------------------------
  ! FORM UNIT VECTOR FROM X-END TO Y-END OF BEAD SEGMENT
  ! *** code change for SIMD horizontal operation ***
  do s=0,sLast
    if(DUMEL.v(s) .ne. 0.0_8) then
      TUI.v(s) = SUI.v(s) / DUMEL.v(s)
    else
      call RandomUnitVector(TUI.v(s))
    endif
  end do
  rBBTUI(1:3,JSEG) = TUI(1:3)
  ! recompute velocity of Bead 0
  if(JSEG .eq. 1) then
    do i=1,3
      rBeadDIPIF(i,0).v = rAVI(i).v + (TUI(i).v * rELD.v)
    end do
  endif

  ! ARC LENGTH WR/T UNDEFORMED TETHER
  !--------------------------------------------
  ! (NOTE, NEGATIVE SIGN REFLECTS MU DEFINITION FROM FAR END)
  rDSDU(JSEG).v  = - DUMEL.v/ELBis.v

  !**************************************************************
  ! FORM DERIVATIVE OF SEGMENT VECTOR (INER FRAME)
  !**************************************************************
  do i=1,3
    SUDI(i).v = rBeadDIPIF(i,JSEG).v - rBeadDIPIF(i,JSEG-1).v
  end do
  ! CALC DERIV OF DIST BETWEEN BEADS ADJACENT TO SEGMENT
  !-----------------------------------------------------
  DUMELD = DOT(TUI,SUDI)
  rELBSGD(JSEG) = DUMELD

  !--------------------------------------------------------
  ! POPULATE THE SEGMENT DATA ARRAY'S FOR THIS JSEG ELEMENT
  ! (FOR AVERAGING AND NUMERICAL DIFF IN DEPLOY FLOW CALCS)
  !--------------------------------------------------------
  if((DUMEL.v(0) .ne. 0.0_8) .and. (DUMEL.v(1) .ne. 0.0_8) &
     & .and. (DUMEL.v(2) .ne. 0.0_8) .and. (DUMEL.v(3) .ne. 0.0_8)) then
    do i=1,3
     rBBTUDI(i,JSEG).v =((SUDI(i).v) + (SUI(i).v * -DUMELD.v / DUMEL.v)) / DUMEL.v
    end do
  else
    do s=0,sLast ! *** code change for SIMD horizontal operation ***
      if(DUMEL.v(s) .ne. 0.0_8) then
        do i=1,3
          rBBTUDI(i,JSEG).v(s) = ((SUDI(i).v(s)) &
             & + (SUI(i).v(s) * -DUMELD.v(s) / DUMEL.v(s))) / DUMEL.v(s)
        end do
      else
        do i=1,3
          rBBTUDI(i,JSEG).v(s) = 0.0_8
        end do
      endif
    end do ! *** code change for SIMD horizontal operation ***
 endif

  ! FINALLY, ARC LENGTH DERIV WR/T UNDEFORMED TETHER, AND ITS TIME DERIV
  !---------------------------------------------------------------------
  ! (NOTE, NEGATIVE SIGN REFLECTS MU DEFINITION FROM FAR END)
  rDSDUD(JSEG).v = - ( DUMELD.v - DUMEL.v * ELDoverEL.v ) / ELBis.v
end do

では、メインループ内の最初の小さなループを分解しましょう。このループは、積分区間の途中に存在する 2 つのポイント (ビード) 間のベクトルを計算します。このベクトルは、(2 つのセグメント最後の各ビードの) 2 つのばねのフォースベクトルの方向を決定するために後で使用されます。

AOS

        do i=1,3
            ! Determine TUI at 1/2 integration step from now
            ! using current velocity and acceleration
            ! Position of X-End of segment
            TOSVX1(i) = rBeadIPIF(i,JSEG-1) &
              & + (rBeadDIPIF(i,JSEG-1)*(rDELTIS/2.0_8)) &
              & + (rBeadDDIPIF(i,JSEG-1)*(rDELTIS**2/4.0_8))
            ! Position of Y-End of segment
            TOSVX2(i) = rBeadIPIF(i,JSEG) &
              & + (rBeadDIPIF(i,JSEG)*(rDELTIS/2.0_8)) &
              & + (rBeadDDIPIF(i,JSEG)*(rDELTIS**2/4.0_8))
          ! SUI X->Y
          SUI(i) = TOSVX2(i) - TOSVX1(i)
        end do

PAOS

  do i=1,3
      ! Determine TUI at 1/2 integration step from now
      ! using current velocity and acceleration
      ! Position of X-End of segment
      TOSVX1(i).v = rBeadIPIF(i,JSEG-1).v &
        & + (rBeadDIPIF(i,JSEG-1).v * (rDELTIS.v/2.0_8)) &
        & + (rBeadDDIPIF(i,JSEG-1).v * (rDELTIS.v**2/4.0_8))
      ! Position of Y-End of segment
      TOSVX2(i).v = rBeadIPIF(i,JSEG).v &
        & + (rBeadDIPIF(i,JSEG).v * (rDELTIS.v/2.0_8)) &
        & + (rBeadDDIPIF(i,JSEG).v * (rDELTIS.v**2/4.0_8))
      ! SUI X->Y
      SUI(i).v = TOSVX2(i).v - TOSVX1(i).v
  end do

算術演算子を含む文にサフィックス “.v” が追加されていることに注意してください。これらのデータ型用に演算子を多重定義した関数を記述する方法を選択すればサフィックスを追加する必要はありませんが、デバッグ上の理由 (デバッグビルドでコードをインライン展開する) により、ここではサフィックスを追加しています。

3 つの文の小さなループの AOS アセンブラー・コード

        mov       r12, QWORD PTR [80+rbp]                       ;178.25
        mov       r11, QWORD PTR [176+rbp]                      ;179.18
        mov       rdi, QWORD PTR [152+rbp]                      ;179.18
        mov       r8, QWORD PTR [272+rbp]                       ;180.18
        mov       rax, QWORD PTR [56+rbp]                       ;178.25
        mov       rdx, QWORD PTR [248+rbp]                      ;180.18
        mov       QWORD PTR [896+rbp], r12                      ;178.25
        mov       QWORD PTR [928+rbp], r11                      ;179.18
        mov       QWORD PTR [912+rbp], rdi                      ;179.18
        mov       QWORD PTR [936+rbp], r8                       ;180.18
        mov       r9, QWORD PTR [rbp]                           ;178.13
        mov       r15, QWORD PTR [64+rbp]                       ;178.25
        mov       rcx, QWORD PTR [88+rbp]                       ;178.25
        mov       r12, QWORD PTR [184+rbp]                      ;179.18
        mov       r11, QWORD PTR [160+rbp]                      ;179.18
        mov       r14, QWORD PTR [96+rbp]                       ;178.25
        mov       rdi, QWORD PTR [192+rbp]                      ;179.15
        mov       r8, QWORD PTR [256+rbp]                       ;180.18
        mov       r10, QWORD PTR [280+rbp]                      ;180.18
        mov       QWORD PTR [920+rbp], rax                      ;178.25
        mov       QWORD PTR [904+rbp], rdx                      ;180.18
        cmp       rax, 8                                        ;180.18
        jne       .B1.12        ; Prob 50%                      ;180.18
.B1.9::                         ; Preds .B1.8
        cmp       QWORD PTR [912+rbp], 8                        ;180.18
        jne       .B1.12        ; Prob 50%                      ;180.18
.B1.10::                        ; Preds .B1.9
        cmp       QWORD PTR [904+rbp], 8                        ;180.18
        jne       .B1.12        ; Prob 50%                      ;180.18
.B1.11::                        ; Preds .B1.10
        mov       QWORD PTR [960+rbp], rbx                      ;
        mov       rbx, QWORD PTR [936+rbp]                      ;182.13
        imul      r10, rbx                                      ;182.13
        mov       QWORD PTR [968+rbp], r10                      ;182.13
        mov       r10, rsi                                      ;182.25
        mov       rax, QWORD PTR [896+rbp]                      ;182.13
        imul      r10, rax                                      ;182.25
        imul      rcx, rax                                      ;182.13
        add       r10, r9                                       ;182.13
        shl       r15, 3                                        ;182.13
        sub       r10, rcx                                      ;182.13
        mov       QWORD PTR [976+rbp], r15                      ;182.13
        sub       r10, r15                                      ;182.13
        mov       r15, rsi                                      ;183.18
        mov       rdx, QWORD PTR [928+rbp]                      ;182.13
        imul      r15, rdx                                      ;183.18
        imul      r12, rdx                                      ;182.13
        add       r15, r14                                      ;182.13
        shl       r11, 3                                        ;182.13
        sub       r15, r12                                      ;182.13
        mov       QWORD PTR [984+rbp], r11                      ;182.13
        sub       r15, r11                                      ;182.13
        mov       r11, rsi                                      ;184.18
        imul      r11, rbx                                      ;184.18
        vmulsd    xmm0, xmm10, QWORD PTR [8+r15]                ;183.45
        mov       QWORD PTR [944+rbp], rsi                      ;
        lea       rsi, QWORD PTR [-1+rsi]                       ;178.25
        imul      rdx, rsi                                      ;179.18
        imul      rax, rsi                                      ;178.25
        imul      rbx, rsi                                      ;180.18
        vaddsd    xmm0, xmm0, QWORD PTR [8+r10]                 ;183.15
        add       r11, rdi                                      ;182.13
        add       r14, rdx                                      ;178.13
        mov       QWORD PTR [952+rbp], rdi                      ;
        sub       r14, r12                                      ;178.13
        mov       rdi, QWORD PTR [968+rbp]                      ;182.13
        sub       r11, rdi                                      ;182.13
        shl       r8, 3                                         ;182.13
        add       r9, rax                                       ;178.13
        sub       r11, r8                                       ;182.13
        sub       r9, rcx                                       ;178.13
        sub       r14, QWORD PTR [984+rbp]                      ;178.13
        mov       rcx, QWORD PTR [952+rbp]                      ;178.13
        add       rcx, rbx                                      ;178.13
        vmulsd    xmm5, xmm8, QWORD PTR [8+r11]                 ;184.49
        sub       rcx, rdi                                      ;178.13
        sub       rcx, r8                                       ;178.13
        vaddsd    xmm14, xmm0, xmm5                             ;182.13
        vmulsd    xmm0, xmm10, QWORD PTR [8+r14]                ;179.47
        vmulsd    xmm5, xmm8, QWORD PTR [8+rcx]                 ;180.51
        vmulsd    xmm1, xmm8, QWORD PTR [24+rcx]                ;180.51
        sub       r9, QWORD PTR [976+rbp]                       ;178.13
        vmovsd    QWORD PTR [704+rbp], xmm14                    ;182.13
        mov       QWORD PTR [888+rbp], rsi                      ;178.25
        mov       rbx, QWORD PTR [960+rbp]                      ;186.11
        mov       rsi, QWORD PTR [944+rbp]                      ;186.11
        vaddsd    xmm0, xmm0, QWORD PTR [8+r9]                  ;179.15
        vaddsd    xmm13, xmm0, xmm5                             ;178.13
        vmulsd    xmm0, xmm10, QWORD PTR [16+r15]               ;183.45
        vmulsd    xmm5, xmm8, QWORD PTR [16+r11]                ;184.49
        vaddsd    xmm0, xmm0, QWORD PTR [16+r10]                ;183.15
        vmovsd    QWORD PTR [736+rbp], xmm13                    ;178.13
        vaddsd    xmm12, xmm0, xmm5                             ;182.13
        vmulsd    xmm0, xmm10, QWORD PTR [16+r14]               ;179.47
        vmulsd    xmm5, xmm8, QWORD PTR [16+rcx]                ;180.51
        vaddsd    xmm0, xmm0, QWORD PTR [16+r9]                 ;179.15
        vmovsd    QWORD PTR [712+rbp], xmm12                    ;182.13
        vaddsd    xmm11, xmm0, xmm5                             ;178.13
        vmulsd    xmm0, xmm10, QWORD PTR [24+r15]               ;183.45
        vmulsd    xmm5, xmm8, QWORD PTR [24+r11]                ;184.49
        vaddsd    xmm0, xmm0, QWORD PTR [24+r10]                ;183.15
        vmovsd    QWORD PTR [744+rbp], xmm11                    ;178.13
        vaddsd    xmm5, xmm0, xmm5                              ;182.13
        vmulsd    xmm0, xmm10, QWORD PTR [24+r14]               ;179.47
        vunpcklpd xmm12, xmm14, xmm12                           ;186.20
        vaddsd    xmm0, xmm0, QWORD PTR [24+r9]                 ;179.15
        vunpcklpd xmm11, xmm13, xmm11                           ;186.32
        vaddsd    xmm0, xmm0, xmm1                              ;178.13
        vsubpd    xmm11, xmm12, xmm11                           ;186.11
        vmovupd   XMMWORD PTR [1024+rbp], xmm11                 ;186.11
        vsubsd    xmm11, xmm5, xmm0                             ;186.11
        vmovsd    QWORD PTR [720+rbp], xmm5                     ;182.13
        vmovsd    QWORD PTR [752+rbp], xmm0                     ;178.13
        vmovsd    QWORD PTR [1040+rbp], xmm11                   ;186.11
        jmp       .B1.13        ; Prob 100%                     ;186.11
.B1.12::                        ; Preds .B1.8 .B1.9 .B1.10
        mov       rax, rsi                                      ;182.25
        mov       rdx, QWORD PTR [896+rbp]                      ;182.13
        imul      rax, rdx                                      ;182.25
        imul      rcx, rdx                                      ;182.13
        add       rax, r9                                       ;182.13
        mov       QWORD PTR [1000+rbp], rcx                     ;182.13
        sub       rax, rcx                                      ;182.13
        mov       rcx, QWORD PTR [920+rbp]                      ;182.13
        imul      r15, rcx                                      ;182.13
        mov       QWORD PTR [960+rbp], rbx                      ;
        mov       rbx, rsi                                      ;183.18
        mov       QWORD PTR [976+rbp], r15                      ;182.13
        sub       rcx, r15                                      ;182.13
        mov       r15, QWORD PTR [928+rbp]                      ;182.13
        imul      rbx, r15                                      ;183.18
        imul      r12, r15                                      ;182.13
        add       rbx, r14                                      ;182.13
        mov       QWORD PTR [1008+rbp], r12                     ;182.13
        sub       rbx, r12                                      ;182.13
        mov       r12, QWORD PTR [912+rbp]                      ;182.13
        imul      r11, r12                                      ;182.13
        mov       QWORD PTR [984+rbp], r11                      ;182.13
        sub       r12, r11                                      ;182.13
        mov       r11, rsi                                      ;184.18
        mov       QWORD PTR [992+rbp], r14                      ;
        mov       r14, QWORD PTR [936+rbp]                      ;182.13
        imul      r11, r14                                      ;184.18
        imul      r10, r14                                      ;182.13
        vmulsd    xmm0, xmm10, QWORD PTR [r12+rbx]              ;183.45
        add       r11, rdi                                      ;182.13
        vaddsd    xmm0, xmm0, QWORD PTR [rcx+rax]               ;183.15
        mov       QWORD PTR [968+rbp], r10                      ;182.13
        sub       r11, r10                                      ;182.13
        mov       r10, QWORD PTR [904+rbp]                      ;182.13
        imul      r8, r10                                       ;182.13
        mov       QWORD PTR [944+rbp], rsi                      ;
        lea       rsi, QWORD PTR [-1+rsi]                       ;178.25
        imul      rdx, rsi                                      ;178.25
        imul      r15, rsi                                      ;179.18
        imul      r14, rsi                                      ;180.18
        sub       r10, r8                                       ;182.13
        add       r9, rdx                                       ;178.13
        mov       rdx, QWORD PTR [992+rbp]                      ;178.13
        add       rdi, r14                                      ;178.13
        add       rdx, r15                                      ;178.13
        sub       rdx, QWORD PTR [1008+rbp]                     ;178.13
        sub       r9, QWORD PTR [1000+rbp]                      ;178.13
        sub       rdi, QWORD PTR [968+rbp]                      ;178.13
        mov       QWORD PTR [888+rbp], rsi                      ;178.25
        mov       rsi, QWORD PTR [912+rbp]                      ;183.18
        vmulsd    xmm5, xmm8, QWORD PTR [r10+r11]               ;184.49
        mov       r15, QWORD PTR [976+rbp]                      ;182.13
        lea       r14, QWORD PTR [rsi+rsi]                      ;183.18
        sub       r14, QWORD PTR [984+rbp]                      ;182.13
        vaddsd    xmm14, xmm0, xmm5                             ;182.13
        vmulsd    xmm0, xmm10, QWORD PTR [rdx+r12]              ;179.47
        vmulsd    xmm5, xmm8, QWORD PTR [rdi+r10]               ;180.51
        vaddsd    xmm0, xmm0, QWORD PTR [r9+rcx]                ;179.15
        mov       rcx, QWORD PTR [920+rbp]                      ;182.25
        vaddsd    xmm13, xmm0, xmm5                             ;178.13
        vmulsd    xmm0, xmm10, QWORD PTR [r14+rbx]              ;183.45
        mov       r10, QWORD PTR [904+rbp]                      ;184.18
        lea       r12, QWORD PTR [rcx+rcx]                      ;182.25
        sub       r12, r15                                      ;182.13
        lea       rcx, QWORD PTR [rcx+rcx*2]                    ;182.25
        sub       rcx, r15                                      ;182.13
        lea       r15, QWORD PTR [rsi+rsi*2]                    ;183.18
        mov       rsi, QWORD PTR [904+rbp]                      ;184.18
        add       r10, r10                                      ;184.18
        sub       r10, r8                                       ;182.13
        sub       r15, QWORD PTR [984+rbp]                      ;182.13
        vmovsd    QWORD PTR [704+rbp], xmm14                    ;182.13
        vmovsd    QWORD PTR [736+rbp], xmm13                    ;178.13
        vmulsd    xmm5, xmm8, QWORD PTR [r10+r11]               ;184.49
        vaddsd    xmm0, xmm0, QWORD PTR [r12+rax]               ;183.15
        vaddsd    xmm12, xmm0, xmm5                             ;182.13
        vmulsd    xmm0, xmm10, QWORD PTR [r14+rdx]              ;179.47
        vmulsd    xmm5, xmm8, QWORD PTR [r10+rdi]               ;180.51
        vaddsd    xmm0, xmm0, QWORD PTR [r12+r9]                ;179.15
        vmovsd    QWORD PTR [712+rbp], xmm12                    ;182.13
        lea       r12, QWORD PTR [rsi+rsi*2]                    ;184.18
        sub       r12, r8                                       ;182.13
        vaddsd    xmm11, xmm0, xmm5                             ;178.13
        vmulsd    xmm0, xmm10, QWORD PTR [r15+rbx]              ;183.45
        vmulsd    xmm5, xmm8, QWORD PTR [r12+r11]               ;184.49
        vmulsd    xmm1, xmm8, QWORD PTR [r12+rdi]               ;180.51
        vaddsd    xmm0, xmm0, QWORD PTR [rcx+rax]               ;183.15
        vmovsd    QWORD PTR [744+rbp], xmm11                    ;178.13
        vaddsd    xmm5, xmm0, xmm5                              ;182.13
        vmulsd    xmm0, xmm10, QWORD PTR [r15+rdx]              ;179.47
        vunpcklpd xmm12, xmm14, xmm12                           ;186.20
        vaddsd    xmm0, xmm0, QWORD PTR [rcx+r9]                ;179.15
        vunpcklpd xmm11, xmm13, xmm11                           ;186.32
        vaddsd    xmm0, xmm0, xmm1                              ;178.13
        vsubpd    xmm11, xmm12, xmm11                           ;186.11
        vmovupd   XMMWORD PTR [1024+rbp], xmm11                 ;186.11
        vsubsd    xmm11, xmm5, xmm0                             ;186.11
        vmovsd    QWORD PTR [720+rbp], xmm5                     ;182.13
        vmovsd    QWORD PTR [752+rbp], xmm0                     ;178.13
        vmovsd    QWORD PTR [1040+rbp], xmm11                   ;186.11
        mov       rbx, QWORD PTR [960+rbp]                      ;186.11
        mov       rsi, QWORD PTR [944+rbp]                      ;186.11
.B1.13::                        ; Preds .B1.12 .B1.11

3 つの文用に生成されたアセンブラー・コードは 214 命令でした。これらは、テザーを処理するループ内の一般的な文です。生成されたアセンブラー・コードの大部分でコンポーネントへのインデックスを計算しているのは、プログラマーが 1 以外のストライド参照のポインターを使用しているためです。

	(ストライドがコンパイラーに対して既知の場合)
    	TOSVX1(3),  TOSVX2(3),   SUI(3)
	(ストライドがコンパイラーに対して不明な場合)
    	rBeadIPIF(3,0:NBEAD+1),   rBeadDIPIF(3,0:NBEAD+1),   rBeadDDIPIF(3,0:NBEAD+1)

これらのインデックスは、すべてのテザーの各ビードに対して計算されます。

AOS で行われる倍精度浮動小数点演算にはすべて、スカラーが含まれます。

生成されたコードにはデータのストライドテストが含まれることにも注意してください。

        cmp       rax, 8                                        ;180.18
        jne       .B1.12        ; Prob 50%                      ;180.18
.B1.9::                         ; Preds .B1.8
        cmp       QWORD PTR [912+rbp], 8                        ;180.18
        jne       .B1.12        ; Prob 50%                      ;180.18
.B1.10::                        ; Preds .B1.9
        cmp       QWORD PTR [904+rbp], 8                        ;180.18
        jne       .B1.12        ; Prob 50%                      ;180.18
.B1.11::                        ; Preds .B1.10

ストライドテストは、テストの結果に応じて 2 つの異なるコードパスの 1 つを使用します。

3 つの文の小さなループの PAOS アセンブラー・コード

        mov       r14, QWORD PTR [11184+rdi]                    ;172.15
        mov       r15, QWORD PTR [11176+rdi]                    ;172.15
        imul      r14, r15                                      ;
        vmovupd   ymm1, YMMWORD PTR [160+r13]                   ;204.10
        vmovupd   xmm2, XMMWORD PTR [32+rdi]                    ;178.94
        vmovupd   ymm0, YMMWORD PTR [_2il0floatpacket.40]       ;
        vmovupd   YMMWORD PTR [576+r13], ymm1                   ;204.10
        vmovupd   ymm1, YMMWORD PTR [_2il0floatpacket.39]       ;
        mov       QWORD PTR [1432+rbp], r15                     ;172.15
        sub       r14, r15                                      ;
        mov       r15, rsi                                      ;176.15
        mov       r8, QWORD PTR [11200+rdi]                     ;172.15
        imul      r15, r8                                       ;176.15
        mov       r12, QWORD PTR [11208+rdi]                    ;172.15
        imul      r12, r8                                       ;
        mov       rax, QWORD PTR [11240+rdi]                    ;173.19
        mov       QWORD PTR [1528+rbp], rax                     ;173.19
        mov       rax, QWORD PTR [11328+rdi]                    ;173.19
        mov       QWORD PTR [1360+rbp], rax                     ;173.19
        mov       rax, QWORD PTR [11320+rdi]                    ;173.19
        mov       QWORD PTR [1408+rbp], rax                     ;173.19
        mov       rax, QWORD PTR [11304+rdi]                    ;173.19
        mov       r9, QWORD PTR [11120+rdi]                     ;172.15
        add       r15, r9                                       ;
        mov       QWORD PTR [1352+rbp], rax                     ;173.19
        sub       r15, r12                                      ;
        mov       rax, QWORD PTR [11296+rdi]                    ;173.19
        sub       r15, r14                                      ;
        mov       QWORD PTR [1400+rbp], r12                     ;
        mov       r12, rsi                                      ;176.15
        mov       r10, QWORD PTR [11080+rdi]                    ;172.15
        mov       QWORD PTR [1440+rbp], rax                     ;173.19
        xor       eax, eax                                      ;173.19
        imul      r12, r10                                      ;176.15
        mov       rdx, QWORD PTR [11088+rdi]                    ;172.15
        mov       QWORD PTR [1416+rbp], r10                     ;172.15
        mov       QWORD PTR [1392+rbp], r14                     ;
        imul      rdx, r10                                      ;
        mov       r14, QWORD PTR [1352+rbp]                     ;
        mov       r10, QWORD PTR [1440+rbp]                     ;
        imul      r14, r10                                      ;
        mov       rbx, QWORD PTR [11064+rdi]                    ;172.15
        sub       r14, r10                                      ;
        mov       r11, QWORD PTR [11056+rdi]                    ;172.15
        mov       r10, rsi                                      ;177.19
        imul      rbx, r11                                      ;
        mov       rcx, QWORD PTR [11000+rdi]                    ;172.15
        add       r12, rcx                                      ;
        sub       rbx, r11                                      ;
        sub       r12, rdx                                      ;
        mov       QWORD PTR [1448+rbp], r11                     ;172.15
        sub       r12, rbx                                      ;
        mov       r11, QWORD PTR [1408+rbp]                     ;
        imul      r10, r11                                      ;177.19
        mov       QWORD PTR [1496+rbp], r12                     ;
        mov       r12, QWORD PTR [1360+rbp]                     ;
        imul      r12, r11                                      ;
        mov       QWORD PTR [1504+rbp], r15                     ;
        mov       r15, QWORD PTR [1528+rbp]                     ;
        add       r10, r15                                      ;
        sub       r10, r12                                      ;
        sub       r10, r14                                      ;
        mov       QWORD PTR [1520+rbp], rax                     ;173.19
        mov       QWORD PTR [1488+rbp], r10                     ;
        mov       QWORD PTR [1480+rbp], rax                     ;
        mov       QWORD PTR [1472+rbp], rax                     ;
        mov       QWORD PTR [1464+rbp], rax                     ;
        mov       QWORD PTR [1456+rbp], rax                     ;
        mov       QWORD PTR [1344+rbp], rcx                     ;
        mov       QWORD PTR [1352+rbp], r14                     ;
        mov       QWORD PTR [1360+rbp], r12                     ;
        mov       QWORD PTR [1368+rbp], rbx                     ;
        mov       QWORD PTR [1376+rbp], rdx                     ;
        mov       QWORD PTR [1384+rbp], r8                      ;
        mov       QWORD PTR [1336+rbp], rsi                     ;
        mov       QWORD PTR [1320+rbp], rdi                     ;
        mov       rax, QWORD PTR [1456+rbp]                     ;
        mov       rdx, QWORD PTR [1464+rbp]                     ;
        mov       rcx, QWORD PTR [1472+rbp]                     ;
        mov       rbx, QWORD PTR [1480+rbp]                     ;
        mov       rsi, QWORD PTR [1488+rbp]                     ;
        mov       r8, QWORD PTR [1504+rbp]                      ;
        mov       r10, QWORD PTR [1520+rbp]                     ;
        mov       r11, QWORD PTR [1440+rbp]                     ;
        mov       r12, QWORD PTR [1432+rbp]                     ;
        mov       r14, QWORD PTR [1448+rbp]                     ;
        vinsertf128 ymm2, ymm2, XMMWORD PTR [48+rdi], 1         ;178.94
        mov       rdi, QWORD PTR [1496+rbp]                     ;
        ALIGN     16
.B1.6::                         ; Preds .B1.6 .B1.5
        vmovupd   xmm10, XMMWORD PTR [rdx+rsi]                  ;178.63
        vmovupd   xmm3, XMMWORD PTR [rbx+r8]                    ;177.62
        vmovupd   xmm7, XMMWORD PTR [rcx+rdi]                   ;177.19
        inc       r10                                           ;173.19
        vinsertf128 ymm11, ymm10, XMMWORD PTR [16+rdx+rsi], 1   ;178.63
        add       rdx, r11                                      ;173.19
        vmulpd    ymm12, ymm0, ymm11                            ;178.63
        vmulpd    ymm13, ymm12, ymm2                            ;178.94
        vmulpd    ymm15, ymm13, ymm2                            ;178.94
        vinsertf128 ymm4, ymm3, XMMWORD PTR [16+rbx+r8], 1      ;177.62
        add       rbx, r12                                      ;173.19
        vmulpd    ymm5, ymm1, ymm4                              ;177.62
        vmulpd    ymm9, ymm5, ymm2                              ;177.64
        vinsertf128 ymm8, ymm7, XMMWORD PTR [16+rcx+rdi], 1     ;177.19
        add       rcx, r14                                      ;173.19
        vaddpd    ymm14, ymm8, ymm9                             ;177.19
        vaddpd    ymm3, ymm14, ymm15                            ;176.15
        vmovupd   YMMWORD PTR [256+r13+rax], ymm3               ;176.15
        add       rax, 32                                       ;173.19
        cmp       r10, 3                                        ;173.19
        jb        .B1.6         ; Prob 66%                      ;173.19
.B1.7::                         ; Preds .B1.6
        mov       rsi, QWORD PTR [1336+rbp]                     ;
        lea       r11, QWORD PTR [-1+rsi]                       ;172.15
        mov       rax, QWORD PTR [1416+rbp]                     ;172.15
        xor       r10d, r10d                                    ;173.19
        imul      rax, r11                                      ;172.15
        vmovupd   ymm0, YMMWORD PTR [_2il0floatpacket.40]       ;
        vmovupd   ymm1, YMMWORD PTR [_2il0floatpacket.39]       ;
        mov       rcx, QWORD PTR [1344+rbp]                     ;
        add       rcx, rax                                      ;
        mov       rdx, QWORD PTR [1376+rbp]                     ;
        sub       rcx, rdx                                      ;
        mov       r8, QWORD PTR [1384+rbp]                      ;
        mov       rdx, QWORD PTR [1408+rbp]                     ;173.19
        imul      rdx, r11                                      ;173.19
        imul      r8, r11                                       ;172.15
        add       r15, rdx                                      ;
        add       r9, r8                                        ;
        xor       r8d, r8d                                      ;
        mov       r12, QWORD PTR [1360+rbp]                     ;
        sub       r15, r12                                      ;
        mov       rbx, QWORD PTR [1368+rbp]                     ;
        sub       rcx, rbx                                      ;
        sub       r9, QWORD PTR [1400+rbp]                      ;
        xor       ebx, ebx                                      ;
        mov       QWORD PTR [1512+rbp], r10                     ;173.19
        xor       edx, edx                                      ;
        mov       r14, QWORD PTR [1352+rbp]                     ;
        sub       r15, r14                                      ;
        mov       QWORD PTR [1424+rbp], r11                     ;172.15
        mov       rdi, QWORD PTR [1320+rbp]                     ;
        sub       r9, QWORD PTR [1392+rbp]                      ;
        mov       r11, QWORD PTR [1512+rbp]                     ;
        mov       r12, QWORD PTR [1440+rbp]                     ;
        mov       r14, QWORD PTR [1432+rbp]                     ;
        mov       rax, QWORD PTR [1448+rbp]                     ;
        ALIGN     16
.B1.8::                         ; Preds .B1.8 .B1.7
        vmovupd   xmm10, XMMWORD PTR [rbx+r15]                  ;174.65
        vmovupd   xmm3, XMMWORD PTR [r10+r9]                    ;173.64
        vmovupd   xmm7, XMMWORD PTR [r8+rcx]                    ;173.19
        inc       r11                                           ;173.19
        vinsertf128 ymm11, ymm10, XMMWORD PTR [16+rbx+r15], 1   ;174.65
        add       rbx, r12                                      ;173.19
        vmulpd    ymm12, ymm0, ymm11                            ;174.65
        vmulpd    ymm13, ymm12, ymm2                            ;174.96
        vmulpd    ymm15, ymm13, ymm2                            ;174.96
        vinsertf128 ymm4, ymm3, XMMWORD PTR [16+r10+r9], 1      ;173.64
        add       r10, r14                                      ;173.19
        vmulpd    ymm5, ymm1, ymm4                              ;173.64
        vmulpd    ymm9, ymm5, ymm2                              ;173.66
        vinsertf128 ymm8, ymm7, XMMWORD PTR [16+r8+rcx], 1      ;173.19
        add       r8, rax                                       ;173.19
        vaddpd    ymm14, ymm8, ymm9                             ;173.19
        vaddpd    ymm3, ymm14, ymm15                            ;172.15
        vmovupd   YMMWORD PTR [352+r13+rdx], ymm3               ;172.15
        add       rdx, 32                                       ;173.19
        cmp       r11, 3                                        ;173.19
        jb        .B1.8         ; Prob 66%                      ;173.19
.B1.9::                         ; Preds .B1.8
        xor       ecx, ecx                                      ;173.19
        xor       edx, edx                                      ;
.B1.10::                        ; Preds .B1.10 .B1.9
        vmovupd   ymm1, YMMWORD PTR [256+r13+rdx]               ;192.11
        inc       rcx                                           ;173.19
        vsubpd    ymm2, ymm1, YMMWORD PTR [352+r13+rdx]         ;192.11
        vmovupd   YMMWORD PTR [608+r13+rdx], ymm2               ;192.11
        add       rdx, 32                                       ;173.19
        cmp       rcx, 3                                        ;173.19
        jb        .B1.10        ; Prob 66%                      ;173.19

PAOS コードは、164 の命令 (および ALIGN 16 パディング用の NOP) を生成します。

以前のソースでは 1 つのループを 3 回反復していましたが、PAOS コードでは、TOSVX1、TOSVX2、SUI の結果をそれぞれ計算する 3 つのループを 3 回反復するようになりました。これらのループは比較的短く、大規模なインデックス計算はループエントリーの前に一度だけ行われます。実行パスは 98 命令増えて 4 つの SUI 結果で合計 262 命令、つまり SUI 結果あたり 65.5 命令になりました。1 つの SUI 結果で 214 の命令を生成した AOS 手法と比較すると、このコードセクションで約 3.26 倍スピードアップしたことになります。この概算はメモリーおよびキャッシュのレイテンシーを考慮していません。

倍精度浮動小数点のベクトル化は、大部分が 4 要素幅になりました。

        vmovupd   ymm1, YMMWORD PTR [160+r13]                   ;204.10
        vmovupd   xmm2, XMMWORD PTR [32+rdi]                    ;178.94
...
        vmulpd    ymm12, ymm0, ymm11                            ;178.63
        vmulpd    ymm13, ymm12, ymm2                            ;178.94
        vmulpd    ymm15, ymm13, ymm2                            ;178.94

コンパイラーが上記のケースで 4 要素幅のロードを選択した理由は、分割キャッシュラインのロードの潜在的なペナルティーが、フェッチした変数を複数回再利用することによるパフォーマンス・ゲインよりも少ないと判断したためです。

一部のフェッチはまだ 2 ステップで行われ、

        vmovupd   xmm10, XMMWORD PTR [rbx+r15]                  ;174.65
	...
        vinsertf128 ymm11, ymm10, XMMWORD PTR [16+rbx+r15], 1   ;174.65

潜在的なキャッシュラインの分割ロードによるパフォーマンス低下を回避します。

コンパイラーはローカル一時変数 TOSVX1(3)、TOSVX2(3)、SUI(3) のアライメントを設定するため、これらの変数への 4 要素幅のアクセスではキャッシュラインの分割問題は発生しません。

PAOS には次の利点があります。

ビードごとにインデックス計算を行う点は同様ですが、テザー数は 1/4 です。
多くのフェッチとストアは 4 要素幅で、一部のみ 2 要素幅です。
倍精度の加算、減算、乗算は 4 要素幅です。
コードがより短いため L1 命令キャッシュがより効率良く利用されます。

(FORTRAN に詳しくない) C++ プログラマー向けの注意点

サンプルコードでは、配列へのポインターを使用しました。

AOS

    real, pointer :: rBeadIPIF(:,:)
    real, pointer :: rBeadDIPIF(:,:)
    real, pointer :: rBeadDDIPIF(:,:)
...
    rBeadIPIF => pFiniteSolution.rBeadIPIF
    rBeadDIPIF => pFiniteSolution.rBeadDIPIF
    rBeadDDIPIF => pFiniteSolution.rBeadDDIPIF

PAOS

    type(TypeYMM), pointer :: rBeadIPIF(:,:)
    type(TypeYMM), pointer :: rBeadDIPIF(:,:)
    type(TypeYMM), pointer :: rBeadDDIPIF(:,:)
...
    rBeadIPIF => pFiniteSolutionSIMD.rBeadIPIF
    rBeadDIPIF => pFiniteSolutionSIMD.rBeadDIPIF
    rBeadDDIPIF => pFiniteSolutionSIMD.rBeadDDIPIF

両バージョンとも、これらのポインターは、それぞれ pFiniteSolution および pFiniteSolutionSIMD 構造の配列全体を指します。これらも同様に配列へのポインターです。FORTRAN では、ポインターは連続しない配列部分を指すことができます。

    real, pointer :: f(:,:)
    type(TypeYMM), pointer :: t(:,:)
    real, pointer:: slice(:,:)
      ...
        s = 2 ! 3rd cell of 4-wide v(0:3)
    slice(LBOUND(f, DIM=1):UBOUND(f, DIM=1), LBOUND(f, DIM=2):UBOUND(f, DIM=2)) => &
& t(LBOUND(f, DIM=1), LBOUND(f, DIM=2))%v(s::4)
    f => slice

上記のソースで、ポインター f のストライドは 4 (double) で、pFiniteSolutionSIMD 構造の配列ポインターのストライドは 1 であると仮定し、v(s) を s=2 で開始しています。f では (3,0:NBEAD+1) のような 2 次元配列として表現され、インデックスは 4 要素幅ベクトルの 2 次元配列で、v 配列の列 s に付けられます。C++ プログラマーの皆さんは、なぜこんなことをする必要があるのか、疑問に思われることでしょう。

この疑問に答えるには、コードの 25% のみ PAOS を使用するように変換されたことを思い出してください。コードの残りの 75% はどうでしょうか。結局、ポインターとストライドが 1 でない代入を使用しても、コードの残りの 75% は、非 PAOS で割り当てられたすべての SIMD 割り当て配列を操作するまで変更できません。この機能により、アプリケーションの変換が偶然の産物になることはありません。FORTRAN のこの機能を使用しないと、複数のデータセットを続けて同期しなければなりません。

ストライドは 1 でない可能性があるため、FORTRAN コンパイラーは複数のコードパス (stride=1 の場合と stride != 1 の場合) を生成します。この文は修正するべきです。プロセッサーの命令セットが *1、*2、*4、*8 で配列セルをスケールできる場合、配列が言語の「自然な」数 (char、word、integer、float、double) であれば、上記のコードは正しくなります。Type(TypeYMM) 配列のケースでは、配列の各セルは 4 つの double (32 バイト) で構成されます。命令セットは *32 をスケールできないため、1 つの実行パスのみ生成されます。オーバーヘッドはほとんどありません。

add       rdx, 32                                       ;173.19

C++ の等価コード

C++ プログラマーのために、等価な例を説明します。

struct xyz_t
{
	double xyz[3];
};

struct FiniteSoluton_t
{
	double	rDELTIS;
	int	nBeads;

	xyz_t*	rBeadIPIF;
	xyz_t*	rBeadDIPIF;
	xyz_t*	rBeadDDIPIF;

	FiniteSoluton_t()
	{
		nBeads = 0;
		rBeadIPIF = NULL;
		rBeadDIPIF = NULL;
		rBeadDDIPIF = NULL;
	}
	void Allocate(int n)
	{
		if(rBeadIPIF) delete [] rBeadIPIF;
		if(rBeadDIPIF) delete [] rBeadDIPIF;
		if(rBeadDDIPIF) delete [] rBeadDDIPIF;
		nBeads = n;
		rBeadIPIF = new xyz_t[n];
		rBeadDIPIF = new xyz_t[n];
		rBeadDDIPIF = new xyz_t[n];
	}
};

FiniteSoluton_t	fs;

// dummy external function to force optimizer NOT to remove unused code
// *** this will generate a link error, ignore error and look at .ASM file
void foo(xyz_t& x);

void TENSEG(FiniteSoluton_t* pFiniteSolution)
{
	int JSEG;
	// pull in frequently used items from fs struct
	int JSEGlast = pFiniteSolution->nBeads+1;
	double	rDELTIS = pFiniteSolution->rDELTIS;
	xyz_t*	rBeadIPIF = pFiniteSolution->rBeadIPIF;
	xyz_t*	rBeadDIPIF = pFiniteSolution->rBeadDIPIF;
	xyz_t*	rBeadDDIPIF = pFiniteSolution->rBeadDDIPIF;
	xyz_t	TOSVX1, TOSVX2, SUI;
	for(JSEG=0; JSEG < JSEGlast; ++JSEG)
	{
        for(int i=0; i<3; ++i)
		{

            // Determine TUI at 1/2 integration step from now
            // using current velocity and acceleration
            // Position of X-End of segment
            TOSVX1.xyz[i] = rBeadIPIF[JSEG].xyz[i]
             + (rBeadDIPIF[JSEG].xyz[i]*(rDELTIS/2.0))
             + (rBeadDDIPIF[JSEG].xyz[i]*(rDELTIS*rDELTIS/4.0));
            // Position of Y-End of segment
            TOSVX2.xyz[i] = rBeadIPIF[JSEG+1].xyz[i]
             + (rBeadDIPIF[JSEG+1].xyz[i]*(rDELTIS/2.0))
             + (rBeadDDIPIF[JSEG+1].xyz[i]*(rDELTIS*rDELTIS/4.0));
          // SUI X->Y
          SUI.xyz[i] = TOSVX2.xyz[i] - TOSVX1.xyz[i];
		} // for(int i=0; i<3; ++i)
		// insert code to assure optimizer does not remove loop
		foo(SUI);
	} // for(JSEG=0; JSEG < JSEGlast; ++JSEG)
}

PAOS コード

#include < dvec.h >

struct xyzF64vec4
{
	F64vec4 xyz[3];
};

struct FiniteSolutonF64vec4
{
	F64vec4	rDELTIS;
	int	nBeads;

	xyzF64vec4*	rBeadIPIF;
	xyzF64vec4*	rBeadDIPIF;
	xyzF64vec4*	rBeadDDIPIF;

	FiniteSolutonF64vec4()
	{
		nBeads = 0;
		rBeadIPIF = NULL;
		rBeadDIPIF = NULL;
		rBeadDDIPIF = NULL;
	}
	void Allocate(int n)
	{
		if(rBeadIPIF) delete [] rBeadIPIF;
		if(rBeadDIPIF) delete [] rBeadDIPIF;
		if(rBeadDDIPIF) delete [] rBeadDDIPIF;
		nBeads = n;
		rBeadIPIF = new xyzF64vec4[n];
		rBeadDIPIF = new xyzF64vec4[n];
		rBeadDDIPIF = new xyzF64vec4[n];
	}
};

FiniteSolutonF64vec4	fsF64vec4;

// dummy external functon to force optimizer NOT to remove unused code
// *** this will generate a link error, ignore error and look at .ASM file
void foo(xyzF64vec4& x);

void TENSEG(FiniteSolutonF64vec4* pFiniteSolution)
{
	int JSEG;
	// pull in frequrently used items from fs struct
	int JSEGlast = pFiniteSolution->nBeads+1;
	F64vec4	rDELTIS = pFiniteSolution->rDELTIS;
	xyzF64vec4*	rBeadIPIF = pFiniteSolution->rBeadIPIF;
	xyzF64vec4*	rBeadDIPIF = pFiniteSolution->rBeadDIPIF;
	xyzF64vec4*	rBeadDDIPIF = pFiniteSolution->rBeadDDIPIF;
	xyzF64vec4	TOSVX1, TOSVX2, SUI;
	for(JSEG=0; JSEG < JSEGlast; ++JSEG)
	{
        for(int i=0; i<3; ++i)
		{

            // Determine TUI at 1/2 integration step from now
            // using current velocity and acceleration
            // Position of X-End of segment
            TOSVX1.xyz[i] = rBeadIPIF[JSEG].xyz[i]
             + (rBeadDIPIF[JSEG].xyz[i]*(rDELTIS/2.0))
             + (rBeadDDIPIF[JSEG].xyz[i]*(rDELTIS*rDELTIS/4.0));
            // Position of Y-End of segment
            TOSVX2.xyz[i] = rBeadIPIF[JSEG+1].xyz[i]
             + (rBeadDIPIF[JSEG+1].xyz[i]*(rDELTIS/2.0))
             + (rBeadDDIPIF[JSEG+1].xyz[i]*(rDELTIS*rDELTIS/4.0));
          // SUI X->Y
          SUI.xyz[i] = TOSVX2.xyz[i] - TOSVX1.xyz[i];
		} // for(int i=0; i<3; ++i)
		// insert code to assure optimizer does not remove loop
		foo(SUI);
	} // for(JSEG=0; JSEG < JSEGlast; ++JSEG)
}

型宣言以外、コードセクションは同じです。

PAOS で変更するコードセクション

PAOS でプログラミングを行う場合、ソースコードを追加する必要があります。1 つの例は、ゼロ除算を回避するコードです。次に例を示します。

xyz_t& unitVector(xyz_t& v)
{
  xyz_t ret;
  double Length = sqrt(
            v.xyz[0]*v.xyz[0]
            + v.xyz[1]*v.xyz[1]
            + v.xyz[2]*v.xyz[2]);
  // defensive code to avoid divide by 0
  // When (if) Length = 0.0 then substitute 1.0
  // X = 0.0 / 1.0 = uX 0.0 (not divide by 0)
  // Y = 0.0 / 1.0 = uY 0.0 (not divide by 0)
  // Z = 0.0 / 1.0 = uZ 0.0 (not divide by 0)
  if(Length == 0.0) Length = 1.0;
  ret.xyz[0] = v.xyz[0] / Length;
  ret.xyz[1] = v.xyz[1] / Length;
  ret.xyz[2] = v.xyz[2] / Length;
  return ret;
}

変更後:
xyzF64vec4& unitVector(xyzF64vec4& v)
{
	xyzF64vec4 ret;
	static const F64vec4	Zero(0.0);
	static const F64vec4	One(1.0);
	F64vec4 Length = sqrt(
		v.xyz[0]*v.xyz[0]
		+ v.xyz[1]*v.xyz[1]
		+ v.xyz[2]*v.xyz[2]);
	// defensive code to avoid divide by 0
	// When (if) Length = 0.0 then substitute 1.0
	// X = 0.0 / 1.0 = uX 0.0 (not divide by 0)
	// Y = 0.0 / 1.0 = uY 0.0 (not divide by 0)
	// Z = 0.0 / 1.0 = uZ 0.0 (not divide by 0)
	Length = select_eq(Length, Zero, Length, One);
	ret.xyz[0] = v.xyz[0] / Length;
	ret.xyz[1] = v.xyz[1] / Length;
	ret.xyz[2] = v.xyz[2] / Length;
	return ret;
}

型宣言以外に、1 つの文のみ変更されています。

アセンブリー・コードにおける PAOS の利点

PAOS の実際の利点はアセンブリー・コードを見ると分かります。PAOS コードは一度に 4 つのエンティティーを処理しています。オリジナルの AOS コードと変更後の PAOS コードを比較してみましょう。ソースコードは同じです。唯一の違いはデータ型です。ソースコードを次に示します。

    for(int i=0; i<3; ++i)
    {
      // Determine TUI at 1/2 integration step from now
      // using current velocity and acceleration
      // Position of X-End of segment
      TOSVX1.xyz[i] = rBeadIPIF[JSEG].xyz[i]
        + (rBeadDIPIF[JSEG].xyz[i]*(rDELTIS/2.0))
        + (rBeadDDIPIF[JSEG].xyz[i]*(rDELTIS*rDELTIS/4.0));
      // Position of Y-End of segment
      TOSVX2.xyz[i] = rBeadIPIF[JSEG+1].xyz[i]
        + (rBeadDIPIF[JSEG+1].xyz[i]*(rDELTIS/2.0))
        + (rBeadDDIPIF[JSEG+1].xyz[i]*(rDELTIS*rDELTIS/4.0));
      // SUI X->Y
      SUI.xyz[i] = TOSVX2.xyz[i] - TOSVX1.xyz[i];
    } // for(int i=0; i<3; ++i)

AOS アセンブリー・コード

;;;         for(int i=0; i<3; ++i)
;;; 		{
;;;
;;;             // Determine TUI at 1/2 integration step from now
;;;             // using current velocity and acceleration
;;;             // Position of X-End of segment
;;;             TOSVX1.xyz[i] = rBeadIPIF[JSEG].xyz[i]
;;;              + (rBeadDIPIF[JSEG].xyz[i]*(rDELTIS/2.0))
;;;              + (rBeadDDIPIF[JSEG].xyz[i]*(rDELTIS*rDELTIS/4.0));
;;;             // Position of Y-End of segment
;;;             TOSVX2.xyz[i] = rBeadIPIF[JSEG+1].xyz[i]
        vmulsd    xmm1, xmm2, QWORD PTR [_2il0floatpacket.42]   ;66.13
        vmulsd    xmm2, xmm2, xmm2                              ;66.13
        vmulsd    xmm3, xmm2, QWORD PTR [_2il0floatpacket.43]   ;66.13
        vmovddup  xmm0, xmm1                                    ;66.13
        vmovddup  xmm2, xmm3                                    ;66.13
        vmovups   XMMWORD PTR [80+rsp], xmm6                    ;66.13
        vmovapd   xmm6, xmm2                                    ;66.13
        vmovups   XMMWORD PTR [64+rsp], xmm7                    ;66.13
        vmovapd   xmm7, xmm1                                    ;66.13
        vmovups   XMMWORD PTR [48+rsp], xmm8                    ;66.13
        vmovapd   xmm8, xmm3                                    ;66.13
        vmovups   XMMWORD PTR [32+rsp], xmm9                    ;66.13
        vmovapd   xmm9, xmm0                                    ;66.13
        mov       QWORD PTR [136+rsp], rbx                      ;66.13
        mov       rbx, rax                                      ;66.13
        mov       QWORD PTR [128+rsp], rsi                      ;66.13
        mov       rsi, rdx                                      ;66.13
        mov       QWORD PTR [120+rsp], rdi                      ;66.13
        mov       rdi, rcx                                      ;66.13
        mov       QWORD PTR [112+rsp], r12                      ;66.13
        mov       r12, r8                                       ;66.13
        mov       QWORD PTR [104+rsp], r13                      ;66.13
        mov       r13, r9                                       ;66.13
        mov       QWORD PTR [96+rsp], r14                       ;66.13
        mov       r14, r10                                      ;66.13
.B5.3::                         ; Preds .B5.4 .B5.2
        vmulpd    xmm4, xmm9, XMMWORD PTR [24+rbx+r12]          ;66.13
        vaddpd    xmm5, xmm4, XMMWORD PTR [24+rbx+r13]          ;66.13
        vmulpd    xmm4, xmm6, XMMWORD PTR [24+rbx+rdi]          ;66.13
        vaddpd    xmm0, xmm5, xmm4                              ;66.13
        vmulpd    xmm4, xmm9, XMMWORD PTR [rbx+r12]             ;62.13
        vmulpd    xmm5, xmm6, XMMWORD PTR [rbx+rdi]             ;62.13
        vaddpd    xmm4, xmm4, XMMWORD PTR [rbx+r13]             ;62.13
        vaddpd    xmm4, xmm4, xmm5                              ;62.13
;;;              + (rBeadDIPIF[JSEG+1].xyz[i]*(rDELTIS/2.0))
;;;              + (rBeadDDIPIF[JSEG+1].xyz[i]*(rDELTIS*rDELTIS/4.0));
;;;           // SUI X->Y
;;;           SUI.xyz[i] = TOSVX2.xyz[i] - TOSVX1.xyz[i];
        vsubpd    xmm4, xmm0, xmm4                              ;70.11
        vmovupd   XMMWORD PTR [144+rsp], xmm4                   ;70.11
;;; 		} // for(int i=0; i<3; ++i)
        lea       rcx, QWORD PTR [144+rsp]                      ;73.3
        vmulsd    xmm4, xmm7, QWORD PTR [40+rbx+r12]            ;66.13
        vmulsd    xmm5, xmm8, QWORD PTR [40+rbx+rdi]            ;66.13
        vaddsd    xmm4, xmm4, QWORD PTR [40+rbx+r13]            ;66.13
        vaddsd    xmm0, xmm4, xmm5                              ;66.13
        vmulsd    xmm4, xmm7, QWORD PTR [16+rbx+r12]            ;62.13
        vmulsd    xmm5, xmm8, QWORD PTR [16+rbx+rdi]            ;62.13
        vaddsd    xmm4, xmm4, QWORD PTR [16+rbx+r13]            ;62.13
        vaddsd    xmm4, xmm4, xmm5                              ;62.13
        vsubsd    xmm4, xmm0, xmm4                              ;70.11
        vmovsd    QWORD PTR [160+rsp], xmm4                     ;70.11

AOS では、2 要素幅のベクトルとスカラー命令を使用して、46 命令で 1 つの SUI ベクトルが計算されました。このループはアンロールされました。

PAOS アセンブリー・コード

;;;         for(int i=0; i<3; ++i)
;;; 		{
;;;
;;;             // Determine TUI at 1/2 integration step from now
;;;             // using current velocity and acceleration
;;;             // Position of X-End of segment
;;;             TOSVX1.xyz[i] = rBeadIPIF[JSEG].xyz[i]
        vdivpd    ymm1, ymm0, YMMWORD PTR [_2il0floatpacket.437] ;213.13
        vmulpd    ymm0, ymm0, ymm0                              ;213.13
        vmovupd   YMMWORD PTR [288+r13], ymm1                   ;213.13
        vdivpd    ymm0, ymm0, YMMWORD PTR [_2il0floatpacket.438] ;213.13
        vmovupd   YMMWORD PTR [320+r13], ymm0                   ;213.13
        mov       QWORD PTR [456+rsp], rbx                      ;213.13
        mov       rbx, rax                                      ;213.13
        mov       QWORD PTR [448+rsp], rsi                      ;213.13
        mov       rsi, rdx                                      ;213.13
        mov       QWORD PTR [440+rsp], rdi                      ;213.13
        mov       rdi, rcx                                      ;213.13
        mov       QWORD PTR [432+rsp], r12                      ;213.13
        mov       r12, r8                                       ;213.13
        mov       QWORD PTR [424+rsp], r14                      ;213.13
        mov       r14, r9                                       ;213.13
        mov       QWORD PTR [416+rsp], r15                      ;213.13
        mov       r15, r10                                      ;213.13
.B5.3::                         ; Preds .B5.4 .B5.2
        vmovupd   ymm4, YMMWORD PTR [288+r13]                   ;213.13
;;;              + (rBeadDIPIF[JSEG].xyz[i]*(rDELTIS/2.0))
;;;              + (rBeadDDIPIF[JSEG].xyz[i]*(rDELTIS*rDELTIS/4.0));
;;;             // Position of Y-End of segment
;;;             TOSVX2.xyz[i] = rBeadIPIF[JSEG+1].xyz[i]
;;;              + (rBeadDIPIF[JSEG+1].xyz[i]*(rDELTIS/2.0))
;;;              + (rBeadDDIPIF[JSEG+1].xyz[i]*(rDELTIS*rDELTIS/4.0));
;;;           // SUI X->Y
;;;           SUI.xyz[i] = TOSVX2.xyz[i] - TOSVX1.xyz[i];
;;; 		} // for(int i=0; i<3; ++i)
;;; 		// insert code to assure optimizer does not remove loop
;;; 		foo(SUI);
        lea       rcx, QWORD PTR [192+r13]                      ;224.3
        vmovupd   ymm3, YMMWORD PTR [320+r13]                   ;213.13
        vmulpd    ymm5, ymm4, YMMWORD PTR [rbx+r12]             ;213.13
        vmulpd    ymm1, ymm3, YMMWORD PTR [rbx+rdi]             ;213.13
        vaddpd    ymm0, ymm5, YMMWORD PTR [rbx+r14]             ;213.13
        vmulpd    ymm5, ymm4, YMMWORD PTR [96+rbx+r12]          ;217.13
        vaddpd    ymm2, ymm0, ymm1                              ;213.13
        vmulpd    ymm1, ymm3, YMMWORD PTR [96+rbx+rdi]          ;217.13
        vmovupd   YMMWORD PTR [r13], ymm2                       ;213.13
        vaddpd    ymm0, ymm5, YMMWORD PTR [96+rbx+r14]          ;217.13
        vaddpd    ymm5, ymm0, ymm1                              ;217.13
        vsubpd    ymm2, ymm5, ymm2                              ;221.11
        vmovupd   YMMWORD PTR [96+r13], ymm5                    ;217.13
        vmovupd   YMMWORD PTR [192+r13], ymm2                   ;221.11
        vmulpd    ymm0, ymm4, YMMWORD PTR [32+rbx+r12]          ;213.13
        vmulpd    ymm2, ymm3, YMMWORD PTR [32+rbx+rdi]          ;213.13
        vmulpd    ymm5, ymm4, YMMWORD PTR [128+rbx+r12]         ;217.13
        vaddpd    ymm1, ymm0, YMMWORD PTR [32+rbx+r14]          ;213.13
        vaddpd    ymm0, ymm5, YMMWORD PTR [128+rbx+r14]         ;217.13
        vaddpd    ymm1, ymm1, ymm2                              ;213.13
        vmulpd    ymm2, ymm3, YMMWORD PTR [128+rbx+rdi]         ;217.13
        vmovupd   YMMWORD PTR [32+r13], ymm1                    ;213.13
        vaddpd    ymm5, ymm0, ymm2                              ;217.13
        vsubpd    ymm1, ymm5, ymm1                              ;221.11
        vmovupd   YMMWORD PTR [128+r13], ymm5                   ;217.13
        vmovupd   YMMWORD PTR [224+r13], ymm1                   ;221.11
        vmulpd    ymm0, ymm4, YMMWORD PTR [64+rbx+r12]          ;213.13
        vmulpd    ymm2, ymm3, YMMWORD PTR [64+rbx+rdi]          ;213.13
        vmulpd    ymm4, ymm4, YMMWORD PTR [160+rbx+r12]         ;217.13
        vmulpd    ymm3, ymm3, YMMWORD PTR [160+rbx+rdi]         ;217.13
        vaddpd    ymm1, ymm0, YMMWORD PTR [64+rbx+r14]          ;213.13
        vaddpd    ymm0, ymm1, ymm2                              ;213.13
        vaddpd    ymm1, ymm4, YMMWORD PTR [160+rbx+r14]         ;217.13
        vmovupd   YMMWORD PTR [64+r13], ymm0                    ;213.13
        vaddpd    ymm1, ymm1, ymm3                              ;217.13
        vsubpd    ymm0, ymm1, ymm0                              ;221.11
        vmovupd   YMMWORD PTR [160+r13], ymm1                   ;217.13
        vmovupd   YMMWORD PTR [256+r13], ymm0                   ;221.11

PAOS は、4 要素幅のベクトルをすべて使用して一度に (4 つの異なるテザーの) 4 つのセグメントを計算します。for ループはアンロールされ、生成されたコードは 4 つの SUI 結果を 56 命令で計算しました。1 つの SUI 結果あたりの命令数は 56/4 = 14 となります。

AOS では 46 命令で 1 つの SUI を計算したのに対して、PAOS では 14 命令で 1 つの SUI を計算しました。命令あたりの時間が等しいと仮定すると、(このコードサンプルでは) PAOS は AOS に対して 3.28 倍パフォーマンスが向上したことになります。

注意: アプリケーション全体で実際に 3.28 倍パフォーマンスが向上すると仮定しないでください。

すべてのアプリケーションで AOS から PAOS が使用できるとは限りませんが、アプリケーションの一部は変換によるメリットを得られるでしょう。この後、テザーシステム研究用の FORTRAN アプリケーションで PAOS に変換したコードを使用した結果を紹介します。このアプリケーションでは、コードの 25% のみを PAOS に変更しました (大部分はコピー・アンド・ペースト、検索、置換)。宇宙船は 96 のテザーのコレクションで相互接続されます。この設定では、パフォーマンスの向上は 2 倍になります。上記のコードの 3.28 倍には届きませんが、プログラミングに時間をかけるだけの価値はあるといえるでしょう。

プログラム変換手法

テザー・シミュレーション・プログラムでは、“type TypeFiniteSolution” の本体を別のファイル (FiniteSolution.inc) に分割しました。型定義を含むモジュールのコンパイルには、FORTRAN プリプロセッサー (FPP) を使用します。

! Note, "real" is not defined at this point
! i.e. "real" in the FPP #include file passes through as "real"
type TypeFiniteSolution
    SEQUENCE
#include "FiniteSolution.inc"
    ! No additional data to the original type
end type TypeFiniteSolution

! XMM, YMM and YMM02 variants replace "real" with
! "type(TypeXMM)", "type(TypeYMM)" and "type(TypeYMM02)"
! XMM variant
#define real type(TypeXMM)
type TypeFiniteSolutionXMM
    SEQUENCE
#include "FiniteSolution.inc"
    ! additional members for TypeXMM
    integer :: TetherNumber0
    integer :: TetherNumber1
end type TypeFiniteSolutionXMM
#undef real

! YMM variant
#define real type(TypeYMM)
type TypeFiniteSolutionYMM
    SEQUENCE
#include "FiniteSolution.inc"
    ! additional members for TypeYMM
    integer :: TetherNumber0
    integer :: TetherNumber1
    integer :: TetherNumber2
    integer :: TetherNumber3
end type TypeFiniteSolutionYMM
#undef real

! YMM02 variant
#define real type(TypeYMM02)
type TypeFiniteSolutionYMM02
    SEQUENCE
#include "FiniteSolution.inc"
    ! additional members for TypeYMM02
    integer :: TetherNumber0
    integer :: TetherNumber1
    integer :: TetherNumber2
end type TypeFiniteSolutionYMM02
#undef real

FPP を使用して “real” を適切な型に再定義することにより、同じ手法をサブルーチンと関数に使用できます。非 SIMD では通常、サフィックス “.v” は含まれないため、SIMD 型変数の影響を受けるサブルーチンと関数では 2 つのバージョンが必要になります。

ジェネリック・サブルーチンと関数のインターフェイス

ジェネリック・サブルーチンと関数のインターフェイスの使用は、PAOS への変換の主な要件です。次元 3 の 2 つの配列の CROSS 積の要件について見てみましょう。

    interface CROSS
        RECURSIVE SUBROUTINE CROSS_r_r_r(VA,VB,VC)
            use MOD_ALL
            real :: VA(3), VB(3), VC(3)
        END SUBROUTINE CROSS_r_r_r

        RECURSIVE SUBROUTINE CROSS_r_r_ymm(VA,VB, VC)
	        USE MOD_ALL
            real :: VA(3), VB(3)
            type(TypeYMM) :: VC(3)
        END SUBROUTINE CROSS_r_r_ymm

        RECURSIVE SUBROUTINE CROSS_r_r_ymm02(VA,VB, VC)
	        USE MOD_ALL
            real :: VA(3), VB(3)
            type(TypeYMM02) :: VC(3)
        END SUBROUTINE CROSS_r_r_ymm02

        RECURSIVE SUBROUTINE CROSS_r_r_xmm(VA,VB, VC)
	        USE MOD_ALL
            real :: VA(3), VB(3)
            type(TypeXMM) :: VC(3)
        END SUBROUTINE CROSS_r_r_xmm

        RECURSIVE SUBROUTINE CROSS_ymm_ymm_ymm(VA,VB,VC)
            use MOD_ALL
            type(TypeYMM) :: VA(3), VB(3), VC(3)
        END SUBROUTINE CROSS_ymm_ymm_ymm

        RECURSIVE SUBROUTINE CROSS_ymm02_ymm02_ymm02(VA,VB,VC)
            use MOD_ALL
            type(TypeYMM02) :: VA(3), VB(3), VC(3)
        END SUBROUTINE CROSS_ymm02_ymm02_ymm02

        RECURSIVE SUBROUTINE CROSS_xmm_xmm_xmm(VA,VB, VC)
            use MOD_ALL
            type(TypeXMM) :: VA(3), VB(3), VC(3)
        END SUBROUTINE CROSS_xmm_xmm_xmm
    end interface CROSS

一般的なアプリケーションでは、さまざまな (すべてではない) 引数の型が置換されることに注意してください。この処理にはジェネリック・サブルーチン・インターフェイスを使用します。

! EVAL ANGULAR MOMENTUM DERIVATIVE TERM
    CALL CROSS (RPI,RPIDD,  pFiniteSolutionSIMD.rRVDI)

上記では CROSS_r_r_ymm を呼び出します。

! FORM VECTOR IN DIRECTION OF TETHER FRAME "K" UNIT VECTOR
    CALL CROSS (pFiniteSolutionSIMD.rUITI, pFiniteSolutionSIMD.rUJOI,  pFiniteSolutionSIMD.rUKTI)

上記では CROSS_ymm_ymm_ymm を呼び出します。

各サブルーチンは、異なる PAOS 幅 (2 要素幅、3 要素幅、4 要素幅) 用にオリジナルの AOS レイアウトをコピーしたものです。この点は、テンプレートを使用できる C++ のほうが楽です。FORTRAN の場合、Visual Studio* でサブルーチンを選択して、サブルーチンのコンテンツをすべて選択し、クリップボードにコピーします。ソリューション・エクスプローラーでプロジェクトをクリックして、項目の追加を選択し、オリジナルの名前にサフィックス (ここでは、ymm、ymm02、xmm を使用) を加えたファイルを追加します。そして、オリジナルのサブルーチンのコンテンツを新しい空のファイルに貼り付けます。残りのステップは、型の変更と適切な USE モジュールの追加です。

データの変更についても考慮する必要があります。FORTRAN で演算子関数を使用すれば、変数のサフィックス “.v” を検索および置換して削除できますが、コンパイラーがコードをインライン展開しなくなるため、今回はその方法を採用していません。このインライン問題は、この記事が掲載される頃には製品アップデートで解消される予定です。

コードセクションの一部は、PAOS ベクトルを水平にアドレスする必要があります。すべての異なる PAOS 幅で同じコードテキストになるように、FORTRAN サブルーチンに次のコードを追加します。

    integer :: s
    integer, parameter :: sLast = 3

以下のように使用しました。

! FORM UNIT VECTOR FROM X-END TO Y-END OF BEAD SEGMENT
     do s=0,sLast
         if(DUMEL.v(s) .ne. 0.0_8) then
            TUI.v(s) = SUI.v(s) / DUMEL.v(s)
         else
            call RandomUnitVector(TUI.v(s))
         endif
     end do

(“parameter” は C++ の “static const” のようなものです)

上記のコードは、NULL ベクトルを返す代わりに任意の単位ベクトルを返すことを除いてゼロ除算を回避しています。上記の変更は AOS (非 PAOS) として動作します。

NULL ベクトルを生成する場合、コードは次のようになります。

RadiusSquared.v = RelativePOSE_IF(1).v**2 &
     & + RelativePOSE_IF(2).v**2 &
     & + RelativePOSE_IF(3).v**2
 ! N.B we are comparing a bead position within a tether against
 ! a gravitational body position they should not coincide
 ! ignore if at center of object
 !DEC$ VECTOR ALWAYS
 do s = 0, sLast
   if(RadiusSquared.v(s) .eq. 0.0_8) RadiusSquared.v(s) = 1.0_8
 end do
 Radius.v = dsqrt(RadiusSquared.v)
 ACCE_IFlocal(1).v = RelativePOSE_IF(1).v &
    & * ((pObject.rGMft3Ps2 / RadiusSquared.v) / Radius.v)
 ACCE_IFlocal(2).v = RelativePOSE_IF(2).v &
    & * ((pObject.rGMft3Ps2 / RadiusSquared.v) / Radius.v)
 ACCE_IFlocal(3).v = RelativePOSE_IF(3).v &
    & * ((pObject.rGMft3Ps2 / RadiusSquared.v) / Radius.v)

上記のコードに関する最適化の注意点

ゼロ除算を回避するコードは、ベクトルの 2 乗をテストするために移動されています。パスにかかわらず結果は同じになりますが、dsqrt が 0.0 の平方根を実行することはありません。プロセッサーのマイクロコードに応じて、0.0 の平方根のパスは長くなります。

上記の s ループ (定数 sLast) はアンロールおよびベクトル化されます。このループは分岐命令なしで完全にベクトル化されます。

AOS に対する PAOS の利点

AOS は、NumberOfBeads に関連して割り当てられる大きな配列をベクトル化できますが、次元 (3) および (3,3) の小さな固定配列をベクトル化する可能性は限定的で、スカラー操作はベクトル化できません。PAOS は、スカラー、そして次元 (3) および (3,3) の小さな固定配列も完全にベクトル化でき、NumberOfBeads に関連して割り当てられる大きな配列をベクトル化する可能性も高くなります。

スカラー計算の利点

各テザーで繰り返される従来の非 SIMD 文

! CALCULATE 1ST TERM
! AND ACCUMULATE THIS INCREMENT
DUMSCL = DUMCEE * (  2.0_8*(pFiniteSolution%rELD / pFiniteSolution%rEL)**2 &
& - pFiniteSolution%rELDD / pFiniteSolution%rEL  )

4 つのテザーのグループで繰り返される SIMD 文

! CALCULATE 1ST TERM
! AND ACCUMULATE THIS INCREMENT
DUMSCL.v = DUMCEE.v * (  2.0_8*(pFiniteSolution%rELD.v / pFiniteSolution%rEL.v)**2 &
& - pFiniteSolution%rELDD.v / pFiniteSolution%rEL.v  )

コードが少しだけ変更されていることに注意してください。一部のコードは変数 TypeYMM、TypeXMM、TypeYMM02 にサフィックス “.v” を追加して使用されています。このサフィックスは演算子関数を宣言すると削除できますが (後述)、デバッグビルドでコードをインライン展開するために、ここではサフィックスを使用しています。これらのコード変更は、検索と置換を使用して行うことができます。プログラマーが演算子の多重定義関数を使用することを決めた場合、(型宣言以外に) コードを変更する必要はありません。

小さな固定配列による計算の利点

小さな固定サイズの配列の処理は、PAOS の利点の 1 つです。テザーの先端と終端の位置を考慮して、先端と終端間のベクトルおよび先端と終端間の単位ベクトルを計算するとします (コードが読みやすいように pFiniteSolution% ポインターは削除しています)。

    real(8) :: Front(3), Back(3), ELI(3), UELI(3), Length

    ELI(1)   = Back(1) – Front(1)
    ELI(2)   = Back(2) – Front(2)
    ELI(3)   = Back(3) – Front(3)
    Length = DSQRT(ELI(1)**2 + ELI(2)**2 + ELI(3)**2)
    UEIL(1) = ELI(1) / Length
    UEIL(2) = ELI(2) / Length
    UEIL(3) = ELI(3) / Length

スカラー AOS コードは、上記のコードの部分のみをベクトル化できます。SSE プラットフォームでは、Back と Front の 3 つのコンポーネントのうち 2 つのみを一度にロードして減算できます。3 つ目のコンポーネントは別にロードして減算します。“ELI(1)**2 + ELI(2)**2” は 1 つの操作で行うことができますが、“ELI(3)**2” は別の操作になります。残りの問題は、SSE では “ELI(1)**2 + ELI(2)**2” の結果の水平加算をした後に “ELI(3)**2” の追加スカラー加算を実行しなければならないことです。その後、スカラー DSQRT 操作を実行して Length を生成します。スカラーによる ELI(1:3) の除算は、“ELI(1) + ELI(2)” (2 要素幅) と “ELI(3)” コンポーネントの 2 つのステップで行われます。ストアは、ロードのときに説明した 2 つのステップで行われます。

AVX システムでは、コードは var(1:2) と var(3) の 2 つの部分で (YMM レジスターの 4 番目の要素をあらかじめクリアする XOR を追加して) Back と Front をそれぞれロードします。減算は、“ELI(1)**2 + ELI(2)**2 + ELI(3)**2” の 2 乗と同じように、1 つの操作で行われます。しかし、この時点では、AVX コードは 3 つの部分的な結果を水平加算した後にスカラー DSQRT を実行して Length を生成する必要があります。Length による ELI の除算は、YMM レジスターに Length をブロードキャストした後に 1 つの操作で行われます。通常、YMM レジスターの 4 番目の要素が未定義であることが確実でないため、この操作は 1 ステップではなく、2 要素幅、1 要素幅の 2 つのステップで行われます。ロードとストアが、2 つの部分で YMM レジスターの 4 番目の要素をあらかじめクリアする XOR を追加してそれぞれ実行されていることに注意してください。

PAOS は容易にすべてのコードを完全にベクトル化できる

    type(TypeYMM) :: Front(3), Back(3), ELI(3), UELI(3), Length

    ELI(1).v   = Back(1).v – Front(1).v
    ELI(2).v   = Back(2).v – Front(2).v
    ELI(3).v   = Back(3).v – Front(3).v
    Length.v = DSQRT(ELI(1).v**2 + ELI(2).v**2 + ELI(3).v**2)
    UEIL(1).v = ELI(1).v / Length.v
    UEIL(2).v = ELI(2).v / Length.v
    UEIL(3).v = ELI(3).v / Length.v

最初の文で、ELI ベクトルの各要素は 4 要素幅で計算されます (一度に 4 テザー)。部分的な結果は別の 4 要素幅の YMM レジスターで保存されます。2 乗は、各 4 要素幅の 3 つの異なるレジスターで実行されます。3 つの要素の水平加算が、3 つの 4 要素幅の要素の垂直加算になりました。次に、2 乗の 4 要素幅の合計に DSQRT が実行されます (Sandy Bridge では YMM レジスターの半分に DSQRT が内部的に実行され、Ivy Bridge では 4 要素幅の DSQRT が実行されます)。生成された 4 要素幅の長さ (個別の値) は、4 つの単位ベクトルの生成に使用されます。上記のコードは完全な 4 要素幅 SIMD で、1 つのテザーよりも 4 つのテザーでより高速に実行されます (速度はキャッシュラインがどこで分割されるかに依存します)。

大きな配列の利点

大きな配列のループを実行すると、配列が 3 つの (X、Y、Z) コンポーネントを含む場合と同様の利点が得られます。PAOS データフロー計算は次のようになります。

	OBJECT(1)%X(1) | OBJECT(2)%X(1) | OBJECT(3)%X(1) | OBJECT(4)%X(1)
	OBJECT(1)%Y(1) | OBJECT(2)%Y(1) | OBJECT(3)%Y(1) | OBJECT(4)%Y(1)
	OBJECT(1)%Z(1) | OBJECT(2)%Z(1) | OBJECT(3)%Z(1) | OBJECT(4)%Z(1)
	OBJECT(1)%X(2) | OBJECT(2)%X(2) | OBJECT(3)%X(2) | OBJECT(4)%X(2)
	OBJECT(1)%Y(2) | OBJECT(2)%Y(2) | OBJECT(3)%Y(2) | OBJECT(4)%Y(2)
	OBJECT(1)%Z(2) | OBJECT(2)%Z(2) | OBJECT(3)%Z(2) | OBJECT(4)%Z(2)

OBJECT は、アプリケーション・ベクトルの配列を含み、3 ユニットの SIMD 4 要素幅ベクトルに格納された SIMD 4 要素幅ベクトルへ再マップされた構造を表します。

スカラー (オリジナル) 表現

    ! Position with respect to IP IF
    real, pointer :: rBeadIPIF(:,:)   	! (3,NumberOfBeads+2)

AVX 4 要素幅 SIMD 表現

    ! Position with respect to IP IF
    type(TypeYMM), pointer :: rBeadIPIF(:,:)   	! (3,NumberOfBeads+2)

隣接するビード間のベクトルおよびそのベクトルの単位ベクトルを生成するコード。

部分的なベクトル化を含めて最適化するオリジナルのコード (一部):

    DO I=1,nBeads
      ELI(1)   = pFiniteSolution%rBeadIPIF(1,I) – pFiniteSolution%rBeadIPIF(1,I+1)
      ELI(2)   = pFiniteSolution%rBeadIPIF(2,I) – pFiniteSolution%rBeadIPIF(2,I+1)
      ELI(3)   = pFiniteSolution%rBeadIPIF(3,I) – pFiniteSolution%rBeadIPIF(3,I+1)
      Length = DSQRT(ELI(1)**2 + ELI(2)**2 + ELI(3)**2)
      pFiniteSolution%rTUI(1,I) = ELI(1) / Length
      pFiniteSolution%rTUI(2,I) = ELI(2) / Length
      pFiniteSolution%rTUI(3,I) = ELI(3) / Length
   END DO

4 つのテザーを完全にベクトル化する YMM SIMD コード:

    DO I=1,nBeads
      ELI(1).v   = pFiniteSolution%rBeadIPIF(1,I).v – pFiniteSolution%rBeadIPIF(1,I+1).v
      ELI(2).v   = pFiniteSolution%rBeadIPIF(2,I).v – pFiniteSolution%rBeadIPIF(2,I+1).v
      ELI(3).v   = pFiniteSolution%rBeadIPIF(3,I).v – pFiniteSolution%rBeadIPIF(3,I+1).v
      Length = DSQRT(ELI(1).v**2 + ELI(2).v**2 + ELI(3).v**2).v
      pFiniteSolution%rTUI(1,I).v = ELI(1).v / Length
      pFiniteSolution%rTUI(2,I).v = ELI(2).v / Length
      pFiniteSolution%rTUI(3,I).v = ELI(3).v / Length
   END DO

SOA に対する利点

SOA レイアウトでは、PAOS よりも多くのキャッシュラインのロードと TLB ロードが必要です。例えば、テザーのビード間のベクトルと単位ベクトルを生成するケースです。SOA では、以下の個別のキャッシュラインおよび TLB が必要です。

   rBeadIPIF(I,1,t) rBeadIPIF(I+1,1,t) (8 分割のキャッシュライン)
   rBeadIPIF(I,2,t) rBeadIPIF(I+1,2,t) (8 分割のキャッシュライン)
   rBeadIPIF(I,3,t) rBeadIPIF(I+1,3,t) (8 分割のキャッシュライン)
   rTUI(I,1,t) (8 分割のキャッシュライン)
   rTUI(I,2,t) (8 分割のキャッシュライン)
   rTUI(I,3,t) (8 分割のキャッシュライン)

各テザーのビードごとに 6 つのキャッシュラインが必要です。

PAOS では、一度に 4 つのテザーのビードごとに 4 つのキャッシュラインが必要です。

   rBeadIPIF(1,I).v(0:3) rBeadIPIF(1,I+1).v(0:3) (↓ とキャッシュラインを共有)
   rBeadIPIF(2,I).v(0:3) rBeadIPIF(2,I+1).v(0:3) (上記/下記の半分)
   rBeadIPIF(3,I).v(0:3) rBeadIPIF(3,I+1).v(0:3) (↑ とキャッシュラインを共有)
   rTUI(1,I).v(0:3) (↓ とキャッシュラインを共有)
   rTUI(2,I).v(0:3) (上記/下記の半分)
   rTUI(3,I).v(0:3) (↑ とキャッシュラインを共有)
   

PAOS はキャッシュラインを高速にトラバース (高速にフラッシュ) しますが、(SIMD は 4 つのテザーをデータにパックするため) トラバースは 1/4 になります。合計のデータストレージが両方のレイアウトで同じであれば、個別のキャッシュラインおよび TLB エントリーに関する要求は減ります。

ベンチマーク・プログラム

PAOS の利点を説明するため、ベンチマークを使用して実際のアプリケーションと比較しましょう。ベンチマークには、よく知られている複雑なシミュレーション・プログラムを使用しました。これまで 7 年間にわたりコードの最適化を繰り返してきた結果、PAOS への変換前は、コードを改良できる部分はほとんど残されていないと私は考えていました。コードの大部分は容易にベクトル化できないことが分かっていたからです。また、コンパイラーによりベクトル化および最適化されるコードをすべてベクトル化できるとは限らないことも分かっていました。

この状況を打破するために、このアプリケーション用に開発されたのがパックド構造体配列です。SOA 形式も考えましたが、このアプリケーションではあまり効果がないことが分かっていました。

このプログラムは、宇宙船に接続されたテザーのシミュレーション用に高度に最適化された OpenMP* FORTRAN プログラムで、宇宙エレベーター設計問題の調査にも使用されています。ベンチマークとして、EDDE 宇宙船のシミュレーション研究構成を選択しました。この宇宙船は、衛星軌道近くのデブリ (人工衛星の残骸などの宇宙ゴミ) を除去するために提案されたものです。

EDDE 宇宙船 の構成は、これまで説明したテザー衛星よりも長く大規模になります。

ペイロード・マネージャー x 2
コントローラー/エミッター x 2
太陽電池 x 9
ペイロード・マネージャーとコントローラー/エミッターを接続する短いテザー (~15 m) x 2
アルミテープの長いテザー (各 1 km) x 10

合計 12 個のテザーで 13 個のサテライトを接続します。

PAOS に変換したのは、アプリケーションのテザー部分の有限要素部分のみです。この部分は重要ですが、コードの一部にすぎません (~25%)。残りのテザー部分とボディー部分 (ペイロード・マネージャー、コントローラー/エミッター、太陽電池、地球、月、惑星、その他) はまだ PAOS に変換されていないため、PAOS の利点は完全には引き出されていません。

このプログラムの Visual Studio* ソリューションには、12 の FORTRAN プロジェクトと 1 つの C++ プロジェクトが含まれています。C++ プロジェクトは、プロセッサーがサポートする SIMD 関数を調べるために使用されます。合計 554 個の F90 ソースファイルが使用され、それらの多くには複数のサブルーチンと関数が含まれています。PAOS をサポートするため、オリジナルのソリューションに約 100 個のファイルが追加されました。これらのファイルのコンテンツは、表面上は、オリジナルのソースにサフィックス “.v” と、異なる SIMD パック形式用の異なるデータ型を追加したものと同じです。

テストは、インテル® Core™ i7 2600K プロセッサー (4 コア、インテル® ハイパースレッディング (HT) テクノロジー対応、開発コード名 Sandy Bridge)、Windows* 7 Professional (64 ビット)、16GB RAM のシステムで行われました。

システムは、HT をオン/オフ、ターボブーストをオン/オフにしました。今回は 4 コア・プロセッサーのシステムを使用したため、テストは主に HT オフで実行され、アプリケーションで倍精度浮動小数点を多用しました。

コードの多くの部分はボディーの処理を行っていたため (非 PAOS コードの約 80%)、テザーのシミュレーションでは要素 (テザー) 数を増やしました。テザーあたりのビード数は 1,000 から 10,000 に増やしました。テザーあたりのビード数を 10 倍にすると、PAOS コードに対する非 PAOS コードの相対的な負荷は 1/10 になります。さらにビード数を増やすこともできましたが、テスト結果で PAOS の使用にバイアスをかけすぎないように、この値に決めました。

負荷バランスをとるため、両端の 2 つの短いテザー (各 15 m) を 1 km のアルミのテザー (10,000 ビード) に置き換え、12 個のテザーすべてが同じデザインと統合オプションになるようにしました。

このアプリケーションでは、同じビード数と同じ統合オプション (空気力学、電気力学、その他) のテザーが SIMD PAOS レイアウトへの統合に適しています。SIMD に対するテザーの互換性とテザー数に応じて、2 要素幅、3 要素幅、4 要素幅のパックがランタイムに選択されます。12 個のテザーでは、1 (なし)、2 要素幅、3 要素幅、4 要素幅のパックを組み合わせることができます。

さまざまなスレッド数 (1:4 および 1:8) とさまざまなパック密度の PAOS (1 要素幅 (パックなし)、2 要素幅、3 要素幅、4 要素幅 double) を実行できるように、テザー数を 12 から 96 に増やした結果、パック (1、2、3、4) とスレッド数 (1、2、4、8) の任意の組み合わせが可能になりました。

追加のテザーは、ボディー (衛星) のシミュレーションに使用されるコードの非 PAOS 変更部分のロードを増やさないように、既存の衛星に接続されました。テザー数が大幅に増えるため、3、5、6、7 スレッドの理想的なテザー数は測定していません。

テストプログラムの構成は次のようになりました。

13	個のサテライトを
96	個のテザー (1 km) で接続
各テザーの構成: ~85 スカラー 57 個の次元 (3) の配列 3 個の次元 (3,3) の配列 22 個の次元 (3,~10000) の配列 52 個の次元 (~10000) の配列

96 * (85 + (57*3) + (3*3*3) + (22*3*1000) + (52*10000)) = 56,283,168 double (450MB)

テザー、衛星、惑星、その他をそれぞれ操作する変数も必要です。

ベースラインの参照シミュレーション時間に実行する適切な積分区間を決定するため、PAOS SIMD なしのシングルスレッド構成でテスト実行が行われました。シミュレーション時間区間ごとに、前のシミュレーション時間からのウォールクロック経過時間が表示されます。

テストはパック密度 (パックなし、2 要素幅、3 要素幅、4 要素幅) およびスレッド数 (1、2、3、4) の組み合わせに対して行われました。各構成は 4 つのレポート区間で実行され、最も小さな値を選択して比較しました。最も小さな値の選択は、システムでほかに実行されているプロセスの影響が最小限であるという仮定の下で行われました。

96 テザー、各 10,000 ビード - ターボオン、HT オフ

   パックなし SIMD      2 要素幅 SIMD       3 要素幅 SIMD       4 要素幅 SIMD
1  202.461830544090  98.8166282926832  110.127472870544  96.2114920825516
2  113.080892757974  64.3881699061922  69.6634485553468  62.4252337711068
3  88.0928441275801  56.0872621388371  60.3187281050650  57.5615369606003
4  78.3640600375229  56.5572142589117  58.9836796998115  58.8164001500936

96 テザー、各 10,000 ビード - ターボオフ、HT オフ

   パックなし SIMD      2 要素幅 SIMD       3 要素幅 SIMD       4 要素幅 SIMD
1  222.377931407129  108.414170956848  120.604551444653  105.015817485928
2  119.739932457786  67.4135627767355  73.9538575609758  65.4644253658544
3  90.9758964352723  56.9652196622883  61.5711771857414  58.8302754221386
4  80.8031147467163  57.1387076923083  59.7432093058160  59.3558339962474

最初のテスト (1 スレッド、ターボオン、HT オフ) では、オリジナルのコードに対するスピードアップは 2.05 倍でした。3 要素幅のスピードアップは 1.88 倍で、4 要素幅のスピードアップは 2.10 倍でした。2 要素幅と 4 要素幅の差は 1.027 倍にすぎません。

2 スレッド以上では適切にスケーリングされていないように見えますが、このシミュレーションはメモリーを多用している (作業メモリー ~450MB) ことに注意してください。

3 要素幅のパフォーマンスを見てみましょう。テスト実行はビード数 4000 で行われました。

4000 ビード - ターボオン、HT オン

   パックなし SIMD      2 要素幅 SIMD       3 要素幅 SIMD       4 要素幅 SIMD
1  91.3673146779801  50.2108811987264  53.7199640737510  47.4793344518262
2  55.4531537381101  34.9485817566820  35.3566061461863  31.8637723342063
3  45.6128820578588  31.2057180011579  30.7373190216704  28.4547075003547
4  45.9029787147183  30.9318707075290  30.1450711425587  29.1348981424198
5  45.9152253990896  30.9467731366030  30.1314135844295  29.2071261732417
6  52.4540275792824  36.6395676842658  34.7508676123107  33.7327393855012
7  55.0174089813809  39.3389245960061  36.7703592835023  36.2333221964363
8  60.0940263305092  42.1638867950896  39.0601884747230  39.5467420973800

シングルスレッドのより少ないビード数では、パックなしと 2 要素幅のスピードアップが 2.05 倍から 1.82 倍に低下していることに注意してください。これは、非 PAOS コードと PAOS コードの比率が変化したためです。3 スレッドから 8 スレッドの実行では、3 要素幅よりも 2 要素幅のパフォーマンスが高く、8 スレッドでは、3 要素幅よりも 4 要素幅のパフォーマンスが高いことにも注意してください。今回の条件で実行したときに 4 要素幅が最適なのは明白ですが、プロセッサーの設計の違い、ビード数、テザー数によって結果は大きく変わります。最終レベルのキャッシュのサイズとデータセットのサイズの組み合わせによっては、3 要素幅が最適になる可能性もあります。

2 スレッド以上のスケーリングでスピードアップが低下するのは 3 つの要因によるものです。

a) データセットのフルサイズ (450MB) が L3 キャッシュよりも大きいため、アプリケーションがメモリー帯域幅の制限を受けます。これらのデータがすべてシミュレーション実行中に処理されるとは限らないことに注意する必要があります (中でも空気力学と電気力学のフォースは無効になります)。

b) インテル® Core™ i7 (Sandy Bridge) の AVX では、256 ビットのデータパスが利用できるとは限りません。一部の内部転送には 2 クロック必要です。より新しい Ivy Bridge プロセッサーでは、この問題は解消されます。

c) Sandy Bridge のデュアル・バンク・メモリーは、Ivy Bridge のクワッド・バンク・メモリーよりもメモリーバスの効率が低くなります。また、メモリーのノード数は 1 つです。2 プロセッサーの Ivy Bridge による NUMA 構成 (NUMA ノード 2 つ) では、クワッド・バンク・メモリーでそれぞれ、4 要素幅 SIMD 命令のパフォーマンスを効率的に実行できます。

まとめ

PAOS は、一部のアプリケーションでは非常に有効です。1P Sandy Bridge のシステムを 1P または 2P Ivy Bridge のシステムに変更すると、よりパフォーマンスが向上すると予想されます。さらに、メモリー帯域幅の制限がないアプリケーションで 4 要素幅の PAOS SIMD (AVX) を使用すると、よりパフォーマンスが向上することが予想されます。

PAOS は、サイズ要件が増加しますが、キャッシュラインの効率は良くなります (ヒット率が高くなります)。2 要素幅 SIMD はパックなしバージョンよりも作業ストレージのキャッシュ要件が 2 倍に、4 要素幅 SIMD は作業ストレージのキャッシュ要件が 4 倍になります。PAOS を使用する場合、ループ実行は減少するか分割されます。結果はワーキングセットのサイズやプロセッサーの世代に応じて異なります。

Copyright © 2012 Jim Dempsey. 無断での引用、転載を禁じます。

著者連絡先:
jim.00.dempsey@gmail.com http://www.linkedin.com/in/jim00dempsey

* その他の社名、製品名などは、一般に各社の表示、商標または登録商標です。

コンパイラーの最適化に関する詳細は、最適化に関する注意事項を参照してください。

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