4

現在、C# のみを使用して巨大な配列内のすべての値の合計を計算し、SIMD を使用してパフォーマンスを比較しようとしていますが、SIMD バージョンはかなり遅いです。以下のコード スニペットを参照して、何か不足している場合はお知らせください。「vals」は、画像ファイルから読み取られる巨大な配列であり、無駄を省くためにその部分を省略しています。

var watch1 = new Stopwatch();
watch1.Start();
var total = vals.Aggregate(0, (a, i) => a + i);
watch1.Stop();
Console.WriteLine(string.Format("Total is: {0}", total));
Console.WriteLine(string.Format("Time taken: {0}", watch1.ElapsedMilliseconds));

var watch2 = new Stopwatch();
watch2.Start();
var sTotal = GetSIMDVectors(vals).Aggregate((a, i) => a + i);
int sum = 0;
for (int i = 0; i < Vector<int>.Count; i++)
    sum += sTotal[i];
watch2.Stop();
Console.WriteLine(string.Format("Another Total is: {0}", sum));
Console.WriteLine(string.Format("Time taken: {0}", watch2.ElapsedMilliseconds));

および GetSIMDVectors メソッド

private static IEnumerable<Vector<int>> GetSIMDVectors(short[] source)
{
    int vecCount = Vector<int>.Count;
    int i = 0;
    int len = source.Length;
    for(i = 0; i + vecCount < len; i = i + vecCount)
    {
        var items = new int[vecCount];
        for (int k = 0; k < vecCount; k++)
        {
            items[k] = source[i + k];
        }
        yield return new Vector<int>(items);
    }
    var remaining = new int[vecCount];
    for (int j = i, k =0; j < len; j++, k++)
    {
        remaining[k] = source[j];
    }
    yield return new Vector<int>(remaining);
}
4

1 に答える 1

9

@mike z が示しているように、リリース モードで 64 ビットをターゲットにしていることを確認する必要があります。そうしないと、SIMD をサポートするコンパイラである RuyJIT が機能しません (現在のところ、64 ビット アーキテクチャでのみサポートされています)。また、実行前にチェックすることは、次を使用することを常にお勧めします。

Vector.IsHardwareAccelerated;

また、ベクターを作成する前に、最初に配列を作成するために for ループを使用する必要はありません。コンストラクターを使用して、元のソース配列からベクターを作成するだけですvector<int>(int[] array,int index)

yield return new Vector<int>(source, i);

それ以外の

var items = new int[vecCount];
for (int k = 0; k < vecCount; k++)
{
    items[k] = source[i + k];
}
yield return new Vector<int>(items);

このようにして、ランダムに生成された大きな配列を使用して、パフォーマンスを3.7倍近く向上させることができました。

さらに、次のように、 の値を取得するとすぐに合計を直接計算する方法でメソッドを変更する場合new Vector<int>(source, i):

private static int GetSIMDVectorsSum(int[] source)
    {
        int vecCount = Vector<int>.Count;
        int i = 0;
        int end_state = source.Length;

        Vector<int> temp = Vector<int>.Zero;


        for (; i < end_state; i += vecCount)
        {
            temp += new Vector<int>(source, i);

        }

        return Vector.Dot<int>(temp, Vector<int>.One);


    }

ここでは、パフォーマンスがさらに劇的に向上します。テストでは、パフォーマンスが16 倍向上しました。vals.Aggregate(0, (a, i) => a + i)

ただし、理論的な観点からは、たとえばVector<int>.Count4 が返された場合、パフォーマンスが4 倍以上向上した場合は、ベクトル化されたバージョンを比較的最適化されていないコードと比較していることを示しています。

それがvals.Aggregate(0, (a, i) => a + i)あなたの場合の部分になります。したがって、基本的に、ここには最適化の余地が十分にあります。

単純な for ループに置き換えると

private static int no_vec_sum(int[] vals)
{
    int end = vals.Length;
    int temp = 0;

    for (int i = 0; i < end; i++)
    {
        temp += vals[i];
    }
    return temp;
}

パフォーマンスが1.5 倍しか向上しません。ただし、この非常に特殊なケースでは、操作の単純さを考慮すると、まだ改善されています。

言うまでもなく、ベクトル化されたバージョンでは、反復ごとに作成することによって生じるオーバーヘッドを克服するために、大規模な配列が必要です。new Vector<int>()

于 2016-01-22T07:32:23.540 に答える