操作を実行するハードウェアの最低レベルと、関連する一般的な基本操作(つまり、コード実行時のすべてのプログラミング言語の実際の実装に一般的なもの)で、ベクトル化が通常ループよりも劇的に速いのはなぜですか?
ベクトル化の使用時にループしないとき、コンピューターは何をしますか(プログラマーが書いたものではなく、コンピューターが実行する実際の計算について話している)、またはそれはどう違うのですか?
私は、なぜその違いがそれほど重要なのかを確信することができませんでした。ベクトル化されたコードはどこかでループのオーバーヘッドを削ぎ落とすでしょうが、コンピューターはまだ同じ数の操作を実行する必要がありますよね?たとえば、サイズNのベクトルにスカラーを乗算する場合、どちらの方法でも実行するためにN個の乗算が必要になりますよね。
ベクトル化(この用語は通常使用されます)は、SIMD(単一命令、複数データ)操作を指します。
つまり、本質的に、1つの命令が複数のオペランドに対して同じ操作を並行して実行するということです。たとえば、サイズNのベクトルにスカラーを乗算するには、Mを同時に操作できるサイズのオペランドの数を呼び出します。その場合、実行する必要がある命令の数は約N/Mであり、(純粋なスカラー演算では)N演算を実行する必要があります。
たとえば、Intelの現在のAVX 2命令セットは256ビットのレジスタを使用しています。これらは、それぞれ64ビットの4つのオペランドのセット、または32ビットの8つのオペランドのセットを保持(および操作)するために使用できます。
したがって、32ビットの単精度実数を扱っていると仮定すると、単一の命令で一度に8つの演算(あなたの場合は乗算)を行うことができるので、(少なくとも理論的には)N個の乗算を終了することができますN/8乗算命令のみ。少なくとも理論的には、これにより、一度に1つの命令を実行する場合の約8倍の速度で操作を完了することができます。
もちろん、正確な利点は、命令ごとにサポートするオペランドの数によって異なります。 Intelの最初の試みでは、64ビットのレジスタのみがサポートされていたため、一度に8つのアイテムを操作するには、それらのアイテムはそれぞれ8ビットしか使用できませんでした。現在、256ビットのレジスタをサポートしており、512ビットのサポートを発表しています(そして、少なくとも通常のコンシューマプロセッサではなく、いくつかのハイエンドプロセッサで出荷している場合もあります)。この機能を十分に活用することも、控えめに言っても簡単ではありません。実際にN個のオペランドを使用できるように命令をスケジューリングし、適切なタイミングで適切な場所に配置することは、必ずしも簡単な作業ではありません。
物事を視野に入れるために、(現在の古代の)Cray 1はまさにこの方法で多くの速度を獲得しました。ベクトルユニットは、64ビットの64個のレジスタセットで動作するため、クロックサイクルごとに64の倍精度演算を実行できます。最適化されたベクトル化されたコードでは、(はるかに低い)クロック速度のみに基づいて予想されるよりも、現在のCPUの速度にはるかに近かった。しかし、それを最大限に活用することは必ずしも容易ではありませんでした(それでもそうではありません)。
ただし、ベクトル化はnotであり、CPUが並行して操作を実行できる唯一の方法です。また、命令レベルの並列処理の可能性もあります。これにより、単一のCPU(またはCPUの単一のコア)が一度に複数の命令を実行できます。最新のCPUのほとんどには、命令がロード、ストア、およびALUの組み合わせである場合、クロックサイクルごとに最大で約4命令を実行するハードウェアが含まれています。平均して、1クロックあたり2命令近く、またはメモリがボトルネックになっていない場合は適切に調整されたループで、ほぼ定期的に実行できます。
それから、もちろん、マルチスレッドがあります-命令の複数のストリームを(少なくとも論理的に)別のプロセッサ/コアで実行します。
そのため、最新のCPUには、たとえば4つのコアがあり、それぞれがクロックごとに2つのベクトル乗算を実行でき、これらの各命令は8つのオペランドで動作できます。そのため、少なくとも理論的には、1クロックあたり4 * 2 * 8 = 64操作を実行できます。
一部の命令では、スループットが向上または低下しています。たとえば、FPは、スループットをFMAより低くするか、Skylakeの前にIntelで乗算します(2ではなくクロックごとに1つのベクトル)。ただし、ANDまたはXOR =クロックスループットごとに3つのベクトルがあり、AND/XOR/OR実行ユニットを構築するのに多くのトランジスタを必要としないため、CPUがそれらを複製します。高スループット命令を使用する場合、特定の実行ユニットのボトルネックではなく、コアの順序が乱れている部分が一般的です。
ベクトル化には2つの主な利点があります。
主な利点は、ベクトル命令をサポートするように設計されたハードウェアには、一般に、ベクトル命令が使用される場合に一般に複数のALU操作を実行できるハードウェアがあることです。たとえば、16要素のベクトル命令を使用して16の加算を実行するように要求した場合、すべての加算を一度に実行できる16の並列加算器があります。 onlyこれらすべての加算器にアクセスする方法1 ベクトル化によるものです。スカラー命令を使用すると、1つの孤独な加算器が得られます。
通常、ベクトル命令を使用すると、オーバーヘッドがいくらか節約されます。データを大きなチャンク(一部の最近のIntel CPUでは一度に最大512ビット)でロードおよび保存すると、各ループの反復により多くの作業が行われるため、一般的にループのオーバーヘッドは相対的に低くなります。2、同じ作業を行うために必要な命令が少なくなるため、CPUフロントエンドのオーバーヘッドが低くなります。
最後に、loopsとvectorizationの間の二分法は奇妙です。非ベクトルコードを取得してベクトル化する場合、通常、ループが以前にあった場合はループが発生し、発生しなかった場合は発生しません。比較は、実際にはスカラー(非ベクター)命令とベクター命令の間です。
1 または、16のうち少なくとも15は、おそらくスカラー演算にも使用されます。
2 おそらく、スカラーの場合、ループのアンロールが大量に発生するという犠牲を払っても、同様のループオーバーヘッドの利点を得ることができます。
ベクトル化は、並列処理の一種です。これにより、より多くのコンピューターハードウェアを計算の実行に充てることができるため、計算が高速になります。
多くの数値問題、特に偏微分方程式の解法では、多数のセル、要素、またはノードに対して同じ計算を実行する必要があります。ベクトル化は、多くのセル/要素/ノードの計算を並行して実行します。
ベクトル化は特別なハードウェアを使用します。各並列処理ユニットが完全に機能するCPUコアであるマルチコアCPUとは異なり、ベクトル処理ユニットは単純な操作のみを実行でき、すべてのユニットはデータ値のシーケンスを操作して同じ操作を同時に実行します(ベクトル)同時に。