# 前言 ### 在上一篇文章[【基於ASP.NET ZERO,開發SaaS版供應鏈管理系統】](https://www.cnblogs.com/freedyang/p/17679280.html)中有提到對Webhook功能的擴展改造,本文詳細介紹一下具體過程。 ### Webhook功能操作說明 ...
發佈 VectorTraits v1.0, 它是C#下增強SIMD向量運算的類庫
VectorTraits: SIMD Vector type traits methods (SIMD向量類型的特征方法).
NuGet: https://www.nuget.org/packages/VectorTraits/1.0.0
源代碼: https://github.com/zyl910/VectorTraits
用途
總所周知,使用SIMD指令集,能夠加速 多媒體處理(圖形、圖像、音頻、視頻...)、人工智慧、科學計算 等。
然而,傳統的SIMD編程存在以下痛點:
- 難以跨平臺。因為不同的CPU體系,提供了不同的SIMD指令集,例如 X86與Arm平臺的SIMD指令集存在很多差異。如果程式欲移植到另一平臺下,則需要查找該平臺的SIMD指令集手冊,重新開發一遍。
- 位寬難以升級。即使是同一個平臺,隨著發展,會逐漸增加位數更寬的指令集。例如X86平臺,除了已淘汰的64位MMX系列指令外,提供了了 128位SSE指令集、256位的AVX指令集,且部分高端處理器開始支持 512位的AVX-512指令集。以前用128位SSE系列指令編寫的演算法,若想移植到256位的AVX指令集,需要重新開發一遍,才能充分利用更寬的SIMD指令集。
- 代碼可讀性差,開發門檻高。很多現代C語言編譯器為SIMD指令,映射了
內在函數
(Intrinsic Functions),比編寫彙編代碼要容易了不少,且可讀性提升了不少。但是由於函數名使用了一些晦澀的縮寫,且C語言不支持函數名重載,以及C語言本身的複雜性,導致代碼可讀性與開發難度,仍有較高的門檻。
2016年的 .NET Core 1.0
新增了 Vector<T>
等向量類型,在很大程度上解決了以上痛點。
- 容易跨平臺。
.NET
平臺的程式,是通過JIT(Just-In-Time Compiler,即時編譯器)運行的。只編寫一套基於向量方法的演算法,且僅需編譯為一套程式。隨後該程式在不同平臺上運行時,向量方法會被JIT編譯為平臺特有的SIMD指令集,從而充分的享用硬體加速。 - 位寬能自動升級。對於
Vector<T>
類型,它的長度不是固定的,而是與該處理器的最長向量寄存器相同。具體來說,若CPU支持AVX指令集(嚴格來說是AVX2及以上),Vector<T>
類型便是256位;若CPU僅支持SSE指令集(嚴格來說是SSE2及以上),Vector<T>
類型便是128位。簡單來說,在編寫程式時僅使用Vector<T>
類型就行,程式運行時,JIT會自動使用最寬的SIMD指令集。 - 代碼可讀性較高,降低了開發門檻。
.NET
平臺下,向量類型的方法名都是用完整英文單詞所組成,並充分利用了函數名重載等 C# 語法特點,使這些方法名既簡潔、又清晰。使得代碼可讀性有了很大的提高。
向量類型Vector<T>
雖然設計的好,但它缺少許多重要的向量函數,如 Ceiling、Sum、Shift、Shuffle 等。導致很多演算法,難以用向量類型來實現。
當 .NET
平臺版本升級時, 有時會增加若幹個向量方法。例如2022年發佈的 .NET 7.0
,增加了ShiftRightArithmetic、Shuffle 等函數。但目前的向量方法還是較少, 例如缺少飽和處理等.
為瞭解決缺少向量方法的問題,.NET Core 3.0
開始支持了內在函數。這能讓開發者直接使用SIMD指令集,但這又面臨了難以跨平臺與位寬難以升級等問題。隨著 .NET
平臺的不斷升級,會增加了更多的內在函數。例如 .NET 5.0
增加了 Arm平臺的內在函數。
對於開發類庫, 不能僅支持 .NET 7.0
,而是需要支持多個 .NET
版本。於是你會面臨繁瑣的版本檢查與條件處理. 而且 .NET Standard
類庫的最高版本(2.1),仍是是不支持Ceiling等向量方法的,導致版本檢查更加繁瑣.
本庫致力於解決以上麻煩, 使您能更方便的編寫跨平臺的SIMD演算法。
特點:
- 支持低版本的
.NET
程式(.NET Standard 1.1
,.NET Core 1.0
,.NET Framework 4.5
, ...)。能使低版本的.NET
程式,也能使用最新的向量函數. 例如.NET 7.0
所新增的 ShiftRightArithmetic、Shuffle 等。 - 功能強. 除了參考高版本
.NET
的向量方法外,本庫還參考內在函數,提供了很多有用的向量方法。例如 YClamp, YNarrowSaturate ... - 性能高。本庫能充分利用 X86、Arm架構的內在函數對向量類型的運算進行硬體加速,且能夠享受內聯編譯優化。且本庫解決了BCL的部分向量方法(如Multiply, Shuffle等)在一些平臺上沒有硬體加速的問題, 因它補充了硬體加速演算法.
- 軟體演算法也很快。若發現向量類型的某個方法不支持硬體加速時,.NET Bcl會切換為軟體演算法,但它軟體演算法很多是含有分支語句的,性能較差。而本庫的軟體演算法,是高度優化的無分支演算法。
- 使用方便。本庫不僅支持
Vector<T>
,還支持Vector128<T>
/Vector256<T>
等向量類型。工具類的類名很好記(Vectors/Vector64s/Vector128s/Vector256s),且通過同名的泛型類提供了許多常用的向量常數。 - 為每一個特征方法, 增加了一些獲取信息的的屬性. e.g.
_AcceleratedTypes
,_FullAcceleratedTypes
.
提示: 在 Visual Studio 的 Disassembly視窗可以查看運行時的彙編代碼. 例如在支持 Avx指令集的機器上運行時, Vectors.ShiftLeft_Const
會被內聯編譯優化為使用 vpsllw
指令. 且對於常量值(1), 會被編譯為指令的立即數.
例2: 使用 Vectors.ShiftLeft_Args
與 Vectors.ShiftLeft_Core
, 能將部分運算挪到迴圈外去提前處理. 例如在支持 Avx指令集的機器上運行時, 會在迴圈外設置好 xmm1
, 隨後在內迴圈的vpsllw
指令里使用了它. 且這裡展示了: 內聯編譯優化消除了冗餘的 xmm/ymm 轉換.
簡介
本庫為向量類型提供了許多重要的算術方法(如 Shift, Shuffle, NarrowSaturate)及常數, 使您能更方便的編寫跨平臺的SIMD運算代碼。它充分利用了 X86、Arm架構的內在函數實現硬體加速,且能夠享受內聯編譯優化。
常用類型:
Vectors
: 為向量類型, 提供了常用工具函數, e.g. Create(T/T[]/Span/ReadOnlySpan), CreatePadding, CreateRotate, CreateByFunc, CreateByDouble ... 它還為向量提供了特征方法, e.g. ShiftLeft、ShiftRightArithmetic、ShiftRightLogical、Shuffle ...Vectors<T>
: 為向量類型, 提供了各種元素類型的常數. e.g. Serial, SerialDesc, XyzwWMask, MantissaMask, MaxValue, MinValue, NormOne, FixedOne, E, Pi, Tau, VMaxByte, VReciprocalMaxSByte ...Vector64s/Vector128s/Vector256s
: 為固定位寬的向量(Vector64/Vector128/Vector256),提供了常用工具函數與特征方法.Vector64s<T>/Vector128s<T>/Vector256s<T>
: 為固定位寬的向量,提供了各種元素類型的常數.Scalars
: 為標量類型, 提供了各種工具函數. e.g. GetByDouble, GetFixedByDouble, GetByBits, GetBitsMask ...Scalars<T>
: 為標量類型, 提供了許多常數. e.g. ExponentBits, MantissaBits, MantissaMask, MaxValue, MinValue, NormOne, FixedOne, E, Pi, Tau, VMaxByte, VReciprocalMaxSByte ...VectorTextUtil
: 提供了一些向量的文本性工具函數. e.g. GetHex, Format, WriteLine ...
特征方法:
- 支持
.NET Standard 2.1
新增的向量方法: ConvertToDouble, ConvertToInt32, ConvertToInt64, ConvertToSingle, ConvertToUInt32, ConvertToUInt64, Narrow, Widen . - 支持
.NET 5.0
新增的向量方法: Ceiling, Floor . - 支持
.NET 6.0
新增的向量方法: Sum . - 支持
.NET 7.0
新增的向量方法: ExtractMostSignificantBits, Shuffle, ShiftLeft, ShiftRightArithmetic, ShiftRightLogical . - 提供縮窄飽和的向量方法: YNarrowSaturate, YNarrowSaturateUnsigned .
- 提供舍入的向量方法: YRoundToEven, YRoundToZero .
- 提供換位的向量方法: YShuffleInsert, YShuffleKernel, YShuffleG2, YShuffleG4, YShuffleG4X2 . 且提供了 ShuffleControlG2/ShuffleControlG4 enum.
- ...
- 完整列表: TraitsMethodList
支持的指令集:
- x86
- 256位向量: Avx, Avx2 .
- Arm
- 128位向量: AdvSimd .
入門指南
1) 通過NuGet安裝
可在'包管理器控制台'里輸入以下命令, 或是使用'包管理器'GUI來安裝本庫.
NuGet: PM> Install-Package VectorTraits
2) 用法示例
靜態類 Vectors
提供了許多方法, 例如 CreateRotate, ShiftLeft, Shuffle.
泛型結構體 Vectors<T>
為常用常數提供了欄位.
範例代碼在 samples/VectorTraits.Sample
文件夾. 源代碼如下.
using System;
using System.IO;
using System.Numerics;
#if NETCOREAPP3_0_OR_GREATER
using System.Runtime.Intrinsics;
#endif
using Zyl.VectorTraits;
namespace Zyl.VectorTraits.Sample {
class Program {
private static readonly TextWriter writer = Console.Out;
static void Main(string[] args) {
writer.WriteLine("VectorTraits.Sample");
writer.WriteLine();
VectorTraitsGlobal.Init(); // Initialization (初始化).
TraitsOutput.OutputEnvironment(writer); // Output environment info. It depends on `VectorTraits.InfoInc`. This row can be deleted when only VectorTraits are used (輸出環境信息. 它依賴 `VectorTraits.InfoInc`. 當僅使用 VectorTraits 時, 可以刪除本行).
writer.WriteLine();
// -- Start --
Vector<short> src = Vectors.CreateRotate<short>(0, 1, 2, 3, 4, 5, 6, 7); // The `Vectors` class provides some methods. For example, 'CreateRotate' is rotate fill (`Vectors` 類提供了許多方法. 例如 `CreateRotate` 是旋轉填充).
VectorTextUtil.WriteLine(writer, "src:\t{0}", src); // It can not only format the string, but also display the hexadecimal of each element in the vector on the right Easy to view vector data (它不僅能格式化字元串, 且會在右側顯示向量中各元素的十六進位. 便於查看向量數據).
// ShiftLeft. It is a new vector method in `.NET 7.0` (左移位. 它是 `.NET 7.0` 新增的向量方法)
const int shiftAmount = 1;
Vector<short> shifted = Vectors.ShiftLeft(src, shiftAmount); // shifted[i] = src[i] << shiftAmount.
VectorTextUtil.WriteLine(writer, "ShiftLeft:\t{0}", shifted);
#if NET7_0_OR_GREATER
// Compare BCL function (與BCL的函數做對比).
Vector<short> shiftedBCL = Vector.ShiftLeft(src, shiftAmount);
VectorTextUtil.WriteLine(writer, "Equals to BCL ShiftLeft:\t{0}", shifted.Equals(shiftedBCL));
#endif
// ShiftLeft_Const
VectorTextUtil.WriteLine(writer, "Equals to ShiftLeft_Const:\t{0}", shifted.Equals(Vectors.ShiftLeft_Const(src, shiftAmount))); // If the parameter shiftAmount is a constant, you can also use the Vectors' ShiftLeft_Const method. It is faster in many scenarios (若參數 shiftAmount 是常數, 還可以使用 Vectors 的 ShiftLeft_Const 方法. 它在不少場景下更快).
writer.WriteLine();
// Shuffle. It is a new vector method in `.NET 7.0` (換位. 它是 `.NET 7.0` 新增的向量方法)
Vector<short> desc = Vectors<short>.SerialDesc; // The generic structure 'Vectors<T>' provides fields for commonly used constants. For example, 'SerialDesc' is a descending order value (泛型結構體 `Vectors<T>` 為常用常數提供了欄位. 例如 `SerialDesc` 是降序的順序值).
VectorTextUtil.WriteLine(writer, "desc:\t{0}", desc);
Vector<short> dst = Vectors.Shuffle(shifted, desc); // dst[i] = shifted[desc[i]].
VectorTextUtil.WriteLine(writer, "Shuffle:\t{0}", dst);
#if NET7_0_OR_GREATER
// Compare BCL function (與BCL的函數做對比).
Vector<short> dstBCL = default; // Since `.NET 7.0`, the Shuffle method has been provided in Vector128/Vector256, but the Shuffle method has not yet been provided in Vector (自 `.NET 7.0` 開始, Vector128/Vector256 里提供了 Shuffle 方法, 但 Vector 里尚未提供 Shuffle 方法).
if (Vector<short>.Count == Vector128<short>.Count) {
dstBCL = Vector128.Shuffle(shifted.AsVector128(), desc.AsVector128()).AsVector();
} else if (Vector<short>.Count == Vector256<short>.Count) {
dstBCL = Vector256.Shuffle(shifted.AsVector256(), desc.AsVector256()).AsVector();
}
VectorTextUtil.WriteLine(writer, "Equals to BCL Shuffle:\t{0}", dst.Equals(dstBCL));
#endif
// Shuffle_Args and Shuffle_Core
Vectors.Shuffle_Args(desc, out var args0, out var args1); // The suffix is the `Args' method used for parameter calculation, which involves processing such as parameter transformation in advance It is suitable for external loop (尾碼是 `Args` 的方法, 用於參數計算, 即提前進行參數變換等處理. 它適合放在外迴圈).
Vector<short> dst2 = Vectors.Shuffle_Core(shifted, args0, args1); // The suffix is the `Core` method used for core calculations, which calculates based on cached parameters It is suitable for internal loop to improve performance (尾碼是 `Core` 方法, 用於核心計算, 既根據已緩存的參數進行計算. 它適合放在內迴圈, 便於改善性能).
VectorTextUtil.WriteLine(writer, "Equals to Shuffle_Core:\t{0}", dst.Equals(dst2));
writer.WriteLine();
// Show AcceleratedTypes.
VectorTextUtil.WriteLine(writer, "ShiftLeft_AcceleratedTypes:\t{0}", Vectors.ShiftLeft_AcceleratedTypes);
VectorTextUtil.WriteLine(writer, "Shuffle_AcceleratedTypes:\t{0}", Vectors.Shuffle_AcceleratedTypes);
}
}
}
3) 示例的運行結果
.NET7.0
on X86
程式: VectorTraits.Sample
VectorTraits.Sample
IsRelease: True
EnvironmentVariable(PROCESSOR_IDENTIFIER): Intel64 Family 6 Model 142 Stepping 10, GenuineIntel
Environment.ProcessorCount: 8
Environment.Is64BitProcess: True
Environment.OSVersion: Microsoft Windows NT 10.0.19045.0
Environment.Version: 7.0.3
Stopwatch.Frequency: 10000000
RuntimeEnvironment.GetRuntimeDirectory: C:\Program Files\dotnet\shared\Microsoft.NETCore.App\7.0.3\
RuntimeInformation.FrameworkDescription: .NET 7.0.3
RuntimeInformation.OSArchitecture: X64
RuntimeInformation.OSDescription: Microsoft Windows 10.0.19045
RuntimeInformation.RuntimeIdentifier: win10-x64
IntPtr.Size: 8
BitConverter.IsLittleEndian: True
Vector.IsHardwareAccelerated: True
Vector<byte>.Count: 32 # 256bit
Vector<float>.Count: 8 # 256bit
VectorTraitsGlobal.InitCheckSum: 7960959 # 0x0079797F
Vector<T>.Assembly.CodeBase: file:///C:/Program Files/dotnet/shared/Microsoft.NETCore.App/7.0.3/System.Private.CoreLib.dll
GetTargetFrameworkDisplayName(VectorTextUtil): .NET 7.0
GetTargetFrameworkDisplayName(TraitsOutput): .NET 7.0
Vectors.Instance: VectorTraits256Avx2
src: <0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7> # (0000 0001 0002 0003 0004 0005 0006 0007 0000 0001 0002 0003 0004 0005 0006 0007)
ShiftLeft: <0, 2, 4, 6, 8, 10, 12, 14, 0, 2, 4, 6, 8, 10, 12, 14> # (0000 0002 0004 0006 0008 000A 000C 000E 0000 0002 0004 0006 0008 000A 000C 000E)
Equals to BCL ShiftLeft: True
Equals to ShiftLeft_Const: True
desc: <15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0> # (000F 000E 000D 000C 000B 000A 0009 0008 0007 0006 0005 0004 0003 0002 0001 0000)
Shuffle: <14, 12, 10, 8, 6, 4, 2, 0, 14, 12, 10, 8, 6, 4, 2, 0> # (000E 000C 000A 0008 0006 0004 0002 0000 000E 000C 000A 0008 0006 0004 0002 0000)
Equals to BCL Shuffle: True
Equals to Shuffle_Core: True
ShiftLeft_AcceleratedTypes: SByte, Byte, Int16, UInt16, Int32, UInt32, Int64, UInt64 # (00001FE0)
Shuffle_AcceleratedTypes: SByte, Byte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Single, Double # (00007FE0)
註: Vectors.Instance
及之前的文本, 是TraitsOutput.OutputEnvironment
輸出的環境信息. 而從 src
開始的, 才是示例的主體代碼.
由於CPU支持X86的Avx2指令集, 於是 Vector<byte>.Count
為 32(256bit), Vectors.Instance
為 VectorTraits256Avx2
.
.NET7.0
on Arm
程式: VectorTraits.Sample
VectorTraits.Sample
IsRelease: True
EnvironmentVariable(PROCESSOR_IDENTIFIER):
Environment.ProcessorCount: 2
Environment.Is64BitProcess: True
Environment.OSVersion: Unix 5.19.0.1025
Environment.Version: 7.0.8
Stopwatch.Frequency: 1000000000
RuntimeEnvironment.GetRuntimeDirectory: /home/ubuntu/.dotnet/shared/Microsoft.NETCore.App/7.0.8/
RuntimeInformation.FrameworkDescription: .NET 7.0.8
RuntimeInformation.OSArchitecture: Arm64
RuntimeInformation.OSDescription: Linux 5.19.0-1025-aws #26~22.04.1-Ubuntu SMP Mon Apr 24 01:58:03 UTC 2023
RuntimeInformation.RuntimeIdentifier: ubuntu.22.04-arm64
IntPtr.Size: 8
BitConverter.IsLittleEndian: True
Vector.IsHardwareAccelerated: True
Vector<byte>.Count: 16 # 128bit
Vector<float>.Count: 4 # 128bit
VectorTraitsGlobal.InitCheckSum: 7960961 # 0x00797981
Vector<T>.Assembly.CodeBase: file:///home/ubuntu/.dotnet/shared/Microsoft.NETCore.App/7.0.8/System.Private.CoreLib.dll
GetTargetFrameworkDisplayName(VectorTextUtil): .NET 7.0
GetTargetFrameworkDisplayName(TraitsOutput): .NET 7.0
Vectors.Instance: VectorTraits128AdvSimdB64
src: <0, 1, 2, 3, 4, 5, 6, 7> # (0000 0001 0002 0003 0004 0005 0006 0007)
ShiftLeft: <0, 2, 4, 6, 8, 10, 12, 14> # (0000 0002 0004 0006 0008 000A 000C 000E)
Equals to BCL ShiftLeft: True
Equals to ShiftLeft_Const: True
desc: <7, 6, 5, 4, 3, 2, 1, 0> # (0007 0006 0005 0004 0003 0002 0001 0000)
Shuffle: <14, 12, 10, 8, 6, 4, 2, 0> # (000E 000C 000A 0008 0006 0004 0002 0000)
Equals to BCL Shuffle: True
Equals to Shuffle_Core: True
ShiftLeft_AcceleratedTypes: SByte, Byte, Int16, UInt16, Int32, UInt32, Int64, UInt64 # (00001FE0)
Shuffle_AcceleratedTypes: SByte, Byte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Single, Double # (00007FE0)
運算結果與X86的相同,只是環境信息不同。
由於CPU支持Arm的AdvSimd指令集, 於是 Vector<byte>.Count
為 16(128bit), Vectors.Instance
為 VectorTraits128AdvSimdB64
.
.NET Framework 4.5
on X86
程式: VectorTraits.Sample.NetFw
.
VectorTraits.Sample
IsRelease: True
EnvironmentVariable(PROCESSOR_IDENTIFIER): Intel64 Family 6 Model 142 Stepping 10, GenuineIntel
Environment.ProcessorCount: 8
Environment.Is64BitProcess: True
Environment.OSVersion: Microsoft Windows NT 6.2.9200.0
Environment.Version: 4.0.30319.42000
Stopwatch.Frequency: 10000000
RuntimeEnvironment.GetRuntimeDirectory: C:\Windows\Microsoft.NET\Framework64\v4.0.30319\
RuntimeInformation.FrameworkDescription: .NET Framework 4.8.9167.0
RuntimeInformation.OSArchitecture: X64
RuntimeInformation.OSDescription: Microsoft Windows 10.0.19045
IntPtr.Size: 8
BitConverter.IsLittleEndian: True
Vector.IsHardwareAccelerated: True
Vector<byte>.Count: 32 # 256bit
Vector<float>.Count: 8 # 256bit
VectorTraitsGlobal.InitCheckSum: -25396097 # 0xFE7C7C7F
Vector<T>.Assembly.CodeBase: file:///E:/zylSelf/Code/cs/base/VectorTraits/samples/VectorTraits.Sample.NetFw/bin/Release/System.Numerics.Vectors.DLL
GetTargetFrameworkDisplayName(VectorTextUtil): .NET Standard 1.1
GetTargetFrameworkDisplayName(TraitsOutput): .NET Framework 4.5
Vectors.Instance: VectorTraits256Base
src: <0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7> # (0000 0001 0002 0003 0004 0005 0006 0007 0000 0001 0002 0003 0004 0005 0006 0007)
ShiftLeft: <0, 2, 4, 6, 8, 10, 12, 14, 0, 2, 4, 6, 8, 10, 12, 14> # (0000 0002 0004 0006 0008 000A 000C 000E 0000 0002 0004 0006 0008 000A 000C 000E)
Equals to ShiftLeft_Const: True
desc: <15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0> # (000F 000E 000D 000C 000B 000A 0009 0008 0007 0006 0005 0004 0003 0002 0001 0000)
Shuffle: <14, 12, 10, 8, 6, 4, 2, 0, 14, 12, 10, 8, 6, 4, 2, 0> # (000E 000C 000A 0008 0006 0004 0002 0000 000E 000C 000A 0008 0006 0004 0002 0000)
Equals to Shuffle_Core: True
ShiftLeft_AcceleratedTypes: SByte, Byte, Int16, UInt16, Int32, UInt32 # (000007E0)
Shuffle_AcceleratedTypes: None # (00000000)
Vectors 的 ShiftLeft/Shuffle 都能正常工作.
由於CPU支持X86的Avx2指令集, 於是 Vector<byte>.Count
為 32(256bit). Vectors.Instance
為 VectorTraits256Base
. 它不是 VectorTraits256Avx2
, 是因為直到 .NET Core 3.0
才支持內在函數.
ShiftLeft_AcceleratedTypes的值含有“Int16”等類型,這表示ShiftLeft在使用這些類型時, 是存在硬體加速的. 本庫巧妙的利用了向量演算法, 即使在沒有內在函數時,也儘量實現了硬體加速.
基準測試結果
數據的單位: 百萬次操作/秒. 數字越大, 性能越好.
ShiftLeft
ShiftLeft: 將向量的每個元素左移指定量.
它是.NET 7.0
所新增的向量方法.
ShiftLeft - x86 - lntel Core i5-8250U
Type | Method | .NET Framework | .NET Core 2.1 | .NET Core 3.1 | .NET 5.0 | .NET 6.0 | .NET 7.0 |
---|---|---|---|---|---|---|---|
Byte | SumSLLScalar | 853.802 | 817.528 | 1104.993 | 1118.381 | 1374.255 | 1480.225 |
Byte | SumSLLNetBcl | 1128.290 | |||||
Byte | SumSLLNetBcl_Const | 1137.564 | |||||
Byte | SumSLLTraits | 8296.682 | 8114.085 | 21811.573 | 19960.732 | 21044.192 | 23074.627 |
Byte | SumSLLTraits_Core | 33328.333 | 35503.285 | 41644.146 | 35703.816 | 36615.138 | 32872.874 |
Byte | SumSLLConstTraits | 10849.899 | 10168.754 | 25029.290 | 29761.737 | 33785.502 | 32862.094 |
Byte | SumSLLConstTraits_Core | 36537.668 | 31837.586 | 39307.523 | 35698.909 | 35679.744 | 33994.997 |
Int16 | SumSLLScalar | 823.668 | 806.395 | 1176.133 | 1183.966 | 1379.498 | 1486.900 |
Int16 | SumSLLNetBcl | 18445.571 | |||||
Int16 | SumSLLNetBcl_Const | 19054.243 | |||||
Int16 | SumSLLTraits | 5076.036 | 5047.453 | 16986.361 | 16653.329 | 16496.182 | 16114.543 |
Int16 | SumSLLTraits_Core | 20318.984 | 18959.033 | 20182.655 | 17683.717 | 18500.302 | 18439.182 |
Int16 | SumSLLConstTraits | 5899.256 | 5693.084 | 16944.673 | 19378.434 | 21059.682 | 19572.551 |
Int16 | SumSLLConstTraits_Core | 20172.952 | 19339.311 | 18407.673 | 19850.711 | 21232.279 | 18136.492 |
Int32 | SumSLLScalar | 803.506 | 820.639 | 1307.614 | 1328.703 | 2199.685 | 1587.071 |
Int32 | SumSLLNetBcl | 9469.894 | |||||
Int32 | SumSLLNetBcl_Const | 10657.900 | |||||
Int32 | SumSLLTraits | 2571.456 | 2678.866 | 8246.402 | 7799.748 | 8221.382 | 9594.126 |
Int32 | SumSLLTraits_Core | 8574.361 | 8465.712 | 10320.833 | 10408.381 | 10626.910 | 10035.217 |
Int32 | SumSLLConstTraits | 1493.590 | 2922.103 | 8155.046 | 9293.148 | 10579.400 | 10185.431 |
Int32 | SumSLLConstTraits_Core | 8467.974 | 8554.920 | 9784.699 | 10384.732 | 9790.898 | 10329.112 |
Int64 | SumSLLScalar | 797.703 | 816.504 | 1295.009 | 1305.611 | 2043.527 | 1535.809 |
Int64 | SumSLLNetBcl | 4143.077 | |||||
Int64 | SumSLLNetBcl_Const | 4903.130 | |||||
Int64 | SumSLLTraits | 426.950 | 458.517 | 3867.136 | 3941.999 | 3964.762 | 3713.754 |
Int64 | SumSLLTraits_Core | 441.378 | 463.537 | 4802.911 | 4813.018 | 4776.182 | 4653.104 |
Int64 | SumSLLConstTraits | 490.135 | 536.949 | 3929.109 | 4018.072 | 4725.293 | 4712.366 |
Int64 | SumSLLConstTraits_Core | 491.263 | 531.946 | 4930.099 | 4737.462 | 4782.430 | 4371.649 |
說明:
- SumSLLScalar: 使用標量演算法.
- SumSLLNetBcl: 使用BCL的方法(
Vector.ShiftLeft
), 參數是變數. 註意.NET 7.0
才提供該方法. - SumSLLNetBcl_Const: 使用BCL的方法(
Vector.ShiftLeft
), 參數是常量. 註意.NET 7.0
才提供該方法. - SumSLLTraits: 使用本庫的普通方法(
Vectors.ShiftLeft
), 參數是變數. - SumSLLTraits_Core: 使用本庫的
Core
尾碼的方法(Vectors.ShiftLeft_Args
,Vectors.ShiftLeft_Core
), 參數是變數. - SumSLLConstTraits: 使用本庫的
Const
尾碼的方法(Vectors.ShiftLeft_Const
), 參數是常量. - SumSLLConstTraits_Core: 使用本庫的
ConstCore
尾碼的方法(Vectors.ShiftLeft_Args
,Vectors.ShiftLeft_ConstCore
), 參數是常量.
BCL的方法(Vector.ShiftLeft
) 在X86平臺運行時, 僅 Int16/Int32/Int64 有硬體加速, 而 Byte 沒有硬體加速. 這是可能是因為 Avx2 指令集僅有 16~64位 的左移位指令, 未提供其他類型的指令, BCL便轉為軟體演算法了.
而本庫對於這些數字類型, 會換成由其他指令組合實現的高效演算法. 例如對於 Byte類型, SumSLLConstTraits_Core 在.NET 7.0
的值為“32872.874”, 性能是 標量演算法的 32872.874/1480.225≈22.2080
倍, 且是BCL方法的 32872.874/1137.564≈28.8976
倍.
因為X86的內在函數是從.NET Core 3.0
開始才提供的. 故對於 Int64類型, 在 .NET Core 3.0
之後才有硬體加速.
對於ShiftLeft來說, 當參數shiftAmount
是常量時, 性能一般會比用變數時更高. 無論是 BCL還是本庫的方法, 都是如此.
使用本庫的 Core
尾碼的方法, 能將部分運算挪到迴圈外去提前處理, 從而優化了性能. 而當 CPU提供了常數參數的指令時(專業術語是“立即數參數”), 該指令的性能一般會更高. 於是本庫還提供了 ConstCore
尾碼的方法, 會選擇該平臺最快的指令.
因“CPU睿頻”、“其他進程搶占CPU資源”等因素, 有時性能波動比較大. 但請放心, 已經檢查過了Release的程式運行時的彙編指令, 它已經是按最佳硬體指令運行的. 例如下圖.
ShiftLeft - Arm - AWS Arm t4g.small
Type | Method | .NET Core 3.1 | .NET 5.0 | .NET 6.0 | .NET 7.0 |
---|---|---|---|---|---|
Byte | SumSLLScalar | 610.192 | 610.563 | 653.197 | 891.088 |
Byte | SumSLLNetBcl | 19580.464 | |||
Byte | SumSLLNetBcl_Const | 19599.073 | |||
Byte | SumSLLTraits | 5668.036 | 13252.891 | 13253.575 | 13241.598 |
Byte | SumSLLTraits_Core | 14341.895 | 15888.315 | 15887.520 | 19595.005 |
Byte | SumSLLConstTraits | 9946.663 | 13243.304 | 15895.672 | 19466.408 |
Byte | SumSLLConstTraits_Core | 13201.657 | 15896.748 | 15894.093 | 19447.318 |
Int16 | SumSLLScalar | 606.942 | 607.226 | 607.742 | 765.154 |
Int16 | SumSLLNetBcl | 9332.186 | |||
Int16 | SumSLLNetBcl_Const | 9240.256 | |||
Int16 | SumSLLTraits | 4231.310 | 6553.072 | 6603.431 | 9351.061 |
Int16 | SumSLLTraits_Core | 7881.834 | 7897.878 | 8449.502 | 9356.142 |
Int16 | SumSLLConstTraits | 6577.829 | 6620.078 | 8444.304 | 9359.246 |
Int16 | SumSLLConstTraits_Core | 8383.107 | 7923.119 | 8443.802 | 9317.663 |
Int32 | SumSLLScalar | 749.491 | 746.414 | 747.273 | 1403.533 |
Int32 | SumSLLNetBcl | 4537.804 | |||
Int32 | SumSLLNetBcl_Const | 4533.257 | |||
Int32 | SumSLLTraits | 3233.214 | 3531.441 | 3530.389 | 4545.497 |
Int32 | SumSLLTraits_Core | 3901.975 | 4140.171 | 4142.377 | 4505.555 |
Int32 | SumSLLConstTraits | 3510.471 | 3865.285 | 4134.108 | 4568.054 |
Int32 | SumSLLConstTraits_Core | 3905.829 | 3895.898 | 3896.719 | 4547.294 |
Int64 | SumSLLScalar | 743.187 | 742.685 | 743.760 | 1372.299 |
Int64 | SumSLLNetBcl | 2473.172 | |||
Int64 | SumSLLNetBcl_Const | 2468.456 | |||
Int64 | SumSLLTraits | 482.056 | 1637.232 | 1640.547 | 1981.831 |
Int64 | SumSLLTraits_Core | 488.072 | 1970.152 | 2088.793 | 2468.202 |
Int64 | SumSLLConstTraits | 467.942 | 1958.432 | 2099.095 | 2460.619 |
Int64 | SumSLLConstTraits_Core | 470.112 | 1971.898 | 2097.693 | 2465.419 |
說明:
- SumSLLScalar: 使用標量演算法.
- SumSLLNetBcl: 使用BCL的方法(
Vector.ShiftLeft
), 參數是變數. 註意.NET 7.0
才提供該方法. - SumSLLNetBcl_Const: 使用BCL的方法(
Vector.ShiftLeft
), 參數是常量. 註意.NET 7.0
才提供該方法. - SumSLLTraits: 使用本庫的普通方法(
Vectors.ShiftLeft
), 參數是變數. - SumSLLTraits_Core: 使用本庫的
Core
尾碼的方法(Vectors.ShiftLeft_Args
,Vectors.ShiftLeft_Core
), 參數是變數. - SumSLLConstTraits: 使用本庫的
Const
尾碼的方法(Vectors.ShiftLeft_Const
), 參數是常量. - SumSLLConstTraits_Core: 使用本庫的
ConstCore
尾碼的方法(Vectors.ShiftLeft_Args
,Vectors.ShiftLeft_ConstCore
), 參數是常量.
BCL的方法(Vector.ShiftLeft
) 在Arm平臺運行時, 整數類型都有硬體加速. 對於8~64位整數的左移位, AdvSimd指令集都提供了專用指令.
本庫在Arm平臺運行時, 也使用了同樣的指令. 於是性能接近.
因為從 .NET 5.0
開始, 才提供了 Arm的內在函數. 故對於 Int64類型, 在 .NET 5.0
之後才有硬體加速.
ShiftRightArithmetic
ShiftRightArithmetic: 將向量的每個有符號元素算術右移指定量.
它是.NET 7.0
所新增的向量方法.
ShiftRightArithmetic - x86 - lntel Core i5-8250U
Type | Method | .NET Framework | .NET Core 2.1 | .NET Core 3.1 | .NET 5.0 | .NET 6.0 | .NET 7.0 |
---|---|---|---|---|---|---|---|
Int16 | SumSRAScalar | 823.804 | 827.734 | 1180.933 | 1182.307 | 1341.171 | 1592.939 |
Int16 | SumSRANetBcl | 18480.038 | |||||
Int16 | SumSRANetBcl_Const | 21052.686 | |||||
Int16 | SumSRATraits | 1557.132 | 1559.674 | 17325.184 | 17699.944 | 16372.799 | 17193.661 |
Int16 | SumSRATraits_Core | 1653.816 | 1653.714 | 18414.632 | 19664.147 | 17938.068 | 18476.248 |
Int16 | SumSRAConstTraits | 1672.258 | 1675.044 | 17658.703 | 20409.889 | 20233.738 | 20835.294 |
Int16 | SumSRAConstTraits_Core | 1714.582 | 1667.090 | 20076.043 | 20212.774 | 20994.717 | 21053.837 |
Int32 | SumSRAScalar | 825.056 | 829.789 | 1275.799 | 1342.349 | 1621.295 | 1620.315 |
Int32 | SumSRANetBcl | 10132.774 | |||||
Int32 | SumSRANetBcl_Const | 11033.258 | |||||
Int32 | SumSRATraits | 764.013 | 759.588 | 8195.470 | 8298.404 | 8314.921 | 9937.082 |
Int32 | SumSRATraits_Core | 826.612 | 825.854 | 10576.367 | 10449.535 | 9783.716 | 11108.074 |
Int32 | SumSRAConstTraits | 837.650 | 834.126 | 8484.959 | 9238.089 | 9979.236 | 10053.944 |
Int32 | SumSRAConstTraits_Core | 856.397 | 859.426 | 10201.125 | 10314.334 | 11009.384 | 10772.948 |
Int64 | SumSRAScalar | 815.238 | 811.645 | 1300.052 | 1280.982 | 1322.441 | 1602.916 |
Int64 | SumSRANetBcl | 578.499 | |||||
Int64 | SumSRANetBcl_Const | 553.963 | |||||
Int64 | SumSRATraits | 447.196 | 441.690 | 3032.903 | 2830.935 | 2988.130 | 2922.851 |
Int64 | SumSRATraits_Core | 459.781 | 458.269 | 3639.092 | 3352.255 | 3336.974 | 3488.018 |
Int64 | SumSRAConstTraits | 491.449 | 491.420 | 3074.926 | 2820.864 | 3365.642 | 3397.660 |
Int64 | SumSRAConstTraits_Core | 496.174 | 491.022 | 3660.380 | 3365.210 | 3398.657 | 3237.150 |
SByte | SumSRAScalar | 827.231 | 823.643 | 1101.518 | 1105.244 | 1348.340 | 1619.984 |
SByte | SumSRANetBcl | 1161.428 | |||||
SByte | SumSRANetBcl_Const | 1156.552 | |||||
SByte | SumSRATraits | 3108.569 | 3100.703 | 17944.555 | 17103.399 | 17926.975 | 20115.939 |
SByte | SumSRATraits_Core | 3298.491 | 3288.742 | 30742.095 | 30212.469 | 29604.498 | 33040.654 |
SByte | SumSRAConstTraits | 3320.813 | 3327.910 | 18297.669 | 25989.446 | 28437.425 | 31118.235 |
SByte | SumSRAConstTraits_Core | 3423.868 | 3427.681 | 29454.032 | 27559.316 | 30075.338 | 30565.076 |
說明:
- SumSRAScalar: 使用標量演算法.
- SumSRANetBcl: 使用BCL的方法(
Vector.ShiftRightArithmetic
), 參數是變數. 註意.NET 7.0
才提供該方法. - SumSRANetBcl_Const: 使用BCL的方法(
Vector.ShiftRightArithmetic
), 參數是常量. 註意.NET 7.0
才提供該方法. - SumSRATraits: 使用本庫的普通方法(
Vectors.ShiftRightArithmetic
), 參數是變數. - SumSRATraits_Core: 使用本庫的
Core
尾碼的方法(Vectors.ShiftRightArithmetic_Args
,Vectors.ShiftRightArithmetic_Core
), 參數是變數. - SumSRAConstTraits: 使用本庫的
Const
尾碼的方法(Vectors.ShiftRightArithmetic_Const
), 參數是常量. - SumSRAConstTraits_Core: 使用本庫的
ConstCore
尾碼的方法(Vectors.ShiftRightArithmetic_Args
,Vectors.ShiftRightArithmetic_ConstCore
), 參數是常量.
BCL的方法(Vector.ShiftRightArithmetic
) 在X86平臺運行時, 僅 Int16/Int32 有硬體加速, 而 SByte/Int64 沒有硬體加速. 這是可能是因為 Avx2 指令集僅有 16~32位 的算術右移位指令.
而本庫對於這些數字類型, 會換成由其他指令組合實現的高效演算法. 從 .NET Core 3.0
開始, 具有硬體加速.