ParallelHelper
ParallelHelper
병렬 코드로 작업할 고성능 API가 포함되어 있습니다. 여기에는 지정된 데이터 집합 또는 반복 범위 또는 영역에 대해 병렬 작업을 신속하게 설정하고 실행하는 데 사용할 수 있는 성능 지향 메서드가 포함되어 있습니다.
플랫폼 API:
ParallelHelper
,IAction
,IAction2D
,IRefAction<T>
IInAction<T><T>
작동 방식
ParallelHelper
형식은 다음 세 가지 주요 개념을 중심으로 빌드됩니다.
- 대상 반복 범위에 대해 자동 일괄 처리를 수행합니다. 즉, 사용 가능한 CPU 코어 수에 따라 적절한 작업 단위 수를 자동으로 예약합니다. 이 작업은 단일 병렬 반복마다 병렬 콜백을 한 번 호출하는 오버헤드를 줄이기 위해 수행됩니다.
- 제네릭 형식이 C#에서 구현되는 방식을 많이 활용하며, 같은
Action<T>
대리자 대신 특정 인터페이스를 구현하는 형식을 사용합니다struct
. 이 작업은 JIT 컴파일러가 사용 중인 각 개별 콜백 형식을 "볼" 수 있도록 하므로 가능하면 콜백을 완전히 인라인할 수 있습니다. 이렇게 하면 각 병렬 반복의 오버헤드를 크게 줄일 수 있습니다. 특히 매우 작은 콜백을 사용하는 경우 대리자 호출만으로도 비용이 약간 들 수 있습니다. 또한 형식을struct
콜백으로 사용하려면 개발자가 클로저에서 캡처되는 변수를 수동으로 처리해야 하므로 인스턴스 메서드 및 각 콜백 호출 속도가 상당히 느려질 수 있는 다른 값에서 포인터를this
실수로 캡처하지 못하도록 방지할 수 있습니다. 이는 다음과 같은ImageSharp
다른 성능 지향 라이브러리에서 사용되는 것과 동일한 접근 방식입니다. - 1D 및 2D 루프, 부작용이 있는 항목 반복, 부작용이 없는 항목 반복 등 4가지 유형의 반복을 나타내는 4가지 유형의 API를 노출합니다. 각 작업 유형에는 API에 전달되는 콜백에
struct
적용해야 하는ParallelHelper
해당interface
형식이 있습니다IInAction<T><T>
IAction
IAction2D
IRefAction<T>
. 이렇게 하면 개발자가 의도에 대해 더 명확한 코드를 작성할 수 있으며 API가 내부적으로 추가 최적화를 수행할 수 있습니다.
구문
일부 float[]
배열의 모든 항목을 처리하고 각 2
항목을 곱하는 데 관심이 있다고 가정해 보겠습니다. 이 경우 변수를 캡처할 필요가 없습니다. 각 항목을 사용하여 IRefAction<T>
interface
ParallelHelper
콜백에 자동으로 피드할 수 있습니다. 필요한 것은 인수를 수신하고 필요한 작업을 수행하는 콜백을 ref float
정의하는 것입니다.
// Be sure to include this using at the top of the file:
using Microsoft.Toolkit.HighPerformance.Helpers;
// First declare the struct callback
public readonly struct ByTwoMultiplier : IRefAction<float>
{
public void Invoke(ref float x) => x *= 2;
}
// Create an array and run the callback
float[] array = new float[10000];
ParallelHelper.ForEach<float, ByTwoMultiplier>(array);
API를 ForEach
사용하면 반복 범위를 ParallelHelper
지정할 필요가 없습니다. 컬렉션을 일괄 처리하고 각 입력 항목을 자동으로 처리합니다. 또한 이 특정 예제에서는 인수로 전달할 struct
필요도 없었습니다. 초기화하는 데 필요한 필드가 없으므로 호출 ParallelHelper.ForEach
할 때 해당 형식을 형식 인수로 지정할 수 있습니다. 그러면 API가 자체적으로 새 struct
인스턴스를 만들고 이를 사용하여 다양한 항목을 처리할 수 있습니다.
닫기 개념을 소개하기 위해 배열 요소를 런타임에 지정된 값으로 곱하려고 합니다. 이렇게 하려면 콜백 struct
형식에서 해당 값을 "캡처"해야 합니다. 다음과 같이 수행할 수 있습니다.
public readonly struct ItemsMultiplier : IRefAction<float>
{
private readonly float factor;
public ItemsMultiplier(float factor)
{
this.factor = factor;
}
public void Invoke(ref float x) => x *= this.factor;
}
// ...
ParallelHelper.ForEach(array, new ItemsMultiplier(3.14f));
이제 상수 대신 요소를 곱하는 데 사용할 요소를 나타내는 필드가 포함되어 있음을 알 struct
수 있습니다. 또한 호출할 ForEach
때 관심 있는 요소를 사용하여 콜백 형식의 인스턴스를 명시적으로 만듭니다. 또한 이 경우 C# 컴파일러는 사용 중인 형식 인수를 자동으로 인식할 수 있으므로 메서드 호출에서 함께 생략할 수 있습니다.
콜백에서 액세스해야 하는 값에 대한 필드를 만드는 이 방법을 사용하면 캡처하려는 값을 명시적으로 선언할 수 있으므로 코드를 보다 표현적으로 만들 수 있습니다. 이는 일부 지역 변수에 액세스하는 람다 함수 또는 로컬 함수를 선언할 때 C# 컴파일러가 백그라운드에서 수행하는 것과 정확히 동일합니다.
다음은 API를 For
사용하여 배열의 모든 항목을 병렬로 초기화하는 또 다른 예입니다. 이번에는 대상 배열을 직접 캡처하고 콜백을 사용하여 IAction
interface
메서드에 현재 병렬 반복 인덱스를 인수로 제공하는 방법을 확인합니다.
public readonly struct ArrayInitializer : IAction
{
private readonly int[] array;
public ArrayInitializer(int[] array)
{
this.array = array;
}
public void Invoke(int i)
{
this.array[i] = i;
}
}
// ...
ParallelHelper.For(0, array.Length, new ArrayInitializer(array));
참고 항목
콜백 형식은 -s이므로 struct
참조가 아니라 병렬로 실행되는 각 스레드에 복사하여 전달됩니다. 즉, 콜백 형식의 필드로 저장되는 값 형식도 복사됩니다. C# 컴파일러에서 해당 필드의 값을 수정할 수 없도록 콜백 struct
readonly
을 표시하여 세부 정보를 기억하고 오류를 방지하는 것이 좋습니다. 이는 값 형식의 인스턴스 필드에만 적용됩니다. 콜백 struct
에 static
모든 형식의 필드 또는 참조 필드가 있는 경우 해당 값은 병렬 스레드 간에 올바르게 공유됩니다.
메서드
이러한 API는 , 및 IInAction<T>
인터페이스에 해당하는 IAction2D
IRefAction<T>
IAction
4개의 기본 API에 의해 ParallelHelper
노출됩니다. 또한 이 형식은 ParallelHelper
반복 범위 또는 입력 콜백 형식을 지정하는 여러 가지 방법을 제공하는 이러한 메서드에 대한 여러 오버로드를 노출합니다. For
및 For2D
작업 IAction
및 IAction2D
인스턴스는 각 병렬 반복의 인덱스로 직접 액세스할 수 있는 기본 컬렉션에 매핑할 필요가 없는 일부 병렬 작업을 수행해야 할 때 사용됩니다. ForEach
오버로드는 대신 켜 IRefAction<T>
지고 IInAction<T>
인스턴스가 되며, 병렬 반복이 직접 인덱싱할 수 있는 컬렉션의 항목에 직접 매핑될 때 사용할 수 있습니다. 이 경우 인덱싱 논리를 추상화하므로 각 병렬 호출은 해당 항목을 검색하는 방법이 아니라 작업할 입력 항목에 대해서만 걱정해야 합니다.
예제
단위 테스트에서 더 많은 예제를 찾을 수 있습니다.
.NET Community Toolkit