C++ AMP 개요

참고 항목

C++ AMP 헤더는 Visual Studio 2022 버전 17.0부터 더 이상 사용되지 않습니다. AMP 헤더를 포함하면 빌드 오류가 생성됩니다. 경고를 무음으로 표시하기 위해 AMP 헤더를 포함하기 전에 정의 _SILENCE_AMP_DEPRECATION_WARNINGS 합니다.

C++ 가속 대규모 병렬 처리(C++ AMP)는 개별 그래픽 카드 GPU(그래픽 처리 장치)와 같은 데이터 병렬 하드웨어를 활용하여 C++ 코드 실행을 가속화합니다. C++ AMP를 사용하면 다른 유형의 하드웨어에서 병렬 처리를 사용하여 실행을 가속화할 수 있도록 다차원 데이터 알고리즘을 코딩할 수 있습니다. C++ AMP 프로그래밍 모델에는 다차원 배열, 인덱싱, 메모리 전송, 바둑판식 배열 및 수학 함수 라이브러리가 포함됩니다. C++ AMP 언어 확장을 사용하여 CPU에서 GPU로 데이터를 이동하는 방법을 제어하여 성능을 향상시킬 수 있습니다.

시스템 요구 사항

  • Windows 7 이상

  • Windows Server 2008 R2 ~ Visual Studio 2019.

  • DirectX 11 기능 수준 11.0 이상 하드웨어

  • 소프트웨어 에뮬레이터에서 디버깅하려면 Windows 8 또는 Windows Server 2012가 필요합니다. 하드웨어에서 디버깅하는 경우에는 사용 중인 그래픽 카드의 드라이버를 설치해야 합니다. 자세한 내용은 GPU 코드 디버깅을 참조하세요.

  • 참고: AMP는 현재 ARM64에서 지원되지 않습니다.

소개

다음 두 예제에서는 C++ AMP의 기본 구성 요소를 보여 줍니다. 두 개의 1차원 배열의 해당 요소를 추가하려는 경우를 가정합니다. 예를 들어 을 추가하고 {1, 2, 3, 4, 5}{6, 7, 8, 9, 10} 가져올 {7, 9, 11, 13, 15}수 있습니다. C++ AMP를 사용하지 않고 다음 코드를 작성하여 숫자를 추가하고 결과를 표시할 수 있습니다.

#include <iostream>

void StandardMethod() {

    int aCPP[] = {1, 2, 3, 4, 5};
    int bCPP[] = {6, 7, 8, 9, 10};
    int sumCPP[5];

    for (int idx = 0; idx < 5; idx++)
    {
        sumCPP[idx] = aCPP[idx] + bCPP[idx];
    }

    for (int idx = 0; idx < 5; idx++)
    {
        std::cout << sumCPP[idx] << "\n";
    }
}

코드의 중요한 부분은 다음과 같습니다.

  • 데이터: 데이터는 세 개의 배열로 구성됩니다. 모두 동일한 순위(1) 및 길이(5)를 가집니다.

  • 반복: 첫 번째 for 루프는 배열의 요소를 반복하는 메커니즘을 제공합니다. 합계를 계산하기 위해 실행하려는 코드가 첫 번째 for 블록에 포함됩니다.

  • 인덱스: 변수는 idx 배열의 개별 요소에 액세스합니다.

C++ AMP를 사용하여 다음 코드를 대신 작성할 수 있습니다.

#include <amp.h>
#include <iostream>
using namespace concurrency;

const int size = 5;

void CppAmpMethod() {
    int aCPP[] = {1, 2, 3, 4, 5};
    int bCPP[] = {6, 7, 8, 9, 10};
    int sumCPP[size];

    // Create C++ AMP objects.
    array_view<const int, 1> a(size, aCPP);
    array_view<const int, 1> b(size, bCPP);
    array_view<int, 1> sum(size, sumCPP);
    sum.discard_data();

    parallel_for_each(
        // Define the compute domain, which is the set of threads that are created.
        sum.extent,
        // Define the code to run on each thread on the accelerator.
        [=](index<1> idx) restrict(amp) {
            sum[idx] = a[idx] + b[idx];
        }
    );

    // Print the results. The expected output is "7, 9, 11, 13, 15".
    for (int i = 0; i < size; i++) {
        std::cout << sum[i] << "\n";
    }
}

