학습
SIMD 가속 숫자 형식 사용
SIMD(Single Instruction, Multiple Data)는 단일 명령을 사용하여 여러 데이터 조각에 대한 작업을 병렬로 수행하기 위한 하드웨어 지원을 제공합니다. .NET에서는 System.Numerics 네임스페이스 아래에 SIMD 가속 형식 집합이 있습니다. SIMD 작업은 하드웨어 수준에서 병렬화될 수 있습니다. 이를 통해 수학, 과학 및 그래픽 앱에서 공통적인 벡터화된 계산의 처리량이 증가합니다.
.NET SIMD 가속 형식에는 다음 형식이 포함됩니다.
Vector2, Vector3 및 Vector4 형식은 2, 3 및 4 Single 값이 있는 벡터를 나타냅니다.
3x2 행렬을 나타내는 두 가지 행렬 형식인 Matrix3x2와 Single 값의 4x4 행렬을 나타내는 Matrix4x4입니다.
Single 값을 사용하여 3차원 실제 회전을 인코딩하는 데 사용되는 벡터를 나타내는 Quaternion 형식입니다.
Vector<T> 형식은 지정된 숫자 형식의 벡터를 나타내고 SIMD 지원을 활용하는 광범위한 연산자 집합을 제공합니다. Vector<T> 인스턴스의 개수는 애플리케이션 수명 동안 고정되지만 해당 값 Vector<T>.Count는 코드를 실행하는 컴퓨터의 CPU에 따라 달라집니다.
참고
Vector<T> 형식은 .NET Framework에 포함되지 않습니다. 이 형식에 액세스하려면 System.Numerics.Vectors NuGet 패키지를 설치해야 합니다.
SIMD 가속 형식은 SIMD 가속이 아닌 하드웨어 또는 JIT 컴파일러와 함께 사용할 수 있는 방식으로 구현됩니다. SIMD 명령을 활용하려면 64비트 앱이 RyuJIT 컴파일러를 사용하는 런타임에서 실행되어야 합니다. RyuJIT 컴파일러는 .NET Core 및 .NET Framework 4.6 이상에 포함되어 있습니다. SIMD 지원은 64비트 프로세서를 대상으로 하는 경우에만 제공됩니다.
사용자 지정 SIMD 알고리즘을 실행하기 전에 Boolean을 반환하는 Vector.IsHardwareAccelerated를 사용하여 호스트 컴퓨터가 SIMD를 지원하는지 확인할 수 있습니다. 이는 특정 형식에 대해 SIMD 가속이 사용하도록 설정된다는 것을 보장하지 않지만 일부 형식에서 지원된다는 표시입니다.
.NET에서 가장 원시적인 SIMD 가속 형식은 2, 3, 4 Single 값이 있는 벡터를 나타내는 Vector2, Vector3 및 Vector4 형식입니다. 아래 예에서는 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);
3x2 행렬을 나타내는 Matrix3x2 및 4x4 행렬을 나타내는 Matrix4x4입니다. 행렬 관련 계산에 사용할 수 있습니다. 아래 예에서는 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>.Count는 코드를 실행하는 컴퓨터의 CPU에 따라 달라집니다.
다음 예에서는 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가 아닌 동등한 코드보다 성능이 더 나쁠 수도 있습니다.
.NET 피드백
.NET은(는) 오픈 소스 프로젝트입니다. 다음 링크를 선택하여 피드백을 제공해 주세요.
추가 리소스
설명서
-
stackalloc 식 - 힙 대신 스택에 변수 스토리지 할당 - C# reference
C# stackalloc 식은 스택에 메모리 블록을 할당합니다. Stackalloc 메모리는 해당 메서드가 반환될 때 자동으로 삭제됩니다.
-
Memory<T> 및 Span<T> 사용 지침 - .NET
이 문서에서는 파이프라인에서 사용할 수 있는 .NET Core의 구조적 데이터 버퍼인 Memory 및 Span에 대해 설명합니다.