【发布时间】:2018-12-15 22:14:00
【问题描述】:
我正在优化一种算法,并且正在考虑将 Vector over double 用于乘法和累加操作。最接近的实现显然是 Vector.dot(v1, v2);... 但是,为什么我的代码这么慢?
namespace ConsoleApp1 {
class Program {
public static double SIMDMultAccumulate(double[] inp1, double[] inp2) {
var simdLength = Vector<double>.Count;
var returnDouble = 0d;
// Find the max and min for each of Vector<ushort>.Count sub-arrays
var i = 0;
for (; i <= inp1.Length - simdLength; i += simdLength) {
var va = new Vector<double>(inp1, i);
var vb = new Vector<double>(inp2, i);
returnDouble += Vector.Dot(va, vb);
}
// Process any remaining elements
for (; i < inp1.Length; ++i) {
var va = new Vector<double>(inp1, i);
var vb = new Vector<double>(inp2, i);
returnDouble += Vector.Dot(va, vb);
}
return returnDouble;
}
public static double NonSIMDMultAccumulate(double[] inp1, double[] inp2) {
var returnDouble = 0d;
for (int i = 0; i < inp1.Length; i++) {
returnDouble += inp1[i] * inp2[i];
}
return returnDouble;
}
static void Main(string[] args) {
Console.WriteLine("Is hardware accelerated: " + Vector.IsHardwareAccelerated);
const int size = 24;
var inp1 = new double[size];
var inp2 = new double[size];
var random = new Random();
for (var i = 0; i < inp1.Length; i++) {
inp1[i] = random.NextDouble();
inp2[i] = random.NextDouble();
}
var sumSafe = 0d;
var sumFast = 0d;
var sw = Stopwatch.StartNew();
for (var i = 0; i < 10; i++) {
sumSafe = NonSIMDMultAccumulate(inp1, inp2);
}
Console.WriteLine("{0} Ticks", sw.Elapsed.Ticks);
sw.Restart();
for (var i = 0; i < 10; i++) {
sumFast = SIMDMultAccumulate(inp1, inp2);
}
Console.WriteLine("{0} Ticks", sw.Elapsed.Ticks);
// Assert.AreEqual(sumSafe, sumFast, 0.00000001);
}
}
}
与非 SIMD 版本相比,SIMD 版本需要大约 70% 的刻度。我正在运行一个 Haswell 架构和恕我直言。应该实施FMA3! (发布版本,首选 x64)。
有什么想法吗? 谢谢大家!
【问题讨论】:
-
基准测试失败,时间完全由抖动开销决定。很难衡量,这是非常快的代码。在它周围放置一个 for(;;) 循环以运行 10 次,以消除抖动开销并感受可变性。选择一个大于 24 的数字以查看任何真正的改进。顺便说一句,当前代码生成器中没有 FMA。
-
我明白你的意思,改变了参数并获得了更好的结果,但就我而言,这可能不是正确的方法!
-
请使用真正的基准测试框架,github.com/dotnet/BenchmarkDotNet 就像 Hans 提到的那样,只有当您摆脱开销时,测试才会有用。
-
一有空闲时间,我就会尝试做 BenchmarkDotNet 的事情......
-
@harold 删除了他的答案,但重要的是您应该累积结果向量,而不是在内部循环内缩小为标量。并使用多个累加器来隐藏 FP 延迟。
标签: c# simd system.numerics