Тип System.Numerics.Vector4
имеет отличную производительность, поскольку среда CLR может оптимизировать его для использования векторизованных инструкций ЦП. Однако я хотел бы создать свой собственный 4-элементный векторный тип с плавающей запятой одинарной точности, чтобы я мог добавлять различные удобные методы и свойства, атрибуты, интерфейсы и т. Д. c ... (IE, больше, чем я могу сделать с методами расширения.) К сожалению, мой собственный векторный тип работает не так хорошо, как 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;
}
}
В этой программе 3 теста. Первый тестирует работоспособность System.Numerics.Vector4
. Второй тестирует производительность пользовательского векторного типа MyVector
, используя простое добавление элементов. И третий тестирует производительность специального векторного типа MyVectorSimd
, который сам использует вложенный System.Numerics.Vector4
. Вот результаты на моем компьютере, на котором запущена сборка Release. Net Core 3.1:
System.Numerics.Vector4: <499500, 499500, 499500, 499500> (00:00:01.0635501)
MyVector: <499500, 499500, 499500, 499500> (00:00:04.8566430)
MyVectorSimd: <499500, 499500, 499500, 499500> (00:00:03.4586021)
Как видите, оба настраиваемых векторных типа работают намного хуже, чем System.Numerics.Vector4
, хотя один который использует System.Numerics.Vector4
внутри, все же немного лучше, чем тот, который этого не делает.
Итак, повторим свой вопрос, есть ли способ получить собственный векторный тип для работы так же хорошо, как System.Numerics.Vector4
?