免费A级毛片无码专区网站-成人国产精品视频一区二区-啊 日出水了 用力乖乖在线-国产黑色丝袜在线观看下-天天操美女夜夜操美女-日韩网站在线观看中文字幕-AV高清hd片XXX国产-亚洲av中文字字幕乱码综合-搬开女人下面使劲插视频

是什么讓.NET7的Min和Max方法性能暴增了45倍?

簡(jiǎn)介在之前的一篇文章.NET性能系列文章一:.NET7的性能改進(jìn)中我們聊到Linq中的Min()和Max()方法.NET7比.NET6有高達(dá)45倍的性能提升,當(dāng)時(shí)Benchmark代碼和結(jié)果如下所示:
[Params(1000)]public int Length { get; set; }private int[] arr;[GlobalSetup]public void GlobalSetup() => arr = Enumerable.Range(0, Length).ToArray();[Benchmark]public int Min() => arr.Min();[Benchmark]public int Max() => arr.Max();方法運(yùn)行時(shí)數(shù)組長(zhǎng)度平均值比率分配Min

是什么讓.NET7的Min和Max方法性能暴增了45倍?

文章插圖
10003,494.08 ns53.2432 BMin
是什么讓.NET7的Min和Max方法性能暴增了45倍?

文章插圖
100065.64 ns1.00-Max
是什么讓.NET7的Min和Max方法性能暴增了45倍?

文章插圖
10003,025.41 ns45.9232 BMax
是什么讓.NET7的Min和Max方法性能暴增了45倍?

文章插圖
100065.93 ns1.00-
是什么讓.NET7的Min和Max方法性能暴增了45倍?

文章插圖
可以看到有高達(dá)45倍的性能提升,那就有小伙伴比較疑惑,在.NET7中到底是做了什么讓它有如此大的性能提升?所以本文就通過.NET7中的一些pr帶大家一起探索下.NET7的Min()和Max()方法是如何變快的 。
探索首先我們打開.NET Runtime的倉庫,應(yīng)該沒有人不會(huì)知道倉庫的地址吧?里面包含了.NET運(yùn)行時(shí)所有的代碼,包括CLR和BCL庫 。地址如下所示:https://github.com/dotnet/runtime
是什么讓.NET7的Min和Max方法性能暴增了45倍?

文章插圖
然后我們熟練的根據(jù)命名空間System.Linq找到Linq所在的文件夾位置,如下所示:
是什么讓.NET7的Min和Max方法性能暴增了45倍?

文章插圖
可以看到很多Linq相關(guān)的方法都在這個(gè)文件夾內(nèi),讓我們先來找一找Max()方法所對(duì)應(yīng)的類 。就是下方所示,我們可以看到剛好異步小王子Stephen Toub大佬提交了一個(gè)優(yōu)化代碼 。
是什么讓.NET7的Min和Max方法性能暴增了45倍?

文章插圖
然后我們點(diǎn)擊History查看這個(gè)類的提交歷史,我們發(fā)現(xiàn)Stephen大佬在今年多次提交代碼,都是優(yōu)化其性能 。
是什么讓.NET7的Min和Max方法性能暴增了45倍?

文章插圖
找到Stephen大佬的第一個(gè)提交,我們發(fā)現(xiàn)在Max的代碼中,多了一個(gè)特殊的路徑,如果數(shù)據(jù)類型為int[],那么就走單獨(dú)的一個(gè)方法重載,并在這個(gè)重載中啟用了SIMD向量化,代碼如下所示:
是什么讓.NET7的Min和Max方法性能暴增了45倍?

文章插圖
SIMD向量化在我之前的多篇文章中都有提到(如:.NET如何快速比較兩個(gè)byte數(shù)組是否相等),它是CPU的特殊指令,使用它可以大幅度的增強(qiáng)運(yùn)算性能,我猜這就是性能提升的原因 。
我們可以看到在上面只為int[]做了優(yōu)化,然后繼續(xù)瀏覽了Stephen大佬的其它幾個(gè)PR,Stephen大佬將代碼抽象了一下,使用了泛型的特性,然后順便為其它的基本值類型都做了優(yōu)化 。能享受到性能提升的有byte sbyte ushort short uint int ulong long nuint nint 。
是什么讓.NET7的Min和Max方法性能暴增了45倍?

