そういえばVisual Studio 2015からC#でもSIMDが使えるようになったんだっけ・・・と言うことで使ってみた。
SIMDを有効にするには3つの条件がある。
- .NET Framework 4.6を使用すること。
- 64bitコードを選択すること。
- NuGetからNumerics.Vectors(v4.1)をインストールすること。
プロジェクトのプロパティを開いて次の二ヶ所のオプションを変更しておく。
次にNuGetパッケージの管理を開いて、System.Numerics.Vectorsを追加する。
これで準備は出来たのでコードを書いてみる。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Numerics; namespace SimdTest { class Program { static void Main(string[] args) { DateTime startTime = DateTime.Now; double answer = 0.0; for (int i = 0;i < 1000000;i++) { answer = 1.0; for (int j = 1;j <= 100;j++) { answer = answer * (double )j; } } DateTime lastTime = DateTime.Now; Console.WriteLine("{0}ms, answer = {1}", (lastTime - startTime).TotalMilliseconds, answer); Console.WriteLine("Count = {0}, Hardware = {1}", Vector<double>.Count, Vector.IsHardwareAccelerated); startTime = DateTime.Now; for (int i = 0; i < 1000000; i++) { Vector<double> answerTemp = new Vector<double>(new double[] {1, 1, 1, 1}); Vector<double> multiTemp = new Vector<double>(new double[] {0, 25, 50, 75}); for (int j = 1; j <= 25; j++) { multiTemp += Vector<double>.One; answerTemp *= multiTemp; } answer = answerTemp[0] * answerTemp[1] * answerTemp[2] * answerTemp[3]; } lastTime = DateTime.Now; Console.WriteLine("{0}ms, answer = {1}", (lastTime - startTime).TotalMilliseconds, answer); } } }
と言うわけで、100の階乗を求める処理を書いてみた。
非常にシンプルな例なのだが、35%程度の高速化にしかならない。
Vector.IsHardwareAcceleratedを参照すると確かにSIMDが使われている事が分かる。
Vector<double>.Countが4を返すのでSSEではなくIntel AVXにも対応している様子。AVX非対応(SSE2対応)PCだとVector<double>.Countが2を返してきます。
本来的にはVector<double>.Countの値を見て並列数を判断するべきなのかもしれないが、4で決め打ちしています。
#OpenCL使えば手っ取り早いダロという話もあるけど、自宅や会社で使ってるPCが対応していないんだよね。