ベクトル化とインテル® Xeon® スケーラブル・プロセッサーを使用したパフォーマンスの向上
Alberto V. (Intel)

この記事は、2018 年 6 月 12 日時点の、インテル® デベロッパー・ゾーンに公開されている「Improve Performance Using Vectorization and Intel® Xeon® Scalable Processors」の日本語訳です。

はじめに最近の CPU には異なるレベルの並列処理が含まれています。最新のハードウェアの能力を最大限に引き出すには、ソフトウェアで並列処理のすべての機会を活用する必要があります。これらの機会には、ベクトル化、マルチスレッド化、メモリーの最適化、その他が含まれます。
ソフトウェアのパフォーマンスを高めるニーズは増加の一途を辿っていますが、以前のようにハードウェアのクロック速度を上げることでパフォーマンスを向上させることはできません。その代わりに、ソフトウェア・アプリケーションが複数のコアで並列処理を使用し、各コアで SIMD(Single Instruction、Multiple Data) アーキテクチャーやベクトル・プロセッサーにより増加した実行ユニットを活用する必要があります。
複数のコアと広い SIMD ユニットの両方を活用するには、ソフトウェアにベクトル化とマルチスレッド化を追加する必要があります。ベクトル化やマルチスレッド化の効果は倍増するため、各コアのベクトル化は重要なステップです。複数のコアから優れたパフォーマンスを得るには、各コアから優れたパフォーマンスを引き出す必要があります。
インテルの新しいプロセッサーは、増加するパフォーマンス要件に対応するため、要件が厳しい計算ワークロードのパフォーマンスを向上する新しい命令セット、インテル® アドバンスト・ベク
トル・エクステンション 512 (インテル® AVX-512) をサポートしています。
インテル® AVX-512 は、最新のハードウェアおよび将来の世代の並列ハードウェアで、コードのベクトル化の効率を向上します。

ベクトル化の基本
図 1 はインテル® AVX-512 を使用したベクトル化の基本を示しています。
図 1 の上部 (灰色のボックス) のコードは、単精度数を含む 2 つの配列要素の加算を行う非常に単純なループです。スカラーモード (ベクトル化なし) では、すべての命令で 1 つの結果が生成されます。ループがベクトル化されると、各オペランドの 16 の要素が 512 ビットのベクトルレジスターにパックされ、単一のインテル® AVX-512 命令で 16 の結果が生成されます。
倍精度浮動小数点数を使用した場合は 8 つの結果になります。

ループがベクトル化されたかどうかを確認するには? 1 つの方法は、図の下部 (黄緑色のボックス) で示されているように、インテル® C コンパイラーまたはインテル® C++ コンパイラーから最適化レポートを取得することです。この記事の次のセクションでは、これらのレポートの生成方法を説明します。
ただし、すべてのループがベクトルモードで実行できるとは限りません。この単純なループの異なる反復は互いに独立して処理できるため、このループはベクトル化できます。例えば、図 1 では、インデックス変数 i が 3 の場合、反復は同時に実行できます。i が 0 から N の場合も同様です。つまり、このループの反復間にデータの依存関係はありません。

図 1: 2 つの単精度浮動小数点数の配列を追加した単純なループ。演算は 512 ビットのレジスターで同時に行われます。依存関係は示されていません。ループはベクトル化されます。

図 2 はループが直接ベクトル化されないケースを示しています。ループは、各反復で配列 cの 1 つの値を生成します。値は、前の反復で生成された c の別の値を使用して計算されます。
このループがベクトルモードで実行された場合、配列 c の古い値 (この場合は 16 の値) は(図 2 の赤い丸で示された) ベクトルレジスターにロードされるため、結果はスカラーモードで実行した場合と異なります。
つまり、このループはベクトル化できません。この種のデータの依存関係は、「リードアフターライト」あるいはフロー依存と呼ばれ、コンパイラーが依存関係を検出してこのループをベクトル化することはありません。
ほかの種類のデータの依存関係もあります。これはコンパイラーがこのループを自動的にベクトル化することを妨げている例です。これらの場合、ベクトル化可能なループになるように、ループまたはデータ形式を変更する必要があります。

図 2: データの依存関係が存在するためベクトル化されない単純なループ

