前回、前々回でイマイチだったSystem.Numerics.Vector、本当は速かった
int型配列の要素の合計の処理時間、シングルスレッド編
test19が今回追加したもの
グラフにすると
普通のForループより2倍速くなった!
マルチスレッド編
前回で速かったTest12~15よりも2倍近く速く、同じVectorを使ったTest19より4倍速くなった!
グラフにすると
シングルスレッドのForループより6倍速くなった!
前回VectorAddを使っても遅かったのは
int型配列の値を合計していくと大きい数値になってint型に収まらなくなるからlong型で合計する必要があるので
long型のVectorを使うことになるけど
int型配列から直接long型Vectorを作成することはできないので
long型配列を新たに作成する、この処理に時間がかかっていた
今回
github.com
ここに書いてあることわかればすごい便利そうなんだけど、わからなくて、それでもVectorのWidenメソッドで変換ぽいのができるみたい?
どうやら1つのint型Vectorから、2つのlong型Vector作成できるみたいってことで、これを使えばint型配列から直接int型Vectorを作成して、それをWidenメソッドでlong型のVectorを作成できるので、時間のかかるlong型配列を作らなくていい!あとは
long型配列を作る時間 > Widenメソッドの処理時間
になっていれば速くなるはずって試したら速かったねえ、Widenすごい
シングルスレッド編
private long TestVectorAdd_VectorWiden(int[] Ary)
{
var subtotal = new Vector<long>();
int simdIntLength = Vector<int>.Count;
int lastIndex = Ary.Length - (Ary.Length % simdIntLength);
Vector<long> l1;
Vector<long> l2;
for (int i = 0; i < lastIndex; i += simdIntLength)
{
System.Numerics.Vector.Widen(new Vector<int>(Ary,i), out l1, out l2);
subtotal = System.Numerics.Vector.Add(subtotal, l1);
subtotal = System.Numerics.Vector.Add(subtotal, l2);
}
long total = 0;
for (int i = 0; i < Vector<long>.Count; i++)
{
total += subtotal[i];
}
for (int i = lastIndex; i < Ary.Length; i++)
{
total += Ary[i];
}
return total;
}
Widenの引数についているoutってのが、わかってないので使い方が違うかもしれないけど動いている、これでシングルスレッドForループの2倍速
マルチスレッド編
private long Test99_ParallelForEachPartitioner_VectorWiden(int[] ary)
{
long total = 0;
int windowSize = ary.Length / Environment.ProcessorCount;
var rangePartitioner = Partitioner.Create(0, ary.Length, windowSize);
int simdLength = Vector<int>.Count;
Parallel.ForEach(rangePartitioner, (range) =>
{
int lastIndex = range.Item2 - ((range.Item2 - range.Item1) % simdLength);
var v = new Vector<long>();
for (int i = range.Item1; i < lastIndex; i += simdLength)
{
Vector<long> v1;
Vector<long> v2;
System.Numerics.Vector.Widen(new Vector<int>(ary, i), out v1, out v2);
v = System.Numerics.Vector.Add(v, v1);
v = System.Numerics.Vector.Add(v, v2);
}
long subtotal = 0;
for (int i = 0; i < Vector<long>.Count; i++)
{
subtotal += v[i];
}
for (int i = lastIndex; i < range.Item2; i++)
{
subtotal += ary[i];
}
Interlocked.Add(ref total, subtotal);
});
return total;
}
2020/02/27追記、間違っていた行をコメントアウトして、その下の行に訂正
これでシングルスレッドForループの6倍速、コア数が多いCPUや新しいCPUならもっと差が出るはず
それにしてもWidenなんて便利なメソッドがあったんだねえ、全然気づかなかったわ、無知は遅なり
無知ついでに欲を言えば
1つのbyte型Vectorから4つのint型Vectorを作成
1つのbyte型Vectorから8つのlong型Vectorを作成
これができるWidenもあればいいなあ
もっと言えばint型配列から直接long型Vectorを作成できるとか、byte配列から直接long型Vectorができればなあ
アプリダウンロード先
20200205_int配列の値の合計1.1
20200207_int配列の値の合計マルチスレッド1.1
ギットハブ
シングルスレッド編
マルチスレッド編
github.com
関連記事
次回は8日後
gogowaten.hatenablog.com
次回は翌日
gogowaten.hatenablog.com
これは失敗だった
前回、マルチスレッド編
gogowaten.hatenablog.com
前々回、シングルスレッド編
gogowaten.hatenablog.com