동일한 기본 요소가 있지만 C++ AMP 구문이 사용됩니다.

  • 데이터: C++ 배열을 사용하여 세 개의 C++ AMP array_view 개체를 생성합니다. 개체를 생성 array_view 하기 위해 데이터 값, 순위, 요소 형식, 각 차원의 array_view 개체 길이 등 네 가지 값을 제공합니다. 순위 및 형식은 형식 매개 변수로 전달됩니다. 데이터와 길이는 생성자 매개 변수로 전달됩니다. 이 예제에서는 생성자에 전달되는 C++ 배열이 1차원입니다. 순위 및 길이는 개체에서 데이터의 array_view 사각형 모양을 생성하는 데 사용되며, 데이터 값은 배열을 채우는 데 사용됩니다. 런타임 라이브러리에는 클래스와 유사한 array_view 인터페이스가 있고 이 문서의 뒷부분에서 설명하는 배열 클래스도 포함됩니다.

  • 반복: parallel_for_each 함수(C++ AMP)는 데이터 요소를 반복하는 메커니즘을 제공합니다기본. 이 예제에서는 컴퓨팅 do기본가 지정합니다sum.extent. 실행하려는 코드는 람다 식 또는 커널 함수에 포함됩니다. C restrict(amp) ++ AMP에서 가속할 수 있는 C++ 언어의 하위 집합만 사용됨을 나타냅니다.

  • 인덱스: 인덱스 클래스 변수 idx는 개체의 순위와 일치하도록 1의 array_view 순위로 선언됩니다. 인덱스 사용 하 여 개체의 개별 요소에 array_view 액세스할 수 있습니다.

데이터 셰이핑 및 인덱싱: 인덱스 및 범위

커널 코드를 실행하려면 먼저 데이터 값을 정의하고 데이터 모양을 선언해야 합니다. 모든 데이터는 배열(사각형)로 정의되며, 임의의 순위(차원 수)를 포함하도록 배열을 정의할 수 있습니다. 데이터는 모든 차원의 모든 크기일 수 있습니다.

index 클래스

인덱스 클래스각 차원의 array 원점에서 하나의 개체로 오프셋을 캡슐화하여 또는 array_view 개체의 위치를 지정합니다. 배열의 위치에 액세스하는 경우 정수 인덱스 목록 대신 인덱싱 연산[]자에 개체를 전달 index 합니다. array::operator() 연산자 또는 array_view::operator() 연산자를 사용하여 각 차원의 요소에 액세스할 수 있습니다.

다음 예제에서는 1차원 개체의 세 번째 요소를 지정하는 1차원 array_view 인덱스를 만듭니다. 인덱스는 개체의 세 번째 요소를 인쇄하는 array_view 데 사용됩니다. 출력은 3입니다.

int aCPP[] = {1, 2, 3, 4, 5};
array_view<int, 1> a(5, aCPP);

index<1> idx(2);

std::cout << a[idx] << "\n";
// Output: 3

다음 예제에서는 2차원 개체에서 행 = 1과 열 = 2가 있는 요소를 지정하는 2차원 array_view 인덱스를 만듭니다. 생성자의 첫 번째 매개 변수 index 는 행 구성 요소이고 두 번째 매개 변수는 열 구성 요소입니다. 출력은 6입니다.

int aCPP[] = {1, 2, 3, 4, 5, 6};
array_view<int, 2> a(2, 3, aCPP);

index<2> idx(1, 2);

std::cout <<a[idx] << "\n";
// Output: 6

다음 예제에서는 3차원 개체에서 깊이 = 0, 행 = 1 및 열 = 3인 요소를 지정하는 3차원 array_view 인덱스를 만듭니다. 첫 번째 매개 변수는 깊이 구성 요소이고, 두 번째 매개 변수는 행 구성 요소이고, 세 번째 매개 변수는 열 구성 요소입니다. 출력은 8입니다.

int aCPP[] = {
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};

array_view<int, 3> a(2, 3, 4, aCPP);

// Specifies the element at 3, 1, 0.
index<3> idx(0, 1, 3);

std::cout << a[idx] << "\n";
// Output: 8

extent 클래스

extent 클래스또는 array_view 개체의 각 차원에 있는 데이터의 길이를 array 지정합니다. 익스텐트를 만들고 이를 사용하여 개체를 arrayarray_view 만들 수 있습니다. 기존 array 또는 array_view 개체의 익스텐트를 검색할 수도 있습니다. 다음은 개체의 각 차원에 있는 익스텐트 길이를 인쇄하는 예제입니다 array_view .