インテル® アドバンスト・ベクトル・エクステンション
512 (インテル® AVX-512): ベクトル処理能力を向上
インテル® AVX-512 命令セットは、新しい命令を 512 ビット・レジスターで処理することにより、ベクトル処理能力を向上します。インテル® AVX-512 は、32 のベクトルレジスターをサポートし、各レジスターで 16 の単精度浮動小数点数または 8 つの倍精度浮動小数点数をパックできます。
インテル® AVX-512 は、インテル® アドバンスト・ベクトル・エクステンション 2 (インテル®
AVX2) 命令セットと比較して、2 倍のベクトルレジスターをサポートし、各ベクトルレジスターで 2 倍の単精度浮動小数点数または倍精度浮動小数点数をパックできます。つまり、インテル®
AVX2 (256 ビットをサポート) よりも、レジスターが同時に処理するデータを多く保持でき、CPU サイクルごとに多くのワークを実行できます。
現時点で、インテル® AVX-512 は、インテル® Xeon Phi™ プロセッサーおよびインテル®
Xeon® スケーラブル・プロセッサーで利用可能です。
インテル® AVX-512 命令セットの仕様は、いくつかのサブセットから構成されます。
A. 次の命令はインテル® Xeon Phi™ プロセッサーとインテル® Xeon® スケーラブル・プロセッサーの両方でサポートされます。
基本命令



競合検出命令B. 次の命令はインテル® Xeon Phi™ プロセッサーでサポートされます。



指数および逆数命令



プリフェッチ命令

C. 次の命令はインテル® Xeon® スケーラブル・プロセッサーでサポートされます。


バイト (char または int8) およびワード (short または int16) 命令
ダブルワード (int32 または int) およびクワッドワード (int64 または long) 命
令ベクトル長の拡張

上記のサブセットは、異なる方法でアクセスできます。最も簡単な方法は、コンパイラー・オプションを使用することです。例として、インテル® C++ コンパイラーの各サブセットを指定するオプションを示します。
••

•

•

-xCOMMON-AVX512 オプションを指定したときに使用する命令
o

基本命令

o

競合検出命令o-xCORE-AVX2 で有効になる命令-xMIC-AVX512 オプションを指定したときに使用する命令
o

基本命令

o

競合検出命令o指数および逆数命令o

プリフェッチ命令

o-xCORE-AVX2 で有効になる命令-xCORE-AVX512 オプションを指定したときに使用する命令
o

基本命令

o

競合検出命令o

バイトおよびワード命令o

ダブルワードおよびクワッドワード命令o

ベクトル長の拡張

o-xCORE-AVX2 で有効になる命令-xCORE-AVX2 オプションを指定したときに使用する命令oインテル® アドバンスト・ベクトル・エクステンション 2 (インテル® AVX2)、インテル®
アドバンスト・ベクトル・エクステンション (インテル® AVX)、インテル® ストリーミン
グ SIMD 拡張命令 (インテル® SSE)、インテル® SSE4.2、インテル® SSE4.1、
インテル® SSE3、インテル® SSE2、インテル® ストリーミング SIMD 拡張命令 3
補足命令 (インテル® SSSE3)

例えば、インテル® Xeon Phi™ プロセッサーとインテル® Xeon® スケーラブル・プロセッサーの両方でバイナリー互換にする必要がある場合は、次のオプションを指定してコードをコンパイルします。
icpc Example.cpp -xCOMMON-AVX512 <ほかのオプション>

実行ファイルをインテル® Xeon® スケーラブル・プロセッサーのみで実行する場合は、次のオプションを指定してコードをコンパイルします。
icpc Example.cpp -xCORE-AVX512 <ほかのオプション>

ベクトル化を先に行う
ベクトル化とマルチスレッド化を組み合わせて使用すると、いずれか一方のみを使用した場合よりもはるかに高速になります。このパフォーマンスの差は、ハードウェアの世代が新しくなるたびに大きくなります。
インテル® Xeon Phi™ プロセッサーや新しいインテル® Xeon® スケーラブル・プロセッサーのような最新の CPU で利用可能な高度な並列処理を使用するには、個々のコアでベクトル処理を、複数のコアでマルチスレッド化をそれぞれ活用するように、新しいアプリケーションを記述する必要があります。コード作成と最適化の労力を無駄にせず、ソフトウェア開発の投資を最大限に活用するため、将来の世代のプロセッサーでもできるだけ最適化が行われることを保証する方法で新しいアプリケーションを記述します。
コードを最適化する場合、最初の労力はベクトル化に集中するようにします。アルゴリズム/コードのデータ並列処理は、最適化プロセスのこの段階で行われます。
インテル® Xeon Phi™ プロセッサーや新しいインテル® Xeon® スケーラブル・プロセッサーの1 つのコアでベクトル化機能を利用する方法はいくつかあります。
1. 最も簡単な方法は、インテル® プロセッサー向けに最適化されているライブラリーを使用することです。例えば、ベクトル化とマルチスレッド化を活用するように最適化された、インテル®
マス・カーネル・ライブラリー (インテル® MKL) を使用します。この場合、ライブラリーをリンクするだけで、パフォーマンスを大幅に向上できます。別の例は、Python* を使用している場合です。
インテル® Distribution for Python* を使用すると、NumPy* などの Python* の計算パッケージが高速化され、パフォーマンスが自動的に向上します。
2. 最適化されたライブラリーを使用することに加えて、コンパイラーがコードを自動的にベクトル化するようにコードを記述するか、既存のコードを変更します。これは自動ベクトル化と呼ばれます。場合によっては、自動ベクトル化に必要なキーワードやディレクティブをコードに追加します。変更がわずかな場合や変更が全くない場合、この方法で最適化されたコードは将来の世代のプロセッサーでも最適化されるため、非常に優れた方法です。必要な作業は再コンパイルのみです。

