Использование SIMD-ускоренные числовых типов


SIMD (одна инструкция, несколько данных) обеспечивает аппаратную поддержку для параллельного выполнения операции над несколькими частями данных с использованием одной инструкции. В .NET существует набор типов с ускорением SIMD в пространстве имен System.Numerics. Операции SIMD можно распараллелить на аппаратном уровне. Это увеличивает пропускную способность векторизованных вычислений, которые распространены в математических, научных и графических приложениях.

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.


Добавить комментарий