int aCPP[] = {
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
// There are 3 rows and 4 columns, and the depth is two.
array_view<int, 3> a(2, 3, 4, aCPP);

std::cout << "The number of columns is " << a.extent[2] << "\n";
std::cout << "The number of rows is " << a.extent[1] << "\n";
std::cout << "The depth is " << a.extent[0] << "\n";
std::cout << "Length in most significant dimension is " << a.extent[0] << "\n";

다음 예제에서는 이전 예제의 개체와 동일한 차원의 개체를 만들 array_view 지만 이 예제에서는 생성자에서 명시적 매개 변수를 사용하는 대신 개체를 array_view 사용합니다extent.

int aCPP[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24};
extent<3> e(2, 3, 4);

array_view<int, 3> a(e, aCPP);

std::cout << "The number of columns is " << a.extent[2] << "\n";
std::cout << "The number of rows is " << a.extent[1] << "\n";
std::cout << "The depth is " << a.extent[0] << "\n";

액셀러레이터로 데이터 이동: array 및 array_view

데이터를 가속기로 이동하는 데 사용되는 두 개의 데이터 컨테이너는 런타임 라이브러리에 정의됩니다. 배열 클래스array_view 클래스입니다. 클래스 array 는 개체가 생성될 때 데이터의 전체 복사본을 만드는 컨테이너 클래스입니다. array_view 클래스는 커널 함수가 데이터에 액세스할 때 데이터를 복사하는 래퍼 클래스입니다. 원본 디바이스에서 데이터가 필요한 경우 데이터가 다시 복사됩니다.

array 클래스

개체가 array 생성되면 데이터 집합에 대한 포인터가 포함된 생성자를 사용하는 경우 데이터의 전체 복사본이 가속기에서 만들어집니다. 커널 함수는 가속기에서 복사본을 수정합니다. 커널 함수 실행이 완료되면 데이터를 원본 데이터 구조로 다시 복사해야 합니다. 다음 예제에서는 벡터의 각 요소를 10으로 곱합니다. 커널 함수가 완료되면 vector conversion operator 데이터를 벡터 개체에 다시 복사하는 데 사용됩니다.

std::vector<int> data(5);

for (int count = 0; count <5; count++)
{
    data[count] = count;
}

array<int, 1> a(5, data.begin(), data.end());

parallel_for_each(
    a.extent,
    [=, &a](index<1> idx) restrict(amp) {
        a[idx] = a[idx]* 10;
    });

data = a;
for (int i = 0; i < 5; i++)
{
    std::cout << data[i] << "\n";
}

array_view 클래스

array_view 클래스와 멤버가 거의 동일 array 하지만 기본 동작은 동일하지 않습니다. 생성자에 전달된 array_view 데이터는 생성자와 마찬가지로 array GPU에 복제본(replica) 없습니다. 대신 커널 함수가 실행될 때 데이터가 가속기로 복사됩니다. 따라서 동일한 데이터를 사용하는 두 array_view 개체를 만드는 경우 두 개체 모두 array_view 동일한 메모리 공간을 참조합니다. 이렇게 하면 다중 스레드 액세스를 동기화해야 합니다. 클래스를 사용하는 array_view 기본 이점은 필요한 경우에만 데이터가 이동된다는 것입니다.

array와 array_view 비교

다음 표에서는 클래스와 클래스 간의 유사점과 차이점을 arrayarray_view 요약합니다.

설명 array 클래스 array_view 클래스
순위가 결정되는 경우 컴파일 시간에. 컴파일 시간에.
익스텐트 결정 시 런타임에. 런타임에.
도형 직사각형. 직사각형.
데이터 저장소 데이터 컨테이너입니다. 데이터 래퍼입니다.
복사 정의에서 명시적 및 심층 복사. 커널 함수에서 액세스할 때 암시적 복사입니다.
데이터 검색 배열 데이터를 CPU 스레드의 개체에 다시 복사합니다. 개체에 array_view 직접 액세스하거나 array_view::synchronize 메서드를 호출하여 원래 컨테이너의 데이터에 계속 액세스합니다.

배열 및 array_view 공유 메모리

공유 메모리는 CPU와 가속기 모두에서 액세스할 수 있는 메모리입니다. 공유 메모리를 사용하면 CPU와 가속기 간에 데이터를 복사하는 오버헤드가 제거되거나 크게 줄어듭니다. 메모리는 공유되지만 CPU와 가속기 둘 다에서 동시에 액세스할 수 없으므로 정의되지 않은 동작이 발생합니다.

array 개체를 사용하여 연결된 가속기가 지원하는 경우 공유 메모리 사용에 대한 세분화된 제어를 지정할 수 있습니다. 가속기가 공유 메모리를 지원하는지 여부는 공유 메모리가 지원되는 경우 반환 true 되는 가속기 supports_cpu_shared_memory 속성에 의해 결정됩니다. 공유 메모리가 지원되는 경우 가속기에서 메모리 할당에 대한 기본 access_type 열거형 은 속성에 의해 default_cpu_access_type 결정됩니다. 기본적으로 arrayarray_view 개체는 연결된 accelerator주 개체와 동일합니다access_type.

명시적으로 배열::cpu_access_type 데이터 멤버 속성을 array 설정하여 공유 메모리 사용 방법을 세밀하게 제어하여 계산 커널의 메모리 액세스 패턴에 따라 하드웨어의 성능 특성에 대해 앱을 최적화할 수 있습니다. 연결된 array_view 것과 동일하게 cpu_access_typearray 반영되거나, 데이터 원본 access_type 없이 array_view 생성되는 경우 먼저 스토리지를 할당하는 환경을 반영합니다. 즉, 호스트(CPU)에서 처음 액세스하는 경우 CPU 데이터 원본을 통해 생성된 것처럼 동작하고 캡처를 통해 연결된 데이터를 공유 access_typeaccelerator_view 합니다. 그러나 처음 액세스하는 accelerator_view경우 해당 데이터 원본을 accelerator_view 통해 array 생성된 것처럼 동작하고 'saccess_type를 공유array합니다.

다음 코드 예제에서는 기본 가속기가 공유 메모리를 지원하는지 여부를 확인한 다음 cpu_access_type 구성이 다른 여러 배열을 만드는 방법을 보여 줍니다.

#include <amp.h>
#include <iostream>

using namespace Concurrency;

int main()
{
    accelerator acc = accelerator(accelerator::default_accelerator);

    // Early out if the default accelerator doesn't support shared memory.
    if (!acc.supports_cpu_shared_memory)
    {
        std::cout << "The default accelerator does not support shared memory" << std::endl;
        return 1;
    }

    // Override the default CPU access type.
    acc.default_cpu_access_type = access_type_read_write

    // Create an accelerator_view from the default accelerator. The
    // accelerator_view inherits its default_cpu_access_type from acc.
    accelerator_view acc_v = acc.default_view;

    // Create an extent object to size the arrays.
    extent<1> ex(10);

    // Input array that can be written on the CPU.
    array<int, 1> arr_w(ex, acc_v, access_type_write);

    // Output array that can be read on the CPU.
    array<int, 1> arr_r(ex, acc_v, access_type_read);

    // Read-write array that can be both written to and read from on the CPU.
    array<int, 1> arr_rw(ex, acc_v, access_type_read_write);
}

데이터에 대해 코드 실행: parallel_for_each

parallel_for_each 함수는 가속기에서 array 실행하려는 코드를 개체의 array_view 데이터에 대해 정의합니다. 이 항목의 소개에서 다음 코드를 고려합니다.

#include <amp.h>
#include <iostream>
using namespace concurrency;

void AddArrays() {
    int aCPP[] = {1, 2, 3, 4, 5};
    int bCPP[] = {6, 7, 8, 9, 10};
    int sumCPP[5] = {0, 0, 0, 0, 0};

    array_view<int, 1> a(5, aCPP);
    array_view<int, 1> b(5, bCPP);
    array_view<int, 1> sum(5, sumCPP);

    parallel_for_each(
        sum.extent,
        [=](index<1> idx) restrict(amp)
        {
            sum[idx] = a[idx] + b[idx];
        }
    );

    for (int i = 0; i < 5; i++) {
        std::cout << sum[i] << "\n";
    }
}

이 메서드는 parallel_for_each 컴퓨팅 do기본 및 람다 식이라는 두 개의 인수를 사용합니다.

compute do기본 병렬 extent 실행을 위해 만들 스레드 집합을 정의하는 개체 또는 tiled_extent 개체입니다. 컴퓨팅의 각 요소에 대해 하나의 스레드가 생성됩니다기본. 이 경우 개체는 extent 1차원이며 5개의 요소가 있습니다. 따라서 5개의 스레드가 시작됩니다.

람다 식각 스레드에서 실행할 코드를 정의합니다. 캡처 절 [=]은 람다 식의 본문이 값으로 캡처된 모든 변수에 액세스(이 경우 a는 , bsum)에 액세스되도록 지정합니다. 이 예제에서 매개 변수 목록은 이름이 idx1차원 index 변수를 만듭니다. 첫 번째 스레드의 idx[0] 값은 0이며 각 후속 스레드에서 하나씩 증가합니다. C restrict(amp) ++ AMP에서 가속할 수 있는 C++ 언어의 하위 집합만 사용됨을 나타냅니다. 제한 한정자가 있는 함수에 대한 제한 사항은 restrict(C++ AMP)설명되어 있습니다. 자세한 내용은 람다 식 구문을 참조 하세요.

람다 식은 실행할 코드를 포함하거나 별도의 커널 함수를 호출할 수 있습니다. 커널 함수는 한정자를 restrict(amp) 포함해야 합니다. 다음 예제는 이전 예제와 동일하지만 별도의 커널 함수를 호출합니다.

#include <amp.h>
#include <iostream>
using namespace concurrency;

void AddElements(
    index<1> idx,
    array_view<int, 1> sum,
    array_view<int, 1> a,
    array_view<int, 1> b) restrict(amp) {
    sum[idx] = a[idx] + b[idx];
}

void AddArraysWithFunction() {

    int aCPP[] = {1, 2, 3, 4, 5};
    int bCPP[] = {6, 7, 8, 9, 10};
    int sumCPP[5] = {0, 0, 0, 0, 0};

    array_view<int, 1> a(5, aCPP);
    array_view<int, 1> b(5, bCPP);
    array_view<int, 1> sum(5, sumCPP);

    parallel_for_each(
        sum.extent,
        [=](index<1> idx) restrict(amp) {
            AddElements(idx, sum, a, b);
        }
    );

    for (int i = 0; i < 5; i++) {
        std::cout << sum[i] << "\n";
    }
}

가속 코드: 타일 및 장벽

타일링을 사용하여 추가 가속을 얻을 수 있습니다. 타일링은 스레드를 동일한 사각형 하위 집합 또는 타일로 나눕니다. 데이터 집합 및 코딩하는 알고리즘에 따라 적절한 타일 크기를 결정합니다. 각 스레드에 대해 전체 또는 타일을 기준으로 하는 데이터 요소의 전역 위치에 액세스하고 타일을 기준으로 로컬 위치에 액세스할 수 array_view 있습니다.array 인덱스 값을 전역에서 로컬로 변환하는 코드를 작성할 필요가 없으므로 로컬 인덱스 값을 사용하면 코드가 간소화됩니다. 타일링을 사용하려면 컴퓨팅에서 extent::tile 메서드를 호출하고기본 람다 식에서 parallel_for_each tiled_index 개체를 사용합니다.

일반적인 애플리케이션에서는 타일의 요소가 어떤 방식으로든 관련되어 있으며, 코드는 타일 전체에서 값에 액세스하고 추적해야 합니다. tile_static 키워드 키워드(keyword) 및 tile_barrier::wait 메서드를 사용하여 이 작업을 수행합니다. tile_static 키워드(keyword) 있는 변수는 전체 타일에 범위가 있으며 각 타일에 대해 변수 인스턴스가 만들어집니다. 변수에 대한 타일 스레드 액세스의 동기화를 처리해야 합니다. tile_barrier::wait 메서드타일의 모든 스레드가 호출tile_barrier::wait에 도달할 때까지 현재 스레드의 실행을 중지합니다. 따라서 tile_static 변수를 사용하여 타일 전체에 값을 누적할 수 있습니다. 그런 다음 모든 값에 액세스해야 하는 계산을 완료할 수 있습니다.

다음 다이어그램은 타일로 정렬된 샘플링 데이터의 2차원 배열을 나타냅니다.

Index values in a tiled extent.

다음 코드 예제에서는 이전 다이어그램의 샘플링 데이터를 사용합니다. 이 코드는 타일의 각 값을 타일의 값 평균으로 바꿉니다.

// Sample data:
int sampledata[] = {
    2, 2, 9, 7, 1, 4,
    4, 4, 8, 8, 3, 4,
    1, 5, 1, 2, 5, 2,
    6, 8, 3, 2, 7, 2};

// The tiles:
// 2 2    9 7    1 4
// 4 4    8 8    3 4
//
// 1 5    1 2    5 2
// 6 8    3 2    7 2

// Averages:
int averagedata[] = {
    0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0,
};

array_view<int, 2> sample(4, 6, sampledata);

array_view<int, 2> average(4, 6, averagedata);

parallel_for_each(
    // Create threads for sample.extent and divide the extent into 2 x 2 tiles.
    sample.extent.tile<2,2>(),
        [=](tiled_index<2,2> idx) restrict(amp) {
        // Create a 2 x 2 array to hold the values in this tile.
        tile_static int nums[2][2];

        // Copy the values for the tile into the 2 x 2 array.
        nums[idx.local[1]][idx.local[0]] = sample[idx.global];

        // When all the threads have executed and the 2 x 2 array is complete, find the average.
        idx.barrier.wait();
        int sum = nums[0][0] + nums[0][1] + nums[1][0] + nums[1][1];

        // Copy the average into the array_view.
        average[idx.global] = sum / 4;
    });

for (int i = 0; i <4; i++) {
    for (int j = 0; j <6; j++) {
        std::cout << average(i,j) << " ";
    }
    std::cout << "\n";
}

// Output:
// 3 3 8 8 3 3
// 3 3 8 8 3 3
// 5 5 2 2 4 4
// 5 5 2 2 4 4

수학 라이브러리

C++ AMP에는 두 개의 수학 라이브러리가 포함되어 있습니다. 동시성::p recise_math 네임스페이스의 배정밀도 라이브러리는 배정밀도 함수를 지원합니다. 또한 하드웨어에 대한 배정밀도 지원이 여전히 필요하지만 단정밀도 함수를 지원합니다. C99 사양(ISO/IEC 9899)을 준수합니다. 가속기는 전체 배정밀도를 지원해야 합니다. 가속기::supports_double_precision 데이터 멤버값을 검사 수행할지 여부를 확인할 수 있습니다. 동시성::fast_math 네임스페이스의 빠른 수학 라이브러리에는 다른 수학 함수 집합이 포함되어 있습니다. 피연산자만 float 지원하는 이러한 함수는 더 빠르게 실행되지만 배정밀도 수학 라이브러리의 함수만큼 정확하지는 않습니다. 함수는 amp_math.h> 헤더 파일에 포함<되며 모두 .로 restrict(amp)선언됩니다. cmath> 헤더 파일의 <함수는 네임스페이스와 네임스페이 precise_mathfast_math 스 모두로 가져옵니다. restrict 키워드(keyword) cmath> 버전과 C++ AMP 버전을 구분<하는 데 사용됩니다. 다음 코드는 컴퓨팅에 있는 각 값의 빠른 메서드를 사용하여 base-10 로그를 계산합니다기본.

#include <amp.h>
#include <amp_math.h>
#include <iostream>
using namespace concurrency;

void MathExample() {

    double numbers[] = { 1.0, 10.0, 60.0, 100.0, 600.0, 1000.0 };
    array_view<double, 1> logs(6, numbers);

    parallel_for_each(
        logs.extent,
        [=] (index<1> idx) restrict(amp) {
            logs[idx] = concurrency::fast_math::log10(numbers[idx]);
        }
    );

    for (int i = 0; i < 6; i++) {
        std::cout << logs[i] << "\n";
    }
}

그래픽 라이브러리

C++ AMP에는 가속 그래픽 프로그래밍을 위해 설계된 그래픽 라이브러리가 포함되어 있습니다. 이 라이브러리는 네이티브 그래픽 기능을 지원하는 디바이스에서만 사용됩니다. 메서드는 동시성::graphics 네임스페이스에 있으며 amp_graphics.h> 헤더 파일에 포함<됩니다. 그래픽 라이브러리의 주요 구성 요소는 다음과 같습니다.

UWP(유니버설 Windows 플랫폼) 앱

다른 C++ 라이브러리와 마찬가지로 UWP 앱에서 C++ AMP를 사용할 수 있습니다. 다음 문서에서는 C++, C#, Visual Basic 또는 JavaScript를 사용하여 만든 앱에 C++ AMP 코드를 포함하는 방법을 설명합니다.

C++ AMP 및 동시성 시각화 도우미

동시성 시각화 도우미에는 C++ AMP 코드의 성능 분석 지원이 포함되어 있습니다. 다음 문서에서는 다음과 같은 기능을 설명합니다.

성능 권장 사항

부호 없는 정수의 모듈러스 및 분할은 부호 있는 정수의 모듈러스 및 나누기보다 훨씬 더 나은 성능을 갖습니다. 가능한 경우 부호 없는 정수를 사용하는 것이 좋습니다.

참고 항목

C++ AMP(C++ Accelerated Massive Parallelism)
람다 식 구문
참조(C++ AMP)
네이티브 코드 블로그의 병렬 프로그래밍