Share via


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>IActionIAction2DIRefAction<T>. 이렇게 하면 개발자가 의도에 대해 더 명확한 코드를 작성할 수 있으며 API가 내부적으로 추가 최적화를 수행할 수 있습니다.

구문

일부 float[] 배열의 모든 항목을 처리하고 각 2항목을 곱하는 데 관심이 있다고 가정해 보겠습니다. 이 경우 변수를 캡처할 필요가 없습니다. 각 항목을 사용하여 IRefAction<T>interfaceParallelHelper 콜백에 자동으로 피드할 수 있습니다. 필요한 것은 인수를 수신하고 필요한 작업을 수행하는 콜백을 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 사용하여 배열의 모든 항목을 병렬로 초기화하는 또 다른 예입니다. 이번에는 대상 배열을 직접 캡처하고 콜백을 사용하여 IActioninterface 메서드에 현재 병렬 반복 인덱스를 인수로 제공하는 방법을 확인합니다.

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# 컴파일러에서 해당 필드의 값을 수정할 수 없도록 콜백 structreadonly을 표시하여 세부 정보를 기억하고 오류를 방지하는 것이 좋습니다. 이는 값 형식의 인스턴스 필드에만 적용됩니다. 콜백 structstatic 모든 형식의 필드 또는 참조 필드가 있는 경우 해당 값은 병렬 스레드 간에 올바르게 공유됩니다.

메서드

이러한 API는 , 및 IInAction<T> 인터페이스에 해당하는 IAction2DIRefAction<T>IAction4개의 기본 API에 의해 ParallelHelper노출됩니다. 또한 이 형식은 ParallelHelper 반복 범위 또는 입력 콜백 형식을 지정하는 여러 가지 방법을 제공하는 이러한 메서드에 대한 여러 오버로드를 노출합니다. ForFor2D 작업 IActionIAction2D 인스턴스는 각 병렬 반복의 인덱스로 직접 액세스할 수 있는 기본 컬렉션에 매핑할 필요가 없는 일부 병렬 작업을 수행해야 할 때 사용됩니다. ForEach 오버로드는 대신 켜 IRefAction<T> 지고 IInAction<T> 인스턴스가 되며, 병렬 반복이 직접 인덱싱할 수 있는 컬렉션의 항목에 직접 매핑될 때 사용할 수 있습니다. 이 경우 인덱싱 논리를 추상화하므로 각 병렬 호출은 해당 항목을 검색하는 방법이 아니라 작업할 입력 항목에 대해서만 걱정해야 합니다.

예제

단위 테스트에서 더 많은 예제를 찾을 수 있습니다.