在通用 Windows 平台中将 Vector<T> 用于 SIMD

Posted

技术标签:

【中文标题】在通用 Windows 平台中将 Vector<T> 用于 SIMD【英文标题】:Using Vector<T> for SIMD in Universal Windows Platform 【发布时间】:2015-12-17 12:46:43 【问题描述】:

我正在尝试使用 System.Numerics.Vector(T) 对算法进行矢量化并利用 CPU 的 SIMD 操作。但是,我的向量实现比我的原始实现慢得多。使用可能没有记录的向量有什么技巧吗?这里的具体用途是尝试加速 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  

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

我确保我运行时启用了 Release、x64、Optimize code。我在 x86 编译中看到了类似的行为。我对机器级的东西有点新手,所以可能只是这里发生了一些我没有正确理解的事情。

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

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

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

【问题讨论】:

这是什么框架版本?硬件加速是否报告为true 框架版本 4.6,IsHardwareAccelerated 返回 false。 why is SIMD not enabled in .NET Native? 我只能大胆猜测:SIMD 由 JIT(即时编译器,在运行时将 IL 代码转换为本机代码的东西)处理。 .NET 本机通过创建纯本机程序集(无需翻译)完全绕过 JIT。我猜他们根本没有在.NET 本机工具链中实现 SIMD 支持。要么是因为他们还没有时间,要么是因为 .NET 本机可用于创建在没有 SIMD 寄存器的 CPU 上运行的程序 KooKiz,这有点道理,但 .NET Native 确实在一定程度上使用了 SSE 寄存器和指令,而且我知道有一种方法可以评估 CPU 是否具有指示与否。所以我只能假设这是由于前者。但是,有一篇博文同时吹捧通用 Windows 应用程序中的 .NET Native 和 SIMD,建议现在两者都应该是可能的:blogs.msdn.com/b/dotnet/archive/2015/07/30/… System.Numerics.Vector.dll 版本 4.1.0.0 中的 Vector 需要支持 AVX 指令集的处理器。至少英特尔 Sandy Bridge 或 AMD Bulldozer。这是像 .NET Native 这样的提前编译器的问题,不能保证目标机器有这样的处理器。 【参考方案1】:

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

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

目前 .NET Native 没有优化 System.Numerics 库 并依赖于默认库实现。这可能(阅读:将 可能)导致使用 System.Numerics 编写的代码无法执行 在 .NET Native 中表现良好,因为它会针对其他 CLR 实现。

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

他还提到,他们希望从 C++ 编译器中引入生成所有向量指令(AVX、SSE 等)的能力,并在运行时基于对指令集的检测进行分支。

然后他建议,如果指令的使用真的很关键,可以用 C++ 构建组件,它可以访问编译器内在函数(并且可能还有这种分支能力?),然后轻松地与剩余的 C# 应用程序接口。

至于跳过的 SSE2 指令,我需要做的就是将循环的“a = a ^ b”替换为“a ^ = b”。由于它们应该是等价的表达式,看来这是一个错误,但幸运的是有一个解决方法。

【讨论】:

非常有趣/有用的信息,感谢您的关注! 感谢您回来。这不是我想要的,但仍然令人着迷。感谢 Kudo 为我们其他人跟进此事。

以上是关于在通用 Windows 平台中将 Vector<T> 用于 SIMD的主要内容,如果未能解决你的问题,请参考以下文章

详解 UWP (通用 Windows 平台) 中的两种 HttpClient API

Android 逆向Android 逆向通用工具开发 ( Windows 平台静态库程序类型 | 编译逆向工具依赖的 Windows 平台静态库程序 )

如何在通用 Windows 平台地图控件上设置 LocationRectangle?

我的通用 Windows 平台软件是不是能够获得启动关机所需的权限?

在通用 Windows 平台 C# 中使用 DeviceWatcher 填充 ComboBox

具有通用 Windows 10 平台的 Cordova 应用程序无法发送分析日志