SIMD (одна инструкция, несколько данных) обеспечивает аппаратную поддержку для параллельного выполнения операции над несколькими частями данных с использованием одной инструкции. В .NET существует набор типов с ускорением SIMD в пространстве имен System.Numerics. Операции SIMD можно распараллелить на аппаратном уровне. Это увеличивает пропускную способность векторизованных вычислений, которые распространены в математических, научных и графических приложениях.
.NET SIMD-ускорение типов
Типы с ускорением .NET SIMD включают следующие типы:
- В Vector2, VEctor3 и Vector4 типы, которые представляют собой векторы с 2, 3, и 4 Одиночные значения.
- Два типа матриц: Matrix3x2, представляющая матрицу 3×2, и Matrix4x4, которая представляет матрицу 4×4 значений Single.
- Тип Plane, который представляет плоскость в трехмерном пространстве с использованием значений Single .
- Тип Quaternion, представляющий вектор, используемый для кодирования трехмерных физических вращений с использованием значений Single .
- Тип Vector<T>, который представляет вектор указанного числового типа и предоставляет широкий набор операторов, которые выигрывают от поддержки SIMD. Количество экземпляров Vector<T> фиксировано на время существования приложения, но его значение Vector<T>.Count зависит от ЦП машины, на которой выполняется код.
Тип Vector не включен в .NET Framework. Чтобы получить доступ к этому типу, необходимо установить пакет NuGet System.Numerics.Vectors.
Типы с ускорением SIMD реализованы таким образом, что их можно использовать с аппаратным обеспечением без ускорения SIMD или с JIT-компиляторами. Чтобы воспользоваться преимуществами инструкций SIMD, ваши 64-битные приложения должны запускаться средой выполнения, которая использует компилятор RyuJIT . RyuJIT компилятор входит в .NET Ядра и в .NET Framework 4.6 и более поздних версий. Поддержка SIMD предоставляется только для 64-битных процессоров.
Как использовать SIMD?
Перед выполнением пользовательских алгоритмов SIMD можно проверить, поддерживает ли хост-компьютер SIMD, используя Vector.IsHardwareAccelerated, который возвращает логическое значение. Это не гарантирует, что SIMD-ускорение включено для определенного типа, но является индикатором того, что оно поддерживается некоторыми типами.
Простые векторы
Наиболее примитивными типами с ускорением SIMD в .NET являются типы Vector2, Vector3 и Vector4, которые представляют векторы с 2, 3 и 4 одиночными значениями. В приведенном ниже примере Vector2 используется для добавления двух векторов.
var v1 = new Vector2(0.1f, 0.2f);
var v2 = new Vector2(1.1f, 2.2f);
var vResult = v1 + v2;
Кроме того, можно использовать .NET векторов вычислить другие математические свойства векторов, таких как Dot product, Transform, Clampи так далее.
var v1 = new Vector2(0.1f, 0.2f);
var v2 = new Vector2(1.1f, 2.2f);
var vResult1 = Vector2.Dot(v1, v2);
var vResult2 = Vector2.Distance(v1, v2);
var vResult3 = Vector2.Clamp(v1, Vector2.Zero, Vector2.One);
Матрица
Matrix3x2, которая представляет матрицу 3×2, и Matrix4x4, которая представляет матрицу 4×4. Может использоваться для матричных вычислений. Пример ниже демонстрирует умножение матрицы на соответствующую матрицу транспонирования с использованием SIMD.
var m1 = new Matrix4x4(
1.1f, 1.2f, 1.3f, 1.4f,
2.1f, 2.2f, 3.3f, 4.4f,
3.1f, 3.2f, 3.3f, 3.4f,
4.1f, 4.2f, 4.3f, 4.4f);
var m2 = Matrix4x4.Transpose(m1);
var mResult = Matrix4x4.Multiply(m1, m2);
Vector<T>
Vector<T> дает возможность использовать более длинные векторы. Количество экземпляров Vector<T> фиксировано, но его значение Vector<T>.Count зависит от ЦП машины, на которой выполняется код.
В приведенном ниже примере демонстрируется добавление элементов длинных массивов с помощью Vector<T>.
double[] SimdVectorProd(double[] left, double[] right)
{
var offset = Vector<double>.Count;
double[] result = new double[left.Length];
int i = 0;
for (i = 0; i < left.Length; i += offset)
{
var v1 = new Vector<double>(left, i);
var v2 = new Vector<double>(right, i);
(v1 * v2).CopyTo(result, i);
}
//remaining items
for (; i < left.Length; ++i)
{
result[i] = left[i] * right[i];
}
return result;
}
Замечания
SIMD с большей вероятностью устранит одно узкое место и выявит следующее, например, пропускную способность памяти. В общем, выигрыш в производительности от использования SIMD зависит от конкретного сценария, а в некоторых случаях он может работать хуже, чем более простой код, не эквивалентный SIMD.