C# 是否可以创建性能类似System.Numerics.Vector4的自定义向量类型?
C# 是否可以创建性能类似System.Numerics.Vector4的自定义向量类型?,c#,.net,.net-core,C#,.net,.net Core,System.Numerics.Vector4类型有很好的性能,因为CLR可以优化它以使用矢量化的CPU指令。但是,我想创建自己的自定义4元素、单精度浮点向量类型,以便添加各种方便的方法和属性、属性、接口等。。。不幸的是,我自己的向量类型的性能不如System.Numerics.Vector4,即使它在内部使用System.Numerics.Vector4。有没有办法从自定义向量类型中获得类似System.Numerics.Vector4的性能 下面是一个程序,它试图(但大多失败)通过在自定义向
System.Numerics.Vector4
类型有很好的性能,因为CLR可以优化它以使用矢量化的CPU指令。但是,我想创建自己的自定义4元素、单精度浮点向量类型,以便添加各种方便的方法和属性、属性、接口等。。。不幸的是,我自己的向量类型的性能不如System.Numerics.Vector4
,即使它在内部使用System.Numerics.Vector4
。有没有办法从自定义向量类型中获得类似System.Numerics.Vector4的性能
下面是一个程序,它试图(但大多失败)通过在自定义向量类型中嵌套System.Numerics.Vector4来提高性能:
using System;
using System.Diagnostics;
using System.Numerics;
using System.Runtime.CompilerServices;
class Program
{
private const int ARR_LENGTH = 1000;
private const int OUTER_LOOP = 1000000;
static void Main(string[] args)
{
TestVector4();
TestMyVector();
TestMyVectorSimd();
}
static void TestVector4()
{
Vector4[] arr = new Vector4[ARR_LENGTH];
for(int i = 0; i < arr.Length; i++)
arr[i] = new Vector4(i, i, i, i);
Stopwatch sw = Stopwatch.StartNew();
Vector4 total = default;
for(int i = 0; i < OUTER_LOOP; i++)
{
total = default;
for(int j = 0; j < ARR_LENGTH; j++)
total += arr[j];
}
sw.Stop();
Console.WriteLine($"System.Numerics.Vector4: {total} ({sw.Elapsed})");
}
static void TestMyVector()
{
MyVector[] arr = new MyVector[ARR_LENGTH];
for(int i = 0; i < arr.Length; i++)
arr[i] = new MyVector(i, i, i, i);
Stopwatch sw = Stopwatch.StartNew();
MyVector total = default;
for(int i = 0; i < OUTER_LOOP; i++)
{
total = default;
for(int j = 0; j < ARR_LENGTH; j++)
total += arr[j];
}
sw.Stop();
Console.WriteLine($"MyVector: {total} ({sw.Elapsed})");
}
static void TestMyVectorSimd()
{
MyVectorSimd[] arr = new MyVectorSimd[ARR_LENGTH];
for(int i = 0; i < arr.Length; i++)
arr[i] = new MyVectorSimd(i, i, i, i);
Stopwatch sw = Stopwatch.StartNew();
MyVectorSimd total = default;
for(int i = 0; i < OUTER_LOOP; i++)
{
total = default;
for(int j = 0; j < ARR_LENGTH; j++)
total += arr[j];
}
sw.Stop();
Console.WriteLine($"MyVectorSimd: {total} ({sw.Elapsed})");
}
}
struct MyVector
{
public float X, Y, Z, W;
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public MyVector(float x, float y, float z, float w)
{
X = x;
Y = y;
Z = z;
W = w;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public override string ToString()
{
return $"<{X}, {Y}, {Z}, {W}>";
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static MyVector operator +(MyVector left, MyVector right)
{
left.X += right.X;
left.Y += right.Y;
left.Z += right.Z;
left.W += right.W;
return left;
}
}
struct MyVectorSimd
{
public Vector4 V;
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public MyVectorSimd(float x, float y, float z, float w)
{
V = new Vector4(x, y, z, w);
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public override string ToString()
{
return V.ToString();
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static MyVectorSimd operator +(MyVectorSimd left, MyVectorSimd right)
{
left.V += right.V;
return left;
}
}
正如您所看到的,这两种自定义向量类型的性能都比System.Numerics.Vector4
差得多,尽管内部使用System.Numerics.Vector4
的类型仍然比不使用的类型好一点
所以重申一下我的问题,有没有办法让自定义向量类型与System.Numerics.Vector4
一样执行?多亏Christopher在对我的原始问题的评论中,我能够简单地将in
关键字添加到left
和right
操作符参数中(重写它们以创建新的返回值,而不是修改left
值)足以触发优化。此更新的代码为所有三个向量变量提供了基本相同的性能:
using System;
using System.Diagnostics;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
class Program
{
private const int ARR_LENGTH = 1000;
private const int OUTER_LOOP = 1000000;
static void Main(string[] args)
{
TestVector4();
TestMyVector();
TestMyVectorSimd();
}
static void TestVector4()
{
Vector4[] arr = new Vector4[ARR_LENGTH];
for(int i = 0; i < arr.Length; i++)
arr[i] = new Vector4(i, i, i, i);
Stopwatch sw = Stopwatch.StartNew();
Vector4 total = default;
for(int i = 0; i < OUTER_LOOP; i++)
{
total = default;
for(int j = 0; j < ARR_LENGTH; j++)
total += arr[j];
}
sw.Stop();
Console.WriteLine($"System.Numerics.Vector4: {total} ({sw.Elapsed})");
}
static void TestMyVector()
{
MyVector[] arr = new MyVector[ARR_LENGTH];
for(int i = 0; i < arr.Length; i++)
arr[i] = new MyVector(i, i, i, i);
Stopwatch sw = Stopwatch.StartNew();
MyVector total = default;
for(int i = 0; i < OUTER_LOOP; i++)
{
total = default;
for(int j = 0; j < ARR_LENGTH; j++)
total += arr[j];
}
sw.Stop();
Console.WriteLine($"MyVector: {total} ({sw.Elapsed})");
}
static void TestMyVectorSimd()
{
MyVectorSimd[] arr = new MyVectorSimd[ARR_LENGTH];
for(int i = 0; i < arr.Length; i++)
arr[i] = new MyVectorSimd(i, i, i, i);
Stopwatch sw = Stopwatch.StartNew();
MyVectorSimd total = default;
for(int i = 0; i < OUTER_LOOP; i++)
{
total = default;
for(int j = 0; j < ARR_LENGTH; j++)
total += arr[j];
}
sw.Stop();
Console.WriteLine($"MyVectorSimd: {total} ({sw.Elapsed})");
}
}
struct MyVector
{
public float X, Y, Z, W;
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public MyVector(float x, float y, float z, float w)
{
X = x;
Y = y;
Z = z;
W = w;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public override string ToString()
{
return $"<{X}, {Y}, {Z}, {W}>";
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static MyVector operator +(in MyVector left, in MyVector right)
{
return new MyVector(left.X + right.X, left.Y + right.Y, left.Z + right.Z, left.W + right.W);
}
}
struct MyVectorSimd
{
public Vector4 V;
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public MyVectorSimd(float x, float y, float z, float w)
: this()
{
V = new Vector4(x, y, z, w);
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public MyVectorSimd(Vector4 v)
: this()
{
V = v;
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public override string ToString()
{
return V.ToString();
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static MyVectorSimd operator +(in MyVectorSimd left, in MyVectorSimd right)
{
return new MyVectorSimd(left.V + right.V);
}
}
使用系统;
使用系统诊断;
使用系统数字;
使用System.Runtime.CompilerServices;
使用System.Runtime.InteropServices;
班级计划
{
专用常量int ARR_LENGTH=1000;
外部循环的私有常量=1000000;
静态void Main(字符串[]参数)
{
TestVector4();
TestMyVector();
TestMyVectorSimd();
}
静态void TestVector4()
{
向量4[]arr=新向量4[arr_长度];
对于(int i=0;i
结果:
System.Numerics.Vector4: <499500, 499500, 499500, 499500> (00:00:00.9987530)
MyVector: <499500, 499500, 499500, 499500> (00:00:01.0064586)
MyVectorSimd: <499500, 499500, 499500, 499500> (00:00:00.9739642)
System.Numerics.Vector4:(00:00:00.9987530)
MyVector:(00:00:01.0064586)
MyVectorSimd:(00:00:00.9739642)
编辑:这对Vector4有效,但无论出于何种原因,对Vector2或Vector3无效。仍在寻找如何创建与System.Numerics中的性能类似的自定义Vector2或Vector3的答案。多亏Christopher在对我原始问题的评论中,我才能够找出simp将in
关键字添加到left
和right
运算符参数中(并重写它们以创建新的返回值,而不是修改l
System.Numerics.Vector4: <499500, 499500, 499500, 499500> (00:00:00.9987530)
MyVector: <499500, 499500, 499500, 499500> (00:00:01.0064586)
MyVectorSimd: <499500, 499500, 499500, 499500> (00:00:00.9739642)