今後のGCC 5.0でのx86の新しい最適化

したがって、GCC 5.0の新しい最適化の実際の開発は完了したと見なすことができます。 GCC 5.0製品は現在フェーズ3フェーズにあります。つまり、最適化はすでに完了しています。 これ以降の記事では、GCC 5.0 for x86で実装された最適化と、 Intel AtomおよびIntel Coreプロセッサのプログラムパフォーマンスへの影響について説明します 。 今日は、グループメモリアクセスのベクトル化に焦点を当てます。 以降の記事では、32ビットPICモードでの高速化とベクトル化の追加増幅について説明します。



おそらく既にご想像のとおり、GCC 5.0ではグループメモリアクセスのベクトル化が大幅に改善されました。 グループメモリアクセスは、反復可能な呼び出しシーケンスを指します。 例:



x = a[i], y = a[i + 1], z = a[i + 2]
      
      





iで反復できるのは、長さ3のメモリダウンロードのグループです。メモリアクセスのグループの長さは、グループ内の最低アドレスと最高アドレスの間の距離です。 上記の例では- (i + 2)-(i)+ 1 = 3

グループ内のメモリアクセス数は、その長さを超えません。 例:



 x = a[i], z = a[i + 2]
      
      





i上で反復可能なのは、メモリアクセスが2つしかないという事実にもかかわらず、長さ3のグループです。



GCC 4.9は、長さが2(2、4、8 ...)のグループをベクトル化します。



GCC 5.0は、長さが3または次数2(2、4、8 ...)のグループをベクトル化します。 他の長さは、実際のアプリケーションでは非常にまれなので、ベクトル化されません。



ほとんどの場合、構造体の配列を操作するときは、メモリアクセスのグループのベクトル化が使用されます。



1.たとえば、RGB構造での画像変換。

2. N次元の座標を操作します(3次元の点を正規化するなど)。

3.定数行列によるベクトルの乗算:



 a[i][0] = 7 * b[i][0] - 3 * b[i][1]; a[i][1] = 2 * b[i][0] + b[i][1];
      
      





GCC 5.0全体(4.9と比較)





次の表は、GCC 4.9と比較してバイト構造(ベクトル内の要素の最大数)でGCC 5.0を使用した場合のパフォーマンスの向上の推定値を示しています。 評価には次のサイクルが使用されます。



  int i, j, k; byte *in = a, *out = b; for (i = 0; i < 1024; i++) { for (k = 0; k < STGSIZE; k++) { byte s = 0; for (j = 0; j < LDGSIZE; j++) s += in[j] * c[j][k]; out[k] = s; } in += LDGSIZE; out += STGSIZE; }
      
      





どこで:



 const byte c[8][8] = {1, -1, 1, -1, 1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, 1, -1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, 1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, -1, -1};
      
      





このようなマトリックスは、ループ内の計算を最小限に抑えて、比較的速い加算と減算を行うために使用されます。





コンパイルオプション「-Ofast」とSilvermontの 「-march = slm」、 Haswellの 「-march = core-avx2」およびすべての組み合わせ-DLDGSIZE = {1,2,3,4,8} -DSTGSIZE = {1,2 、3,4、8}



4.9と比較したGCC 5.0のパフォーマンスの向上(何倍も高速であるほど、優れている)。



Silvermont :Intel Atom(TM)CPU C2750 @ 2.41GHz



最大6.5倍の成長!



画像



表からわかるように、長さ3のメモリグループの結果はあまり良くありません。 これは、そのような変換には8つの「pshufb」命令が必要であり、Silvermontでの実行時間は約5クロックサイクルであるためです。 それにもかかわらず、ループ内の他のチームのベクトル化により、大幅に増加する可能性があります。 これは、長さ2、3、4、および8のメモリダウンロードのグループの例で見ることができます。



長さ2のメモリロードグループのGCCアセンブラの例:

GCC 4.9 GCC 5.0
movdqa .LC0(%rip)、%xmm7

movdqa .LC1(%rip)、%xmm6

movdqa .LC2(%rip)、%xmm5

movdqa .LC3(%rip)、%xmm4

movdqu a(%rax、%rax)、%xmm1

movdqu a + 16(%rax、%rax)、%xmm0

movdqa%xmm1、%xmm3

pshufb%xmm7、%xmm3

movdqa%xmm0、%xmm2

pshufb%xmm5、%xmm1

pshufb%xmm6、%xmm2

pshufb%xmm4、%xmm0

por%xmm2、%xmm3

por%xmm0、%xmm2

movdqa .LC0(%rip)、%xmm3

movdqu a(%rax、%rax)、%xmm0

movdqu a + 16(%rax、%rax)、%xmm1

movdqa%xmm3、%xmm4

pand%xmm0、%xmm3

psrlw $ 8、%xmm0

pand%xmm1、%xmm4

psrlw $ 8、%xmm1

packuswb%xmm4、%xmm3

packuswb%xmm1、%xmm0

ここで、以下のHaswellの例のように、「。LC」定数のほとんどはループ内で不変になりますが、それは空きレジスタがある場合のみです。 それ以外の場合は、phufbに直接インストールする必要があります:“ pshufb .LC0、%xmm3”。 このようなpshufbは、アドレスのサイズだけ大きくなり、実行に時間がかかる可能性があります。



Haswell :Intel Core(TM)i7-4770K CPU @ 3.50GHz



最大3倍の成長!



画像



この場合、長さ3のメモリグループの結果は、 Haswellでは命令「pshufb」が1クロックサイクルで実行されるため、はるかに優れています。 さらに、ここでの最大の増加は、長さ3のメモリアクセスグループの実装されたベクトル化のためです。



長さ2のメモリロードグループのGCCアセンブラの例:

GCC 4.9 GCC 5.0
vmovdqa .LC0(%rip)、%ymm7

vmovdqa .LC1(%rip)、%ymm6

vmovdqa .LC2(%rip)、%ymm5

vmovdqa .LC3(%rip)、%ymm4

vmovdqu a(%rax、%rax)、%ymm0

vmovdqu a + 32(%rax、%rax)、%ymm2

vpshufb%ymm7、%ymm0、%ymm1

vpshufb%ymm5、%ymm0、%ymm0

vpshufb%ymm6、%ymm2、%ymm3

vpshufb%ymm4、%ymm2、%ymm2

vpor%ymm3、%ymm1、%ymm1

vpor%ymm2、%ymm0、%ymm0

vpermq $ 216、%ymm1、%ymm1

vpermq $ 216、%ymm0、%ymm0

vmovdqa .LC0(%rip)、%ymm3

vmovdqu a(%rax、%rax)、%ymm0

vmovdqu a + 32(%rax、%rax)、%ymm2

vpand%ymm0、%ymm3、%ymm1

vpsrlw $ 8、%ymm0、%ymm0

vpand%ymm2、%ymm3、%ymm4

vpsrlw $ 8、%ymm2、%ymm2

vpackuswb%ymm4、%ymm1、%ymm1

vpermq $ 216、%ymm1、%ymm1

vpackuswb%ymm2、%ymm0、%ymm0

vpermq $ 216、%ymm0、%ymm0

上記から、結論は次のとおりです。GCC5.0を使用すると、上記のようなアプリケーションのパフォーマンスを大幅に向上させることができます。 今すぐ試すことができます! ほとんどの編集はAndroid NDKに移植される予定です。



測定に使用されるコンパイラ:



英語の記事の元のテキストから測定値が取得された例をダウンロードできます。



All Articles