簡體   English   中英

在.NET Framework 4.6中使用C#的SIMD操作速度較慢

[英]using SIMD operation from C# in .NET framework 4.6 is slower

我目前正在嘗試使用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);
}

正如@mike z所指出的那樣,你需要確保你處於發布模式並且以64位為目標,否則RuyJIT(支持SIMD的編譯器)將無法工作(目前它只支持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)的valew,就像這樣:

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);


    }

這里的表現更加顯着。 在我的測試中,我設法比vals.Aggregate(0, (a, i) => a + i)性能提高了16倍

但是,從理論的角度來看,如果例如Vector<int>.Count返回4,那么高於性能提升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>()引起的開銷。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM