我想在.NET Core中使用System.Numerics.Vector名称空间,但是遇到了不支持复数向量的问题。就目前而言,Vector类型将适用于任何原始类型,从字节到双精度。我不是行业程序员,因此我可能不了解一些低级/概念性的东西,但是为什么不支持复数呢?据我所知,也许唯一的问题是Complex类型是托管结构。我不仅可以将Register类型扩展为包括[FieldOffset(0)] internal Complex complex_0
并以此为基础构建新功能吗?
我愿意亲自进行类型扩展,但想问一问为什么没有将其放在首位,因为似乎很多信号处理工作将从复数向量中受益匪浅和SIMD。
答案 0 :(得分:0)
System.Numerics.Vector对于您的目标没有用。您可以依赖System.Runtime.Intrinsics命名空间中的Vector256类。必要的说明在System.Runtime.Intrinsics.X86.Avx类中公开。 (Vector128在这里几乎没有什么帮助,因为它代表一个复数。我无法找到任何能帮助我将复数重新解释为Vector128而无需复制的魔术,否则我们将能够增强内置运算符为Complex类提供)
所以,想法是
请参见下面的示例代码:
public static class ComplexAvx
{
public static Span<Complex> Add(ReadOnlySpan<Complex> left, ReadOnlySpan<Complex> right)
{
var result = new Complex[Math.Min(left.Length, right.Length)].AsSpan();
var vectorRes = MemoryMarshal.Cast<Complex, Vector256<double>>(result);
var vectorLeft = MemoryMarshal.Cast<Complex, Vector256<double>>(left);
var vectorRight = MemoryMarshal.Cast<Complex, Vector256<double>>(right);
for (int i = 0; i < vectorRes.Length; i++)
vectorRes[i] = Avx.Add(vectorLeft[i], vectorRight[i]);
for (int i = 2 * vectorRes.Length; i < result.Length; i++)
result[i] = left[i] + right[i];
return result;
}
public static Span<Complex> Subtract(ReadOnlySpan<Complex> left, ReadOnlySpan<Complex> right)
{
var result = new Complex[Math.Min(left.Length, right.Length)].AsSpan();
var vectorRes = MemoryMarshal.Cast<Complex, Vector256<double>>(result);
var vectorLeft = MemoryMarshal.Cast<Complex, Vector256<double>>(left);
var vectorRight = MemoryMarshal.Cast<Complex, Vector256<double>>(right);
for (int i = 0; i < vectorRes.Length; i++)
vectorRes[i] = Avx.Subtract(vectorLeft[i], vectorRight[i]);
for (int i = 2 * vectorRes.Length; i < result.Length; i++)
result[i] = left[i] - right[i];
return result;
}
public static Span<Complex> Multiply(ReadOnlySpan<Complex> left, ReadOnlySpan<Complex> right)
{
var result = new Complex[Math.Min(left.Length, right.Length)].AsSpan();
var vectorRes = MemoryMarshal.Cast<Complex, Vector256<double>>(result);
var vectorLeft = MemoryMarshal.Cast<Complex, Vector256<double>>(left);
var vectorRight = MemoryMarshal.Cast<Complex, Vector256<double>>(right);
for (int i = 0; i < vectorRes.Length; i++)
{
var l = vectorLeft[i];
var r = vectorRight[i];
vectorRes[i] = Avx.HorizontalAdd(
Avx.Multiply(
Avx.Multiply(l, r),
Vector256.Create(1.0, -1.0, 1.0, -1.0)),
Avx.Multiply(
l,
Avx.Permute(r, 0b0101)
));
}
for (int i = 2 * vectorRes.Length; i < result.Length; i++)
result[i] = left[i] * right[i];
return result;
}
}
此代码执行5个AVX操作以将两个复数相乘,而使用System.Numerics.Complex.Multiply将消耗8个乘法和4个加法。 (当然,代码应检查Avx.IsEnabled-为简便起见,跳过了该步骤) 我没有为基准测试感到烦恼,因此不确定在现代CPU上会产生多少(如果有的话)收益。 Avx512将允许使用相同的5条指令一次处理4个复合体。但这尚待CLR支持。