Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
SIMD (одна инструкция, несколько данных) обеспечивает поддержку оборудования для выполнения операции с несколькими частями данных параллельно с помощью одной инструкции. В .NET имеется набор типов, ускоренных с помощью SIMD, принадлежащих пространству имен System.Numerics. Операции SIMD можно параллелизировать на уровне оборудования. Это повышает пропускную способность векторных вычислений, которые распространены в математических, научных и графических приложениях.
Типы .NET с ускорением SIMD
Типы .NET с ускорением SIMD включают следующие:
Типы Vector2, Vector3 и Vector4, которые представляют собой векторы с 2, 3 и 4 значениями Single.
Два типа матриц, Matrix3x2, представляющие матрицу 3x2, и Matrix4x4, представляющие матрицу 4x4 из значений Single.
Тип Plane , представляющий плоскость в трехмерном пространстве с помощью Single значений.
Тип Quaternion , представляющий вектор, используемый для кодирования трехмерных физических поворотов с помощью Single значений.
Тип Vector<T> , представляющий вектор указанного числового типа и предоставляющий широкий набор операторов, которые получают поддержку SIMD. Число экземпляров Vector<T> фиксировано в течение времени существования приложения, но его значение Vector<T>.Count зависит от процессора компьютера, выполняющего код.
Замечание
Тип Vector<T> не включен в .NET Framework. Чтобы получить доступ к этому типу, необходимо установить пакет NuGet System.Numerics.Vectors .
Типы с ускорением через SIMD реализованы таким образом, что их можно использовать с аппаратным обеспечением без ускорения через SIMD или компиляторами JIT. Чтобы воспользоваться инструкциями SIMD, 64-разрядные приложения должны запускаться средой выполнения, используюющей компилятор RyuJIT . Компилятор RyuJIT включается в .NET Core и .NET Framework 4.6 и более поздних версий. Поддержка SIMD предоставляется только при выборе 64-разрядных процессоров.
Как использовать SIMD?
Перед выполнением пользовательских алгоритмов SIMD можно проверить, поддерживает ли главный компьютер SIMD с помощью Vector.IsHardwareAccelerated, который возвращает Booleanзначение. Это не гарантирует, что для определенного типа включено ускорение SIMD, но может служить индикатором того, что некоторые типы его поддерживают.
Простые векторы
Наиболее примитивные типы с ускорением SIMD в .NET: Vector2Vector3и Vector4 типы, представляющие векторы с 2, 3 и 4 Single значениями. В приведенном ниже примере используется 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, представляющий матрицу 3x2 и Matrix4x4, представляющую матрицу 4x4. Можно использовать для вычислений, связанных с матрицами. В приведенном ниже примере показано умножение матрицы на ее корреспондентную транспонированную матрицу с помощью 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);
Вектор<T>
Это Vector<T> дает возможность использовать более длинные векторы. Число экземпляров Vector<T> исправлено, но его значение Vector<T>.Count зависит от ЦП компьютера, на котором выполняется код.
В следующем примере показано, как вычислить поэлементную сумму двух массивов с помощью Vector<T>.
double[] Sum(double[] left, double[] right)
{
if (left is null)
{
throw new ArgumentNullException(nameof(left));
}
if (right is null)
{
throw new ArgumentNullException(nameof(right));
}
if (left.Length != right.Length)
{
throw new ArgumentException($"{nameof(left)} and {nameof(right)} are not the same length");
}
int length = left.Length;
double[] result = new double[length];
// Get the number of elements that can't be processed in the vector
// NOTE: Vector<T>.Count is a JIT time constant and will get optimized accordingly
int remaining = length % Vector<double>.Count;
for (int i = 0; i < length - remaining; i += Vector<double>.Count)
{
var v1 = new Vector<double>(left, i);
var v2 = new Vector<double>(right, i);
(v1 + v2).CopyTo(result, i);
}
for (int i = length - remaining; i < length; i++)
{
result[i] = left[i] + right[i];
}
return result;
}
Замечания
SIMD скорее всего удалит одно узкое место и выявит следующее, например, пропускную способность памяти. В целом преимущество производительности использования SIMD зависит от конкретного сценария, и в некоторых случаях это может даже хуже, чем простой эквивалентный код, отличный от SIMD.