在通用Windows平台中使用Vector <T>进行SIMD

Nic*_*uer 9 .net assembly simd .net-native uwp

我正在尝试使用System.Numerics.Vector(T)来矢量化算法并利用CPU的SIMD操作.但是,我的矢量实现比我原来的实现要慢得多.有没有使用可能没有记录的Vector的技巧?这里的具体用途是尝试加速数据的kb.

不幸的是,我在其上找到的几乎所有文档都基于RyuJIT的预发布版本,我不知道有多少这些材料可以移植到.NET Native.

当我在Vector xor操作期间检查反汇编时,它显示:

00007FFB040A9C10  xor         eax,eax  
00007FFB040A9C12  mov         qword ptr [rcx],rax  
00007FFB040A9C15  mov         qword ptr [rcx+8],rax  
00007FFB040A9C19  mov         rax,qword ptr [r8]  
00007FFB040A9C1C  xor         rax,qword ptr [rdx]  
00007FFB040A9C1F  mov         qword ptr [rcx],rax  
00007FFB040A9C22  mov         rax,qword ptr [r8+8]  
00007FFB040A9C26  xor         rax,qword ptr [rdx+8]  
00007FFB040A9C2A  mov         qword ptr [rcx+8],rax  
00007FFB040A9C2E  mov         rax,rcx  
Run Code Online (Sandbox Code Playgroud)

为什么不使用xmm寄存器和SIMD指令呢?同样奇怪的是,SIMD指令是针对此代码的一个版本生成的,我没有明确地向量化,但它们从未被执行,有利于常规寄存器和指令.

我确保我在Release,x64,Optimize代码启用的情况下运行.我看到了x86编译的类似行为.我在机器级别的东西上有点新手,所以它可能只是在这里发生了一些我不能正确理解的东西.

Framework版本为4.6,Vector.IsHardwareAccelerated在运行时为false.

更新: "使用.NET Native工具链编译"是罪魁祸首.启用它会导致Vector.IsHardwareAccelerated == false; 禁用它会导致Vector.IsHardwareAccelerated == true.我已经确认,当禁用.NET Native时,编译器会使用ymm寄存器生成AVX指令.这导致了一个问题......为什么在.NET Native中没有启用SIMD?有什么办法可以改变吗?

更新Tangent:我发现自动SSE矢量化数组代码未被执行的原因是因为编译器插入了一条指令,该指令查看数组的起始位置是否低于最后一个元素之一的地址数组,如果是,只使用普通寄存器.我认为这必定是编译器中的一个错误,因为按照惯例,数组的开头应始终位于比其最后一个元素低的地址.它是测试每个操作数数组的内存地址的一组指令的一部分,我想确保它们不重叠.我已为此提交了Microsoft Connect错误报告:https://connect.microsoft.com/VisualStudio/feedback/details/1831117

Nic*_*uer 9

我联系了微软,微软发布了针对.Net Native的问题和疑虑的联系地址:https://msdn.microsoft.com/en-us/vstudio/dotnetnative.aspx

我的问题被提交给微软代码生成和优化技术团队的首席软件工程经理Ian Bearman:

目前,.NET Native不会优化System.Numerics库并依赖于默认的库实现.这可能(读取:很可能)导致使用System.Numerics编写的代码在.NET Native中执行效果不如在其他CLR实现中执行.

虽然这很不幸,但.NET Native确实支持使用上面提到的C++优化所带来的自动向量化.当前发布的.NET Native编译器在其x86上的自动矢量化和ARM上的x64和NEON ISA上支持SSE2 ISA.

他还提到,他们希望从C++编译器中带来生成所有向量指令(AVX,SSE等)和基于运行时检测指令集的分支的能力.

然后他建议如果使用指令非常关键,那么组件可以用C++构建,它可以访问编译器内在函数(可能是这种分支能力?),然后很容易与剩余的C#应用​​程序连接.

至于跳过的SSE2指令,我需要做的就是将它编译成正确的指令,就是用"a ^ = b"替换循环的"a = a ^ b".因为它们应该是等效的表达式,所以它似乎是一个bug,但幸运的是它有一个解决方法.