我的CPU是AMD Ryzen 7 7840H,支持AVX-512指令集。当我运行.NET8程序时,
Vector512.IsHardwareAccelerated
的值为true。但System.Numerics.Vector<T>
仍然是256位,并没有达到512位。
为什么Vector<T>
类型的长度没有达到512位?目前是否不支持,或者我需要调整配置吗?
示例代码:
TextWriter writer = Console.Out;
writer.WriteLine(string.Format("Vector512.IsHardwareAccelerated:\t{0}", Vector512.IsHardwareAccelerated));
writer.WriteLine(string.Format("Vector.IsHardwareAccelerated:\t{0}", Vector.IsHardwareAccelerated));
writer.WriteLine(string.Format("Vector<byte>.Count:\t{0}\t# {1}bit", Vector<byte>.Count, Vector<byte>.Count * 8));
测试结果:
Vector512.IsHardwareAccelerated: True
Vector.IsHardwareAccelerated: True
Vector<byte>.Count: 32 # 256bit
参见 https://github.com/dotnet/runtime/issues/92189 - 出于与 C 编译器在自动向量化大循环时默认为
-mprefer-vector-width=256
相同的硬件原因,C# 不会自动使用所有向量化代码512 位,即使可用。
此外,对于小问题,例如9 个浮点数,这可能意味着不会发生矢量化迭代,只是标量后备代码。
此外,显然某些代码库(希望是无意的)依赖于 Vector 的宽度不超过 32 字节,因此这对这些代码库来说将是一个重大更改。
@stephentoub 写道: 在 .NET 8 中,可变宽度
不会自动支持大于 256 位的宽度。 在 .NET 9 中您可能可以选择加入,但目前目前尚不清楚是否会默认启用,Vector<T>
我在 dotnet github 问题上发表了评论,其中包含一些有关 CPU 硬件原因的详细信息;我将在这里重现其中的一些内容:
-mprefer-vector-width=512
相比 256
降低了 1%。但同样,这是标量代码的 LLVM 自动矢量化,与 C# 不同,这只会影响手动矢量化循环,因此调整考虑因素与 -mprefer-vector-width=256
有所不同。在频繁唤醒短突发计算的程序中,使用 AVX-512 仍会降低核心的睿频频率,从而影响其他程序。
Zen 4 上情况有所不同;它们通过在执行单元中占用额外的周期来处理 512 位向量,因此只要 512 位向量不需要更多的洗牌工作或其他会增加开销的效果,512 位向量对于前端来说是一个很好的胜利最终吞吐量以及乱序执行器在元素或标量迭代方面可以看到的领先程度。 (因为 512 位 uop 对于前端来说仍然只有 1 uop。)GCC 和 Clang 默认为
-mprefer-vector-width=512
表示 -march=znver4
。
Zen 4 上的 512 位向量没有涡轮惩罚或其他固有缺点(AFAIK;我不知道未对齐的负载如何执行)。这只是软件是否可以有效地使用它们的问题(不需要更多臃肿的循环序言/尾声代码,例如,如果屏蔽的最终迭代不起作用,则进行标量清理。)AVX-512 屏蔽存储在 Zen 4 上是高效的,尽管事实上 AVX1/2
vmaskmovps
/ vpmaskmovd
不是。 (https://uops.info/)
对于恰好有 32 字节内容的代码,如果 32 字节向量不再是一个选项,那么那就是一种损失。 C# 的可扩展向量长度模型对于这些情况并不理想。 ARM SVE 或 RISC-V 矢量扩展,其中硬件 ISA 围绕可变矢量长度设计,并带有掩码来处理比硬件本机长度短的矢量,但对 C#
Vector<>
做同样的事情可能效果不佳,因为很多硬件(带 AVX2 的 x86 或不带 SVE 的 AArch64)无法有效支持任意长度内容的屏蔽。
我在 github 问题上写了更多有关英特尔的内容,我不会在这里复制/粘贴所有内容。
对于 Intel CPU 上的某些工作负载,512 位向量可以显着提高总体吞吐量。但它也有缺点,比如更昂贵的未对齐内存访问。