在 .NET Framework 4.6 中使用 C# 的 SIMD 操作速度较慢
Posted
技术标签:
【中文标题】在 .NET Framework 4.6 中使用 C# 的 SIMD 操作速度较慢【英文标题】:using SIMD operation from C# in .NET framework 4.6 is slower 【发布时间】:2015-08-13 22:30:03 【问题描述】:我目前正在尝试仅使用 C# 计算一个巨大数组中所有值的总和,并使用 SIMD 来比较性能,而 SIMD 版本的速度要慢得多。请参阅下面的代码 sn-ps,如果我遗漏了什么,请告诉我。 “vals”是从图像文件中读取的巨大数组,并省略了该部分以保持精简。
var watch1 = new Stopwatch();
watch1.Start();
var total = vals.Aggregate(0, (a, i) => a + i);
watch1.Stop();
Console.WriteLine(string.Format("Total is: 0", total));
Console.WriteLine(string.Format("Time taken: 0", watch1.ElapsedMilliseconds));
var watch2 = new Stopwatch();
watch2.Start();
var sTotal = GetSIMDVectors(vals).Aggregate((a, i) => a + i);
int sum = 0;
for (int i = 0; i < Vector<int>.Count; i++)
sum += sTotal[i];
watch2.Stop();
Console.WriteLine(string.Format("Another Total is: 0", sum));
Console.WriteLine(string.Format("Time taken: 0", watch2.ElapsedMilliseconds));
和 GetSIMDVectors 方法
private static IEnumerable<Vector<int>> GetSIMDVectors(short[] source)
int vecCount = Vector<int>.Count;
int i = 0;
int len = source.Length;
for(i = 0; i + vecCount < len; i = i + vecCount)
var items = new int[vecCount];
for (int k = 0; k < vecCount; k++)
items[k] = source[i + k];
yield return new Vector<int>(items);
var remaining = new int[vecCount];
for (int j = i, k =0; j < len; j++, k++)
remaining[k] = source[j];
yield return new Vector<int>(remaining);
【问题讨论】:
您是否在(即发布模式)和 64 位(确保您没有使用任何具有首选 32 位的 CPU)上进行优化?您可以通过Vector.IsHardwareAccelerated 验证是否正在使用 SIMD。 就是这样。没有在 64 位模式下运行。 mike,如果你发布一个答案,我可以标记它 【参考方案1】:正如@mike z 所指出的,您需要确保您处于发布模式并针对 64 位,否则支持 SIMD 的编译器 RuyJIT 将无法工作(目前仅支持 64 位架构)。 此外,在执行前检查始终是一个很好的做法:
Vector.IsHardwareAccelerated;
此外,在创建向量之前,您无需先使用 for 循环创建数组。您只需使用 vector<int>(int[] array,int index)
构造函数从原始源数组创建向量。
yield return new Vector<int>(source, i);
而不是
var items = new int[vecCount];
for (int k = 0; k < vecCount; k++)
items[k] = source[i + k];
yield return new Vector<int>(items);
通过这种方式,我设法使用随机生成的大型数组将性能提高了近 3.7 倍。
此外,如果您要更改您的方法,使用一个在获得new Vector<int>(source, i)
的值后立即计算总和的方法,如下所示:
private static int GetSIMDVectorsSum(int[] source)
int vecCount = Vector<int>.Count;
int i = 0;
int end_state = source.Length;
Vector<int> temp = Vector<int>.Zero;
for (; i < end_state; i += vecCount)
temp += new Vector<int>(source, i);
return Vector.Dot<int>(temp, Vector<int>.One);
这里的性能提升更为显着。在我的测试中,我设法使性能比 vals.Aggregate(0, (a, i) => a + i)
提高了 16 倍。
但是,从理论的角度来看,如果例如 Vector<int>.Count
returns 4,那么任何高于 4x 的性能提升都表明您将矢量化版本与相对未优化的代码进行比较。
在您的情况下,这将是 vals.Aggregate(0, (a, i) => a + i)
部分。所以基本上,这里有很大的优化空间。
当我用一个微不足道的 for 循环替换它时
private static int no_vec_sum(int[] vals)
int end = vals.Length;
int temp = 0;
for (int i = 0; i < end; i++)
temp += vals[i];
return temp;
我的性能只提高了 1.5 倍。不过,考虑到操作的简单性,对于这种非常特殊的情况,这仍然是一个改进。
不用说,矢量化版本需要大型数组来克服在每次迭代中创建new Vector<int>()
引起的开销。
【讨论】:
以上是关于在 .NET Framework 4.6 中使用 C# 的 SIMD 操作速度较慢的主要内容,如果未能解决你的问题,请参考以下文章
如何将.net framework 4.5升级到.net框架4.6?
如何在新的 .NET Framework 4.6 中启用 SIMD?
发布在 C# .net framework 4.6 中开发的 Web API 的命令 [重复]
win10安装net framework4.0报net framework4已是此操作系统的一部分,删除4.5和4.6重启安装还是报这个错