文章插圖
所以我們以最后一個(gè)提交為例,看看到底是用了什么SIMD指令,什么樣的方法來提升的性能 。抽取出來的核心代碼如下所示:
private static T MinMaxInteger<T, TMinMax>(this IEnumerable<T> source)    where T : struct, IBinaryInteger<T>    where TMinMax : IMinMaxCalc<T>{    T value;    if (source.TryGetSpan(out ReadOnlySpan<T> span))    {        if (span.IsEmpty)        {            ThrowHelper.ThrowNoElementsException();        }        // 判斷當(dāng)前平臺(tái)是否支持使用Vector-128 或者 總數(shù)據(jù)長(zhǎng)度是否小于128位        // Vector128是指硬件支持同時(shí)計(jì)算128位二進(jìn)制數(shù)據(jù)        if (!Vector128.IsHardwareAccelerated || span.Length < Vector128<T>.Count)        {            // 進(jìn)入到此路徑,說明最基礎(chǔ)的Vector128都不支持,那么直接使用for循環(huán)來比較            value = span[0];            for (int i = 1; i < span.Length; i++)            {                if (TMinMax.Compare(span[i], value))                {                    value = span[i];                }            }        }        // 判斷當(dāng)前平臺(tái)是否支持使用Vector-256 或者 總數(shù)據(jù)長(zhǎng)度是否小于256位        // Vector256是指硬件支持同時(shí)計(jì)算256位二進(jìn)制數(shù)據(jù)        else if (!Vector256.IsHardwareAccelerated || span.Length < Vector256<T>.Count)        {            // 進(jìn)入到此路徑,說明支持Vector128但不支持Vector256            // 那么進(jìn)入128位的向量化的比較            // 獲取當(dāng)前數(shù)組的首地址,也就是指向第0個(gè)元素            ref T current = ref MemoryMarshal.GetReference(span);            // 獲取Vector128能使用的最后地址,因?yàn)檎麄€(gè)數(shù)組占用的bit位有可能不能被128整除            // 也就是說最后的尾巴不夠128位讓CPU跑一次,那么就直接最后往前數(shù)128位,讓CPU能完整的跑完            ref T lastVectorStart = ref Unsafe.Add(ref current, span.Length - Vector128<T>.Count);            // 從內(nèi)存首地址加載0-127bit數(shù)據(jù),作為最大值的基準(zhǔn)            Vector128<T> best = Vector128.LoadUnsafe(ref current);            // 計(jì)算下一個(gè)的位置,也就是偏移128位            current = ref Unsafe.Add(ref current, Vector128<T>.Count);            // 循環(huán)比較 確保地址小于最后地址            while (Unsafe.IsAddressLessThan(ref current, ref lastVectorStart))            {                // 此時(shí)TMinMax.Compare重載代碼 => Vector128.Max(left, right);                // Vector128.Max 會(huì)根據(jù)類型一一比較,每x位最大的返回,                // 比如int就是每32位比較,詳情可以看我后文的解析                best = TMinMax.Compare(best, Vector128.LoadUnsafe(ref current));                current = ref Unsafe.Add(ref current, Vector128<T>.Count);            }            // 最后一組Vector128進(jìn)行比較            best = TMinMax.Compare(best, Vector128.LoadUnsafe(ref lastVectorStart));            // 由于Vector128最后的結(jié)果是128位,比如我們類型是int32,那么最后的結(jié)果就有            // 4個(gè)int32元素,我們還需要從這4個(gè)int32元素中找到最大的            value = best[0];            for (int i = 1; i < Vector128<T>.Count; i++)            {                // 這里 TMinMax.Compare就是簡(jiǎn)單的大小于比較                // left > right                if (TMinMax.Compare(best[i], value))                {                    value = best[i];                }            }        }        else        {            // Vector256執(zhí)行流程和Vector128一致            // 只是它能一次性判斷256位,舉個(gè)例子就是一個(gè)指令8個(gè)int32            ref T current = ref MemoryMarshal.GetReference(span);            ref T lastVectorStart = ref Unsafe.Add(ref current, span.Length - Vector256<T>.Count);            Vector256<T> best = Vector256.LoadUnsafe(ref current);            current = ref Unsafe.Add(ref current, Vector256<T>.Count);            while (Unsafe.IsAddressLessThan(ref current, ref lastVectorStart))            {                best = TMinMax.Compare(best, Vector256.LoadUnsafe(ref current));                current = ref Unsafe.Add(ref current, Vector256<T>.Count);            }            best = TMinMax.Compare(best, Vector256.LoadUnsafe(ref lastVectorStart));            value = best[0];            for (int i = 1; i < Vector256<T>.Count; i++)            {                if (TMinMax.Compare(best[i], value))                {                    value = best[i];                }            }        }    }    else    {        // 如果不是基本類型的數(shù)組,那么進(jìn)入迭代器,使用原始方法比較        using (IEnumerator<T> e = source.GetEnumerator())        {            if (!e.MoveNext())            {                ThrowHelper.ThrowNoElementsException();            }            value = e.Current;            while (e.MoveNext())            {                T x = e.Current;                if (TMinMax.Compare(x, value))                {                    value = x;                }            }        }    }    return value;}

經(jīng)驗(yàn)總結(jié)擴(kuò)展閱讀