3. 3 つ目の方法は、組込み関数やアセンブリー言語を使用して、ベクトル命令を直接呼び出すことです。
この記事では、上記に示した最初の 2 つの方法を使用してアプリケーションを最適化する例を説明します。
通常のベクトル化ワークフローは次のようになります。
1. コンパイル + プロファイル
2. 最適化
3. 繰り返し
上記の最初のステップは、最新のコンパイラー、プロファイラー、最適化ツールを使用して実行します。この記事では、最適化フローの例として、インテル® コンパイラーで生成された最適化レポートと、インテル® Advisor およびインテル® VTune™ Amplifier のような高度な最適化ツール (新しいインテル® Parallel Studio XE 2018 の一部) を使用します。

例: アメリカンオプションの価格計算
この例は、James Reinders、Jim Jeffers 共著『High Performance Parallelism PearlsVolume Two』1 に掲載されています。第 8 章で、Shuo Li (この章およびこの記事で使用しているコードの著者) は、アメリカンオプションの価格計算問題のソリューションを説明しています。
このソリューションは、ニュートン・ラフソン法 (Barone-Adesi および Whaley の研究 2 に基づく) を使用し、ブラック・ショールズ・モデルに基づいて偏微分方程式の近似解を見つけます。このソリューションは C++ で実装されています。ソースコードは著者の Web サイト(http://lotsofcores.com/ (英語)) から自由にダウンロードできます。
図 3 は、(上記のリンクからダウンロードした) ソースコード am_opt.cpp の 2 つのメインループを示しています。最初のループはランダムな値で配列を初期化します。2 つ目のループは、OptPerThread 変数で示されたオプション (この場合は約 1 億 2500 万オプション) の価格を計算します。この記事の残りでは、ほとんどの CPU 時間を使用している Loop 2 に注目します。特に、1 つのオプションの価格を計算する baw_scalaropt 関数の呼び出し (206 行目) に注目します。

図 3: 著者の Web サイトからダウンロードしたソースコードの 2 つのメインループ

次のコードは、baw_scalaropt 関数の定義を示しています。

float baw_scalaropt( const float S,
const float X,
const float r,
const float b,
const float sigma,
const float time){
float sigma_sqr = sigma*sigma;
float time_sqrt = sqrtf(time);
float nn_1 = 2.0f*b/sigma_sqr-1;
float m = 2.0f*r/sigma_sqr;
float K = 1.0f-expf(-r*time);
float rq2 = 1/((-(nn_1)+sqrtf((nn_1)*(nn_1) +(4.f*m/K)))*0.5f);
float rq2_inf = 1/(0.5f * ( -(nn_1) + sqrtf(nn_1*nn_1+4.0f*m)));float S_star_inf = X / (1.0f - rq2_inf);
float h2 = -(b*time+2.0f*sigma*time_sqrt)*(X/(S_star_inf-X));
float S_seed = X + (S_star_inf-X)*(1.0f-expf(h2));
float cndd1 = 0;
float Si=S_seed;float g=1.f;
float gprime=1.0f;
float expbr=expf((b-r)*time);for ( int no_iterations =0; no_iterations<100; no_iterations++) {float c = european_call_opt(Si,X,r,b,sigma,time);
float d1 = (logf(Si/X)+(b+0.5f*sigma_sqr)*time)/(sigma*time_sqrt);
float cndd1=cnd_opt(d1);g=(1.0f-rq2)*Si-X-c+rq2*Si*expbr*cndd1;
gprime=( 1.0f-rq2)*(1.0f-expbr*cndd1)+rq2*expbr*n_opt(d1)*(1.0f/(sigma*time_sqrt));
Si=Si-(g/gprime);};
float S_star = 0;
if (fabs(g)>ACCURACY) { S_star = S_seed; }else { S_star = Si; };float C=0;
float c = european_call_opt(S,X,r,b,sigma,time);
if (S>=S_star) {C=S-X;}
else {
float d1 = (logf(S_star/X)+(b+0.5f*sigma_sqr)*time)/(sigma*time_sqrt);
float A2 = (1.0f-expbr*cnd_opt(d1))* (S_star*rq2);
C=c+A2*powf((S/S_star),1/rq2);};
return (C>c)?C:c;};上記のコードから次のことが分かります。


113 行目のループでニュートン・ラフソン最適化を行っています。
114 行目 (ループの内側) および 125 行目 (ループの外側) で european_call_opt
関数を呼び出しています。この関数は、アメリカンオプションの価格計算に必要な、ヨーロピアン・オプションの価格計算を行います (詳細は、1 のアルゴリズムを参照してください)。

参考のために、次のコードは european_call_opt 関数の定義を示しています。この関数には計算 (および数学関数の呼び出し) のみ含まれ、ループは含まれていないことに注意してください。
float european_call_opt( const float S,
const float X,
const float r,
const float q,
const float sigma,
const float time){
float sigma_sqr = sigma*sigma;
float time_sqrt = sqrtf(time);
float d1 = (logf(S/X) + (r-q + 0.5f*sigma_sqr)*time)/(sigma*time_sqrt);
float d2 = d1-(sigma*time_sqrt);
float call_price=S*expf(-q*time)*cnd_opt(d1)-X*expf(-r*time)*
cnd_opt(d2);return call_price;};

コードが分かりやすくなるように、図 4 に am_opt.cpp の関数とループの構造を示します。注
目している 2 つのループを Loop 2.0 および Loop 2.1 として示しています。

図 4: プログラムの関数とループの構造最初のアプローチとして、図 5 の黄色のセクションの一番上 (ラベル 1 の行) に示されているオプションを指定してコードをコンパイルします。O2 オプション (中レベルの最適化) および-qopt-report=5 オプション (最高レベルの情報を含む最適化レポートを生成、レベルは 1から 5) を指定していることに注意してください。
図 5 の黄色のセクションは最適化レポートの一部です。次の点に注意してください。


201 行目の Loop 2.0 はベクトル化されません。コンパイラーは、ループ交換と SIMDディレクティブの使用を推奨しています。
コンパイラーは、依存関係が仮定されたために Loop 2.1 がベクトル化されなかったこと、baw_scalaropt 関数がインライン展開されたこともレポートしています。
baw_scalaropt 関数のインライン展開は両方のループ (2.0 および 2.1) に入れ子のループとして示され、コンパイラーは、「不完全な入れ子のループ」 (2 つのループ間に余分な命令が含まれているループ) としてレポートし、変更を推奨しています。

図 5: "O2" 最適化オプションを指定してインテル® AVX-512 命令向けにコードをコンパイル

SIMD オプション (ベクトル化を強制) を試す前に、高度な "O3" 最適化オプションを使用してコードをコンパイルします。図 6 に結果を示します。黄色のセクションから次のことが分かります。


コンパイラーは、外側のループ (Loop 2.0) がベクトル長 16 でベクトル化されたことをレポートしています。
ベクトル化された Loop 2.0 のスピードアップの期待値は 7.46 倍です。
ベクトル化された算術ライブラリーの呼び出しの数は 14 で、シリアル化された関数呼び出しの数は 1 です。

このループに使用したベクトル長が 16 であれば、このベクトル化されたループのスピードアップの期待値は約 16 倍になるはずです。なぜ 7.46 倍にしかならないのでしょうか。

考えられる理由は、コンパイラーによりレポートされたシリアル化された関数呼び出し (内部ループ (Loop 2.1) の内側の european_call_opt 関数の呼び出し) が問題になっていることです。この問題を解決する 1 つの方法は、すべての関数呼び出しを再帰的にインライン展開するようコンパイラーに指示することです。具体的には、baw_scalaropt 関数の呼び出しの直前に #pragma inline recursive ディレクティブを追加します。
コードを (前と同じコンパイラー・オプションを使用して) コンパイルしたところ、新しい最適化レポートではベクトル化された Loop 2.0 のスピードアップの期待値 (図 7) は 14.180 になり、理想値 (16) に近付きました。

図 6: "O3" 最適化オプションを指定してインテル® AVX-512 命令向けにコードをコンパイル

図 7: "#pragma inline recursive" ディレクティブを追加

図 8: 両方のループがベクトル化されたことを示している最適化レポートの一部

図 8 は、両方のループがベクトル化されたことを示している最適化レポートのセクションです。
ラベル 2 の行を見ると、内部ループ (Loop 2.1) が外部ループとともにベクトル化されていることが分かります。つまり、コンパイラーは入れ子のループに含まれるすべての操作を考慮に入れて、効率的なコードを生成したことになります。
コンパイラーが妥当なレベルのベクトル化をレポートしたら、ランタイム解析を実行します。この解析には、インテル® Advisor 2018 を使用します。
インテル® Advisor 2018 は、インテル® Parallel Studio XE に含まれる解析ツールの 1 つで、アプリケーションを解析し、コードのベクトル化を向上するためのアドバイスを表示します。
インテル® Advisor は、アプリケーションを解析し、ベクトル化の範囲を知らせるだけでなく、さらなるベクトル化と現在のベクトル化の効率向上の可能性も示します。
インテル® Advisor は、任意のコンパイラーでコンパイルされたアプリケーションを解析できますが、インテル® コンパイラーを使用すると、コンパイラーにより生成されたレポートの情報を使用できるため、さらに効果的です。
インテル® Advisor を最も効率良く利用するには、グラフィカル・ユーザー・インターフェイス(GUI) を使用します。GUI から、インテル® Advisor がコードを解析して収集したすべての情報と推奨事項にアクセスできます。詳細は、インテル® Advisor ページを参照してください。ドキュ
メント、トレーニング資料、サンプルコード、製品サポート、インテル® Advisor コミュニティー・フォーラムに関する情報を入手できます。
インテル® Advisor には、コマンドライン・インターフェイス (CLI) もあります。リモートホストで作業したり、スクリプトを使用するなどして解析タスクを自動化しやすい方法で情報を生成できます。
例として、インテル® Advisor の CLI を実行してサンプルコード am_opt.cpp の基本的な解析を行います。

図 9: インテル® Advisor でサーベイ情報を収集

最初に、インテル® プロセッサー (この場合はインテル® Xeon® スケーラブル・プロセッサー)上で実行する最適化された実行ファイルを生成します。図 9 に、インテル® Advisor の CLIバージョンを実行します。サーベイ解析は、コードのベクトル化の状況と hotspot を特定するのに必要な情報が得られるため、開始点として役立ちます。
図 9 のコマンド 1 は、インテル® Advisor を実行し、プロジェクト・ディレクトリー
VecAdv-01 を作成します。インテル® Advisor は、このディレクトリー内に解析結果を含むe000 ディレクトリーを作成します。コマンドは次のとおりです。
$ advixe-cl --collect survey --project-dir ./VecAdv-01 --search-dir
src:r=./.-- ./am_call

次のステップでは、インテル® Advisor で実行したサーベイ解析の結果を確認します。CLI を使用してレポートを生成します。-collect オプションを -report オプションに変更して (図9 のコマンド 2 を参照)、データを収集したプロジェクト・ディレクトリーを参照していることを確認します。次のコマンドは、プロジェクト・ディレクトリー以下の結果ディレクトリーに含まれるデータからサーベイレポートを生成します。
$ advixe-cl -report survey -format=xml --project-dir ./VecAdv-01 --search-dir
src:r=./.-- ./am_call

上記のコマンドは、プロジェクト・ディレクトリーに advisor-survey.xml という名前のレポートを作成します。–format=xml オプションを指定しない場合、テキスト形式のレポートが生成されます。このレポートは、列形式になっており、複数の列が含まれているため、コンソール上では読みづらいかもしれません。filter オプションを指定すると、表示される列数を制限できます。

xml 形式でレポートを作成することもできます。その場合、図 9 のように、-format オプションの値を text から xml に変更します。
xml 形式では、レポートファイル内の情報が 1 列にまとめられるため、小さな画面では、xml形式のレポートのほうが読みやすいでしょう。図 9 (のエリア 4) は、Loop 2.0 に対応するレポートの部分を示しています。
インテル® Advisor のサーベイ解析は、アプリケーションに含まれるループのパフォーマンスの概要を生成します。例えば、図 9 の最初のコードブロックは、ソースコードの 200 行目のルー
プがインテル® AVX-512 を使用してベクトル化されたこと、および (スカラーバージョンと比較した) ループのパフォーマンス向上の推定値とタイミング情報を示しています。
また、個々のループにループ ID が割り当てられていることが分かります。これは、インテル®
Advisor が将来の解析でループを追跡できるようにするためのラベル付けです (例えば、上記のパフォーマンスの概要を確認後、コマンドラインでループ ID を指定して特定のループの詳細情報を生成することができます)。
サーベイ解析でパフォーマンスの概要を確認したら、その他のオプションを使用して特定の情報をレポートに追加できます。例えば、トリップカウント解析を実行すると、ループの反復回数が得られます。
この情報をプロジェクトに追加するには、サーベイ解析を行ったプロジェクトでトリップカウント解析を実行します。図 10 はこの様子を示しています。使用したコマンドは次のとおりです。
$ advixe-cl --collect tripcounts --project-dir ./VecAdv-01 --search-dir
src:r=./.-- ./am_call$ advixe-cl –report tripcounts -format=xml --project-dir ./VecAdv-01--search-dir src:r=./.-- ./am_call

xml レポートにループの反復回数に関する情報 (Trip_Counts フィールド) が追加されます。
サーベイレポートからの情報は保持されます。図 10 のエリア 5 を参照してください。
同様の方法で、その他のレポートを生成し、ループに関する有用な情報を得ることができます。利
用可能な収集とレポートの種類は、インテル® Advisor のコマンドライン・オプション -help
collect と -help report で確認できます。例えば、ソースコードのメモリー・アクセス・パターンの詳細を得るには、-map オプションを使用して Memory Access Patterns (メモリー・アクセス・パターン: MAP) 解析を実行します。
図 11 は、-map オプションを指定してインテル® Advisor を実行し、MAP 情報を収集した結果を示しています。MAP 解析はメモリー・アクセス・パターンの詳細な解析であるため、プログラムのループの数と複雑さに応じて、データ収集が完了するまで時間がかかります。レポートも非常に長くなります。そのため、図 11 では、このサンプルで注目しているループ (ID 3) のみ選択しています。この方法により、選択したループのデータのみ収集されレポートされます。このルー
プの MAP 情報を収集およびレポートするために、図 11 では次のコマンドを使用しています。

$ advixe-cl --collect map --project-dir ./VecAdv-01 --mark-up-list=3--search-dir src:r=./.-- ./am_call$ advixe-cl –report map -format=xml --project-dir ./VecAdv-01 --search-dir
src:r=./.-- ./am_call

このループで見つかったすべてのパターンのストライド分布とすべてのメモリーパターンの情報がレポートされています。図 11 では、それらのレポートの 1 つ (ユニット・ストライド・アクセスを示しているパターン ID = "186" のパターン) のみが示されています。

図 10: トリップカウント解析を追加

図 11: MAP 解析を追加

まとめ
最高のパフォーマンスを得るには、最近のプロセッサーで利用可能な並列処理のすべての機会をソフトウェアで活用する必要があります。最新のプロセッサーは、コア数が増え続けるとともに、SIMD レジスターの幅も広くなっています。ベクトル化、スレッド化、効率的なメモリー利用を組み合わせることで、より高速にコードを実行できます。ベクトル化やマルチスレッド化の効果は倍増するため、各コアのベクトル化は重要なステップになります。
各コアから優れたパフォーマンスを引き出すことは、複数のコアを効率的に使用するために必要なステップです。
この記事では、インテル® コンパイラーとインテル® Advisor 2018 を使用したベクトル化の概要を説明しました。また、ソフトウェアを単一コアで高速に実行できるようにする、コードの現代化の最初のステップとして使用できるベクトル化パフォーマンス解析の方法を例を挙げて紹介しました。
この例で使用したコードは書籍 1 の第 8 章に掲載されているもので、ニュートン・ラフソン法のアルゴリズムを実装してアメリカン・コール・オプションの近似解を求めています。ソースコード
は書籍の Web サイト (http://www.lotsofcores.com/ (英語)) からダウンロードできます。

参考文献 (英語)
1. J. Reinders and J. Jeffers, High Performance Parallelism Pearls, vol. 2, Morgan
Kaufmann, 2015.
2. Barone-Adesi and W. R. G., Efficient analytic approximation of american option
values, J. Finance, 1987.
コンパイラーの最適化に関する詳細は、最適化に関する注意事項を参照してください。