作者: zyl910
發(fā)現(xiàn).NET8增加了Arm架構(gòu)的多寄存器的查表函數(shù)(VectorTableLookup/VectorTableLookupExtension),這給編寫SIMD向量化算法帶來了方便。
一、指令說明
在學(xué)習(xí)Arm的AdvSimd(Neon)指令集時(shí),發(fā)現(xiàn)它的Lookup(查表)功能,類似X86的Sse系列指令集中的字節(jié)Shuffle(換位。如 _mm_shuffle_epi8 )功能。
而且Arm的Lookup不僅支持單個(gè)向量的查表,且支持多個(gè)向量的查表。具體來說,是2~4個(gè)向量。
單個(gè)向量查表(如 vqvtbl1q_u8)時(shí),只能在 16字節(jié)(128位)的范圍內(nèi)進(jìn)行查表。而使用4個(gè)向量查表(如 vqtbl4q_u8 )時(shí),能在 16*4=64字節(jié)(512位)的范圍內(nèi)進(jìn)行查表。
.NET 5.0開始支持Arm的內(nèi)在函數(shù),但當(dāng)時(shí)僅支持單個(gè)向量查表。
現(xiàn)在 .NET 8.0 補(bǔ)上了這個(gè)空缺。
二、API文檔的變化
對(duì)于AdvSimd.Arm64.VectorTableLookup
方法,.NET 5.0 的文檔是只有2個(gè)重載。
VectorTableLookup(Vector128<SByte>, Vector128<SByte>) // int8x16_t vqvtbl1q_s8(int8x16_t t, uint8x16_t idx)
VectorTableLookup(Vector128<Byte>, Vector128<Byte>) // uint8x16_t vqvtbl1q_u8(uint8x16_t t, uint8x16_t idx)
到了.NET 8.0 ,文檔多了6個(gè)重載。
VectorTableLookup(ValueTuple<Vector128<Byte>,Vector128<Byte>,Vector128<Byte>,Vector128<Byte>>, Vector128<Byte>) // uint8x16_t vqtbl4q_u8 (uint8x16x4_t t、uint8x16_t idx)
VectorTableLookup(ValueTuple<Vector128<Byte>,Vector128<Byte>,Vector128<Byte>>, Vector128<Byte>) // uint8x16_t vqtbl3q_u8 (uint8x16x3_t t、uint8x16_t idx)
VectorTableLookup(ValueTuple<Vector128<Byte>,Vector128<Byte>>, Vector128<Byte>) // uint8x16_t vqtbl2q_u8 (uint8x16x2_t t、uint8x16_t idx)
VectorTableLookup(ValueTuple<Vector128<SByte>,Vector128<SByte>,Vector128<SByte>,Vector128<SByte>>, Vector128<SByte>) // int8x16_t vqtbl4q_s8 (int8x16x4_t t、uint8x16_t idx)
VectorTableLookup(ValueTuple<Vector128<SByte>,Vector128<SByte>,Vector128<SByte>>, Vector128<SByte>) // int8x16_t vqtbl3q_s8 (int8x16x3_t t、uint8x16_t idx)
VectorTableLookup(ValueTuple<Vector128<SByte>,Vector128<SByte>>, Vector128<SByte>) // int8x16_t vqtbl2q_s8 (int8x16x2_t t、uint8x16_t idx)
可見,2、3、4個(gè)向量的查表功能都加上了了。隨后再區(qū)分一下 Byte/SByte 這2種類型,于是共增加了 3*2=6 個(gè)重載。
三、官方說明
查了一下,發(fā)現(xiàn)在官方博文《Arm64 Performance Improvements in .NET 8》(.NET 8 中的 Arm64 性能改進(jìn))里有說明。
這一段內(nèi)容的機(jī)器翻譯如下。
VectorTableLookup 和 VectorTableLookupExtension
在 .NET 8 中,我們?cè)赟ystem.Runtime.Intrinsics.Arm命名空間下添加了兩組新的 API:VectorTableLookup和 VectorTableLookupExtension。
public static Vector64<byte> VectorTableLookup((Vector128<byte>, Vector128<byte>) table, Vector64<byte> byteIndexes);
public static Vector64<byte> VectorTableLookup(Vector64<byte> defaultValues, (Vector128<byte>, Vector128<byte>) table, Vector64<byte> byteIndexes);
讓我們看一下每個(gè) API 的示例。
// Vector128<byte> a = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16
// Vector128<byte> b = 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160
// Vector64<byte> index = 3, 31, 4, 40, 18, 19, 30, 1
Vector64<byte> ans = VectorTableLookup((a, b), index);
// ans = 4, 160, 5, 0, 30, 40, 150, 2
在上面的示例中,向量 a 和 b 被視為一個(gè)表,共有 32 個(gè)條目(16 個(gè)來自 a,16 個(gè)來自 b),索引從 0 開始。如果索引超出范圍,例如在我們的示例中試圖訪問索引 40,API 將返回該超出范圍索引的值 0。
// Vector64<byte> d = 100, 200, 300, 400, 500, 600, 700, 800
// Vector128<byte> a = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16
// Vector128<byte> b = 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160
// Vector64<byte> index = 3, 31, 4, 40, 18, 19, 30, 1
Vector64<byte> ans = VectorTableLookupExtension(d, (a, b), index);
// ans = 4, 160, 5, 400, 30, 40, 150, 2
與 VectorTableLookup相反,當(dāng)使用VectorTableLookupExtension方法時(shí),如果索引超出有效范圍,則結(jié)果中的相應(yīng)元素將由參數(shù)中提供的defaultValues值確定。值得注意的是,這些 API 還有其他變體,它們也在 3 實(shí)體和 4 實(shí)體元組上運(yùn)行,為各種用例提供了靈活性。
在 dotnet/runtime#85189 中,@MihaZupan 利用此 API 優(yōu)化了 IndexOfAny,顯著提高了 30% 的性能。同樣,在 dotnet/runtime#87126 中,@SwapnilGaikwad 顯著增強(qiáng)了 Guid 格式化器的性能,實(shí)現(xiàn)了高達(dá) 40% 的性能提升。這些優(yōu)化表明,利用這一強(qiáng)大的 API 可以大幅提高性能。
四、X86平臺(tái)的對(duì)應(yīng)
X86的Sse、Avx系列指令集,僅支持單個(gè)向量查表。
直到Avx512系列指令集的出現(xiàn),它增加了2個(gè)向量查表的指令 VPERMI2B
。
.NET8.0也增加了對(duì)Avx512系列指令集的支持,便支持了該指令。
對(duì)于512位向量,可以使用 Avx512Vbmi
類中的方法。文章來源:http://www.zghlxwxcb.cn/news/detail-842833.html
PermuteVar64x8x2(Vector512<Byte>, Vector512<Byte>, Vector512<Byte>) // __m512i _mm512_permutex2var_epi8 (__m512i a, __m512i idx, __m512i b)
PermuteVar64x8x2(Vector512<SByte>, Vector512<SByte>, Vector512<SByte>) // __m512i _mm512_permutex2var_epi8 (__m512i a, __m512i idx, __m512i b)
對(duì)于128、256位向量,可以使用 Avx512Vbmi.VL
類中的方法。文章來源地址http://www.zghlxwxcb.cn/news/detail-842833.html
PermuteVar16x8x2(Vector128<Byte>, Vector128<Byte>, Vector128<Byte>) // __m128i _mm_permutex2var_epi8 (__m128i a,__m128i idx,__m128i b)
PermuteVar16x8x2(Vector128<SByte>, Vector128<SByte>, Vector128<SByte>) // __m128i _mm_permutex2var_epi8 (__m128i a,__m128i idx,__m128i b)
PermuteVar32x8x2(Vector256<Byte>, Vector256<Byte>, Vector256<Byte>) // __m256i _mm256_permutex2var_epi8 (__m256i a, __m256i idx, __m256i b)
PermuteVar32x8x2(Vector256<SByte>, Vector256<SByte>, Vector256<SByte>) // __m256i _mm256_permutex2var_epi8 (__m256i a, __m256i idx, __m256i b)
參考文獻(xiàn)
- 《AdvSimd.Arm64.VectorTableLookup 方法》. https://learn.microsoft.com/zh-cn/dotnet/api/system.runtime.intrinsics.arm.advsimd.arm64.vectortablelookup?view=net-8.0
- 《Avx512Vbmi.PermuteVar64x8x2 方法》. https://learn.microsoft.com/zh-cn/dotnet/api/system.runtime.intrinsics.x86.avx512vbmi.permutevar64x8x2?view=net-8.0
- 《Avx512Vbmi.VL 類》. https://learn.microsoft.com/zh-cn/dotnet/api/system.runtime.intrinsics.x86.avx512vbmi.vl?view=net-8.0
- Kunal Pathak《Arm64 Performance Improvements in .NET 8》. https://devblogs.microsoft.com/dotnet/this-arm64-performance-in-dotnet-8/
到了這里,關(guān)于[C#] .NET8增加了Arm架構(gòu)的多寄存器的查表函數(shù)(VectorTableLookup/VectorTableLookupExtension)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!