午後わてんのブログ

ベランダ菜園とWindows用アプリ作成(WPFとC#)

本当は速いSystem.Numerics.Vector.Addを使って、int型配列の値合計2回め

前回、前々回でイマイチだったSystem.Numerics.Vector、本当は速かった

 

int型配列の要素の合計の処理時間、シングルスレッド編

f:id:gogowaten:20200211132244p:plain

test19が今回追加したもの

 

グラフにすると

f:id:gogowaten:20200211132420p:plain

f:id:gogowaten:20200211132431p:plain

普通のForループより2倍速くなった!

 

 

マルチスレッド編

f:id:gogowaten:20200211135002p:plain

前回で速かったTest12~15よりも2倍近く速く、同じVectorを使ったTest19より4倍速くなった!

 

グラフにすると

f:id:gogowaten:20200211135153p:plain

f:id:gogowaten:20200211135201p:plain

シングルスレッドのForループより6倍速くなった!

 

 

前回VectorAddを使っても遅かったのは

int型配列の値を合計していくと大きい数値になってint型に収まらなくなるからlong型で合計する必要があるので

long型のVectorを使うことになるけど

int型配列から直接long型Vectorを作成することはできないので

long型配列を新たに作成する、この処理に時間がかかっていた

 

今回

github.com

ここに書いてあることわかればすごい便利そうなんだけど、わからなくて、それでもVectorのWidenメソッドで変換ぽいのができるみたい?

f:id:gogowaten:20200211142426p:plain

どうやら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;
}

f:id:gogowaten:20200211143106p:plain

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 % simdLength);
        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追記、間違っていた行をコメントアウトして、その下の行に訂正

f:id:gogowaten:20200227144650p:plain

これでシングルスレッド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