在通用 Windows 平台中为 SIMD 使用 Vector<T>
Using Vector<T> for SIMD in Universal Windows Platform
我正在尝试使用 System.Numerics.Vector(T) 来矢量化算法并利用 CPU 的 SIMD 操作。然而,我的向量实现比我原来的实现要慢得多。使用可能没有记录的向量有什么技巧吗?这里的具体使用是尝试加速kb数据的异或。
不幸的是,我能找到的几乎所有文档都是基于 RyuJIT 的预发布版本,我不知道其中有多少 material 可移植到 .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 指令是为我没有明确矢量化的代码版本生成的,但它们从未被执行,有利于常规寄存器和指令。
我确保我是 运行 版本,x64,启用优化代码。我在 x86 编译中看到了类似的行为。我在机器级别的东西上有点新手,所以可能这里发生了一些我没有正确理解的事情。
框架版本为4.6,Vector.IsHardwareAccelerated运行时为false。
更新: "Compile with .NET Native tool chain" 是罪魁祸首。启用它会导致 Vector.IsHardwareAccelerated == false;禁用它会导致 Vector.IsHardwareAccelerated == true。我已经确认,当禁用 .NET Native 时,编译器正在使用 ymm 寄存器生成 AVX 指令。这就引出了一个问题……为什么 .NET Native 中没有启用 SIMD?有什么办法可以改变吗?
更新切线: 我发现自动 SSE 向量化数组代码未被执行的原因是因为编译器插入了一条指令,该指令查看是否数组的开头地址低于数组的最后一个元素之一,如果是,则只使用普通寄存器。我认为这一定是编译器中的一个错误,因为按照惯例,数组的开头应该始终位于比最后一个元素更低的地址。它是测试每个操作数数组的内存地址的一组指令的一部分,我想确保它们不重叠。我为此提交了 Microsoft Connect 错误报告:https://connect.microsoft.com/VisualStudio/feedback/details/1831117
我联系了 Microsoft,后者发布了有关 .Net Native 问题和疑虑的联系地址:https://msdn.microsoft.com/en-us/vstudio/dotnetnative.aspx
我的问题已提交给 Microsoft 代码生成和优化技术团队的首席软件工程经理 Ian Bearman:
Currently .NET Native does not optimize the System.Numerics library
and relies on the default library implementation. This may (read: will
likely) result in code written using System.Numerics to not perform as
well in .NET Native as it will against other CLR implementations.
While this is unfortunate, .NET Native does support auto-vectorization
which comes with using the C++ optimizations mentioned above. The
current shipping .NET Native compiler supports SSE2 ISA in its
auto-vectorization on x86 and x64 and NEON ISA on ARM.
他还提到他们希望从 C++ 编译器中引入生成所有矢量指令(AVX、SSE 等)和基于运行时指令集检测的分支的能力。
然后他建议,如果指令的使用真的很关键,可以用 C++ 构建组件,它可以访问编译器内部函数(大概还有这种分支能力?),然后轻松连接到其余的 C# 应用程序。
至于跳过的 SSE2 指令,要使其编译为正确的指令,我需要做的就是用 "a ^= b" 替换循环的 "a = a ^ b"。由于它们应该是等价的表达式,所以看起来这是一个错误,但幸运的是有一个解决方法。
我正在尝试使用 System.Numerics.Vector(T) 来矢量化算法并利用 CPU 的 SIMD 操作。然而,我的向量实现比我原来的实现要慢得多。使用可能没有记录的向量有什么技巧吗?这里的具体使用是尝试加速kb数据的异或。
不幸的是,我能找到的几乎所有文档都是基于 RyuJIT 的预发布版本,我不知道其中有多少 material 可移植到 .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 指令是为我没有明确矢量化的代码版本生成的,但它们从未被执行,有利于常规寄存器和指令。
我确保我是 运行 版本,x64,启用优化代码。我在 x86 编译中看到了类似的行为。我在机器级别的东西上有点新手,所以可能这里发生了一些我没有正确理解的事情。
框架版本为4.6,Vector.IsHardwareAccelerated运行时为false。
更新: "Compile with .NET Native tool chain" 是罪魁祸首。启用它会导致 Vector.IsHardwareAccelerated == false;禁用它会导致 Vector.IsHardwareAccelerated == true。我已经确认,当禁用 .NET Native 时,编译器正在使用 ymm 寄存器生成 AVX 指令。这就引出了一个问题……为什么 .NET Native 中没有启用 SIMD?有什么办法可以改变吗?
更新切线: 我发现自动 SSE 向量化数组代码未被执行的原因是因为编译器插入了一条指令,该指令查看是否数组的开头地址低于数组的最后一个元素之一,如果是,则只使用普通寄存器。我认为这一定是编译器中的一个错误,因为按照惯例,数组的开头应该始终位于比最后一个元素更低的地址。它是测试每个操作数数组的内存地址的一组指令的一部分,我想确保它们不重叠。我为此提交了 Microsoft Connect 错误报告:https://connect.microsoft.com/VisualStudio/feedback/details/1831117
我联系了 Microsoft,后者发布了有关 .Net Native 问题和疑虑的联系地址:https://msdn.microsoft.com/en-us/vstudio/dotnetnative.aspx
我的问题已提交给 Microsoft 代码生成和优化技术团队的首席软件工程经理 Ian Bearman:
Currently .NET Native does not optimize the System.Numerics library and relies on the default library implementation. This may (read: will likely) result in code written using System.Numerics to not perform as well in .NET Native as it will against other CLR implementations.
While this is unfortunate, .NET Native does support auto-vectorization which comes with using the C++ optimizations mentioned above. The current shipping .NET Native compiler supports SSE2 ISA in its auto-vectorization on x86 and x64 and NEON ISA on ARM.
他还提到他们希望从 C++ 编译器中引入生成所有矢量指令(AVX、SSE 等)和基于运行时指令集检测的分支的能力。
然后他建议,如果指令的使用真的很关键,可以用 C++ 构建组件,它可以访问编译器内部函数(大概还有这种分支能力?),然后轻松连接到其余的 C# 应用程序。
至于跳过的 SSE2 指令,要使其编译为正确的指令,我需要做的就是用 "a ^= b" 替换循环的 "a = a ^ b"。由于它们应该是等价的表达式,所以看起来这是一个错误,但幸运的是有一个解决方法。