다음을 통해 공유


연습: C++ AMP 응용 프로그램 디버깅

이 항목에서는 C++ 가속 대규모 병렬 (C++ AMP)를 사용 하 여 그래픽 처리 장치 (GPU)를 활용 하는 응용 프로그램을 디버깅 하는 방법을 보여 줍니다.큰 정수 배열을 위로 합계를 구하는 병렬 감소 프로그램을 사용 합니다.이 연습에서는 다음 작업을 수행합니다.

  • GPU 디버거를 실행 합니다.

  • GPU 스레드 창에서 GPU 스레드를 검사 합니다.

  • 병렬 스택 창을 사용 하 여 동시에 여러 GPU 스레드 호출 스택을 볼.

  • 병렬 조사식 창을 사용 하 여 동시에 여러 스레드에서 단일 식의 값을 검사 합니다.

  • 플래그, 고정, 재개, 및 GPU 스레드를 그룹화 합니다.

  • 타일의 특정 위치에 모든 스레드 코드를 실행 합니다.

사전 요구 사항

이 연습을 시작 하기 전에:

  • C++ AMP 개요를 읽는 경우

  • 해당 줄이 있는지 확인 번호를 텍스트 편집기에 표시 됩니다.자세한 내용은 방법: 편집기에서 줄 번호 표시를 참조하십시오.

  • 실행 중인 있는지 확인 Windows 8 또는 Windows Server 2012 소프트웨어 에뮬레이터에서 디버깅을 지원 합니다.

[!참고]

다음 지침처럼 컴퓨터에서 Visual Studio 사용자 인터페이스 요소 일부에 대한 이름이나 위치를 다르게 표시할 수 있습니다. 이러한 요소는 사용하는 Visual Studio 버전 및 설정에 따라 결정됩니다. 자세한 내용은 Visual Studio 설정을 참조하십시오.

샘플 프로젝트를 만들려면

  1. Visual Studio를 시작합니다.

  2. 메뉴 표시줄에서 선택 파일, New, 프로젝트.

  3. 아래 설치 된 템플릿 창에서 선택 Visual C++.

  4. 선택 Win32 콘솔 응용 프로그램, 형식 AMPMapReduce 에 이름 을 선택한 다음 선택은 확인 단추.

  5. 다음 단추를 선택합니다.

  6. 지우기는 미리 컴파일된 헤더 확인란을 선택한 다음 선택 된 완료 단추.

  7. 솔루션 탐색기, targetver.h, stdafx.h, stdafx.cpp를 프로젝트에서 삭제 합니다.

  8. Ampmapreduce.cpp를 열고 내용을 다음 코드로 대체 합니다.

    // AMPMapReduce.cpp defines the entry point for the program.
    // The program performs a parallel-sum reduction that computes the sum of an array of integers. 
    
    #include <stdio.h>
    #include <tchar.h>
    #include <amp.h>
    
    const int BLOCK_DIM = 32;
    
    using namespace concurrency;
    
    void sum_kernel_tiled(tiled_index<BLOCK_DIM> t_idx, array<int, 1> &A, int stride_size) restrict(amp)
    {
        tile_static int localA[BLOCK_DIM];
    
        index<1> globalIdx = t_idx.global * stride_size;
        index<1> localIdx = t_idx.local;
    
        localA[localIdx[0]] =  A[globalIdx];
    
        t_idx.barrier.wait();
    
        // Aggregate all elements in one tile into the first element.
        for (int i = BLOCK_DIM / 2; i > 0; i /= 2) 
        {
            if (localIdx[0] < i) 
            {
    
                localA[localIdx[0]] += localA[localIdx[0] + i];
            }
    
            t_idx.barrier.wait();
        }
    
        if (localIdx[0] == 0)
        {
            A[globalIdx] = localA[0];
        }
    }
    
    int size_after_padding(int n)
    {
        // The extent might have to be slightly bigger than num_stride to 
        // be evenly divisible by BLOCK_DIM. You can do this by padding with zeros.
        // The calculation to do this is BLOCK_DIM * ceil(n / BLOCK_DIM)
        return ((n - 1) / BLOCK_DIM + 1) * BLOCK_DIM;
    }
    
    int reduction_sum_gpu_kernel(array<int, 1> input) 
    {
        int len = input.extent[0];
    
        //Tree-based reduction control that uses the CPU.
        for (int stride_size = 1; stride_size < len; stride_size *= BLOCK_DIM) 
        {
            // Number of useful values in the array, given the current
            // stride size.
            int num_strides = len / stride_size;  
    
            extent<1> e(size_after_padding(num_strides));
    
            // The sum kernel that uses the GPU.
            parallel_for_each(extent<1>(e).tile<BLOCK_DIM>(), [&input, stride_size] (tiled_index<BLOCK_DIM> idx) restrict(amp)
            {
                sum_kernel_tiled(idx, input, stride_size);
            });
        }
    
        array_view<int, 1> output = input.section(extent<1>(1));
        return output[0];
    }
    
    int cpu_sum(const std::vector<int> &arr) {
        int sum = 0;
        for (size_t i = 0; i < arr.size(); i++) {
            sum += arr[i];
        }
        return sum;
    }
    
    std::vector<int> rand_vector(unsigned int size) {
        srand(2011);
    
        std::vector<int> vec(size);
        for (size_t i = 0; i < size; i++) {
            vec[i] = rand();
        }
        return vec;
    }
    
    array<int, 1> vector_to_array(const std::vector<int> &vec) {
        array<int, 1> arr(vec.size());
        copy(vec.begin(), vec.end(), arr);
        return arr;
    }
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        std::vector<int> vec = rand_vector(10000);
        array<int, 1> arr = vector_to_array(vec);
    
        int expected = cpu_sum(vec);
        int actual = reduction_sum_gpu_kernel(arr);
    
        bool passed = (expected == actual);
        if (!passed) {
            printf("Actual (GPU): %d, Expected (CPU): %d", actual, expected);
        }
        printf("sum: %s\n", passed ? "Passed!" : "Failed!"); 
    
        getchar();
    
        return 0;
    }
    
  9. 메뉴 표시줄에서 선택 파일, 모두 저장.

  10. 솔루션 탐색기, 바로 가기 메뉴 열기 AMPMapReduce, 다음을 선택 하 고 속성.

  11. 속성 페이지 대화 상자에서 구성 속성, 선택 C/C++, 미리 컴파일된 헤더.

  12. 미리 컴파일된 헤더 속성을 컴파일된 헤더 사용 안, 다음 선택은 확인 단추.

  13. 메뉴 표시줄에서 선택 빌드, 솔루션 빌드.

CPU 코드 디버깅

이 절차에서는 CPU 코드가이 응용 프로그램에서 정확한 지 확인 하려면 로컬 Windows 디버거를 사용 합니다.특히 흥미로운 것이 응용 프로그램의 CPU 코드 세그먼트입니다는 for 에서 루프의 reduction_sum_gpu_kernel 함수.GPU에서 실행 되는 트리 기반 병렬 감소를 제어 합니다.

CPU 코드를 디버깅 하려면

  1. 솔루션 탐색기, 바로 가기 메뉴 열기 AMPMapReduce, 다음을 선택 하 고 속성.

  2. 속성 페이지 대화 상자에서 구성 속성, 선택 디버깅.확인 로컬 Windows 디버거 선택 되어 있는 디버거를 실행 하 여 목록.

  3. 코드 편집기로 돌아갑니다.

  4. (약 67 줄 줄 70) 다음 그림에 표시 된 코드 줄에 중단점을 설정 합니다.

    CPU 중단점

    CPU 중단점

  5. 메뉴 모음에서 디버그, 디버깅 시작을 선택합니다.

  6. 지역 창에서 값에 대 한 관찰 stride_size 70 줄에 중단점에 도달할 때까지.

  7. 메뉴 표시줄에서 선택 디버깅, 디버깅 중지.

GPU 코드 디버깅

이 섹션에서는 GPU 코드 포함 된 코드를 디버깅 하는 방법을 보여 줍니다.에 sum_kernel_tiled 함수.GPU 코드 병렬로 각 "블록"에 대 한 정수의 합을 계산합니다.

GPU 코드를 디버깅 하려면

  1. 솔루션 탐색기, 바로 가기 메뉴 열기 AMPMapReduce, 다음을 선택 하 고 속성.

  2. 속성 페이지 대화 상자에서 구성 속성, 선택 디버깅.

  3. 디버거를 실행 하 여 목록에서 로컬 Windows 디버거.

  4. 디버거 형식 목록에서 GPU만.

  5. 확인 단추를 선택합니다.

  6. 다음 그림과 같이, 30 줄에 중단점을 설정 합니다.

    GPU 중단점

    GPU 중단점

  7. 메뉴 모음에서 디버그, 디버깅 시작을 선택합니다.중단점 코드 67과 70 줄에 CPU GPU 해당 줄의 코드는 CPU에서 실행 되므로 디버깅 하는 동안 실행 되지 않습니다.

GPU 스레드 창 사용

  1. GPU 스레드 창에서 메뉴 모음을 선택 디버깅, Windows, GPU 스레드.

    GPU 나타나는 GPU 스레드 창에서 스레드 상태를 검사할 수 있습니다.

  2. GPU 스레드 창 Visual Studio 아래쪽에 도킹 합니다.선택의 스레드 스위치 확장 타일 및 스레드 텍스트 상자를 표시 하는 단추입니다.GPU 스레드 창 총 활성 및 차단 된 스레드를 GPU 다음 그림과 같이 표시 합니다.

    GPU 스레드 창

    4개의 활성 스레드가 있는 GPU 스레드 창

    313 타일 할당에 대 한이 계산 됩니다.각 타일 32 개의 스레드를 포함 합니다.GPU 로컬 디버깅 소프트웨어 에뮬레이터에서 발생 하기 때문에 네 개의 활성 GPU 스레드입니다.4 개의 스레드 지침을 동시에 실행 하 고 함께 다음 명령으로 이동 합니다.

    GPU 스레드 창에는 네 개의 GPU 스레드 활성 및 28 GPU 스레드 차단에 tile_barrier::wait 문 21 줄에 대 한 정의 (t_idx.barrier.wait();).32 모든 GPU 스레드 첫 번째 타일에 속하는 tile[0].현재 스레드가 포함 된 행에 있는 화살표를 가리킵니다.다른 스레드로 전환 하려면 다음 방법 중 하나를 사용 하십시오.

    • 행 전환 GPU 스레드 창에서 스레드에 대 한 바로 가기 메뉴를 열고 선택 스레드 전환.두 개 이상의 스레드가 있는 행을 나타내는 경우 첫 번째 스레드가 스레드 좌표에 따라 전환 됩니다.

    • 타일 및 스레드 값의 스레드가 해당 텍스트 상자에 입력 한 다음 선택 된 스레드 스위치 단추.

    호출 스택 창에서 현재 GPU 스레드의 호출 스택을 표시합니다.

병렬 스택 창을 사용 하 여

  1. 병렬 스택 창에서 메뉴 모음을 선택 디버깅, Windows, 병렬 스택.

    병렬 스택 창을 동시에 여러 GPU 스레드의 스택 프레임을 검사할 수 있습니다.

  2. 아래쪽에 Visual Studio 병렬 스택 창을 도킹 합니다.

  3. 스레드 왼쪽 위 모서리에 있는 목록에서 선택 합니다.다음 그림에서는 병렬 스택 창 GPU 스레드 창에서 보았던 GPU 스레드 포커스가 호출 스택 보기를 보여 줍니다.

    병렬 스택 창

    4개의 활성 스레드가 있는 병렬 스택 창

    32 개의 스레드를 단계에서 _kernel_stub 람다 문에 parallel_for_each 함수를 호출 할는 sum_kernel_tiled 함수를 병렬 감소 발생 하는 위치.한 28 32 스레드를 진행 하는 tile_barrier::wait 문 및 4 스레드가 활성으로 유지 되는 반면, 22 줄에 차단 되어 있는 sum_kernel_tiled 30 줄에 함수.

    스레드 GPU의 GPU 스레드 창 풍부한 DataTip 병렬 스택 창에서 사용할 수 있는 속성을 검사할 수 있습니다.이 위해서는 스택 프레임에 마우스 포인터를 놓으면 sum_kernel_tiled.다음 그림에서는 Datatip을 보여 줍니다.

    DataTip GPU 스레드

    병렬 스택용 DataTip 창

    병렬 스택 창에 대 한 자세한 내용은 병렬 스택 창 사용.

병렬 조사식 창을 사용 하려면

  1. 병렬 조사식 창에서 메뉴 모음을 선택 디버깅, Windows, 병렬 조사식, 병렬 조사식 1.

    여러 스레드 간에 식의 값을 검사 하 여 병렬 조사식 창을 사용할 수 있습니다.

  2. 병렬 조사식 1 창 Visual Studio 아래쪽에 도킹 합니다.병렬 조사식 창에서 표에 있는 32 행이 있습니다.각 GPU 스레드 GPU 스레드 창과 병렬 스택 창에 나타나는 해당 합니다.이제 값 32 GPU 스레드 간에 모든 검사 하려면 표현식을 입력할 수 있습니다.

  3. 선택은 조사식 추가 열 머리글을 입력 localIdx, 다음 Enter 키를 선택 합니다.

  4. 선택 된 조사식 추가 유형, 열 머리글을 다시 globalIdx, 다음 Enter 키를 선택 합니다.

  5. 선택은 조사식 추가 유형, 열 머리글을 다시 localA [localIdx [0], 다음 Enter 키를 선택 합니다.

    지정 된 식에는 해당 열 머리글을 선택 하 여 정렬할 수 있습니다.

    선택 된 localA [localIdx [0] 열을 정렬 하려면 열 머리글입니다.다음 그림에서 정렬 결과 보여 줍니다. [localIdx [0]] localA.

    결과 정렬

    정렬된 결과가 포함된 병렬 조사식 창

    Excel 단추를 선택 하 고 다음을 선택 하 여 콘텐츠 병렬 조사식 창에서 Excel로 내보낼 수 있습니다 Excel에서 열기.Excel 개발 컴퓨터에 설치 되어 있는 경우이 콘텐츠를 포함 하는 Excel 워크시트를 엽니다.

  6. 병렬 조사식 창의 오른쪽 위 모서리에 있는 부울 식을 사용 하 여 콘텐츠를 필터링 하는 데 사용할 수 있는 필터 컨트롤이입니다.입력 localA [localIdx [0]] > 20000 필터 컨트롤 텍스트에서 상자 및 다음 Enter 키를 선택 합니다.

    스레드입니다만 이제는 창에 포함 된 localA[localIdx[0]] 값이 20000 보다 큰.여전히 콘텐츠 정렬 된는 localA[localIdx[0]] 앞서 수행한 정렬 작업은 열.

GPU 스레드에 플래그를 지정합니다.

GPU 스레드 창, 병렬 조사식 창 또는 DataTip 병렬 스택 창에 표시 하 여 GPU의 특정 스레드를 표시할 수 있습니다.GPU 스레드 창에서 행 두 개 이상의 스레드가 포함 된 경우 플래그를 지정 하면 해당 행의 행에 포함 된 모든 스레드 플래그입니다.

GPU 스레드에 플래그를 지정 합니다.

  1. 선택은 [스레드] 정렬 바둑판식 배열 인덱스 한 스레드 인덱스 병렬 조사식 1 창에서 열 머리글 있습니다.

  2. 메뉴 표시줄에서 선택 디버깅, 계속, 4 개의 스레드를 일으키는 다음 장애물 (AMPMapReduce.cpp 32 줄에서 정의)를 활성화 된.

  3. 지금 활성화 된 네 개의 스레드를 포함 하는 행의 왼쪽에 있는 플래그 기호를 선택 합니다.

    다음 그림 GPU 스레드 창에서 활성 플래그가 지정 된 스레드 네 개를 보여 줍니다.

    GPU 스레드 창에서 활성 스레드

    플래그 지정된 스레드가 있는 GPU 스레드 창

    병렬 조사식 창과 DataTip 병렬 스택 창 두 플래그가 지정 된 스레드를 나타냅니다.

  4. 4 개의 스레드에서 플래그를 찾으려는 경우, 플래그가 지정 된 스레드만 GPU 스레드, 병렬 감시 및 병렬 스택 창에 표시 하도록 선택할 수 있습니다.

    플래그가 지정 된 항목만 표시 단추 하나에 windows 또는 선택 된 디버그 위치 도구 모음.다음 그림에서 플래그가 지정 된 항목만 표시 단추를 보여 줍니다 있는 디버그 위치 도구 모음.

    플래그가 지정 된 단추만 표시

    플래그 설정된 항목만 표시 아이콘이 있는 디버그 위치 도구 모음

    이제 스레드 GPU 병렬 시청 하 고 병렬 스택 windows 플래그가 지정 된 스레드만 표시 합니다.

GPU 스레드 중지 및 재개

고정 시킬 수 있습니다 (중단) 및 재개 (resume) GPU 스레드 GPU 스레드 창 또는 병렬 조사식 창에서.고정 하 고는 CPU 스레드를 고정 해제 합니다. 에 대 한 내용은 방법: 스레드 창 사용.

고정 하 고 GPU 스레드 재개

  1. 선택은 플래그가 지정 된 항목만 표시 모든 스레드를 표시 하는 단추입니다.

  2. 메뉴 표시줄에서 선택 디버깅, 계속.

  3. 현재 행에 대 한 바로 가기 메뉴를 열고 선택 고정.

    모든 4 개의 스레드를 고정 다음 그림의 GPU 스레드 창 보여 줍니다.

    고정된 스레드 GPU 스레드 창

    고정된 스레드를 표시하는 GPU 스레드 창

    마찬가지로, 모든 4 개의 스레드를 고정 병렬 조사식 창을 보여 줍니다.

  4. 메뉴 표시줄에서 선택 디버깅, 계속 다음 네 개의 GPU 스레드 22 줄 장애물을 지나서 진행 하 고 30 줄에 중단점을 연결할 수 있도록 합니다.GPU 스레드 창 이전에 고정 된 네 개의 스레드를 계속 고정 및 현재 상태를 표시 합니다.

  5. 메뉴 표시줄에서 선택 디버깅, 계속.

  6. 병렬 조사식 창에서 개인 또는 다중 GPU 스레드 재개 수 있습니다.

GPU 스레드 그룹

  1. 스레드 중 하나에 대 한 바로 가기 메뉴의 GPU 스레드 창에서 선택 Group By, 주소.

    GPU 스레드 창에서 스레드 주소에 의해 그룹화 됩니다.각 그룹의 스레드 위치한 디스어셈블리 명령 주소에 해당 합니다.24 스레드는 22 줄에 위치는 tile_barrier::wait 메서드 실행 됩니다.12 스레드 줄 32에 장애물에 대 한 명령입니다.이러한 스레드 4 플래그가 지정 됩니다.8 개의 스레드로 30 줄에 중단점입니다.4 개의 스레드를 고정 됩니다.다음 그림에서는 GPU 스레드 창에서 그룹화 된 스레드를 보여 줍니다.

    그룹화 된 스레드 GPU 스레드 창

    주소로 그룹화된 스레드가 있는 GPU 스레드 창

  2. 수행할 수도 있습니다는 Group By 병렬 조사식 창의 데이터 표의 바로 가기 메뉴를 열어 작업 선택 Group By, 다음 해당 스레드를 그룹화 할 수 있는 메뉴 항목을 선택 합니다.

모든 스레드를 실행 하 여 코드의 특정 위치에

커서를 사용 하 여 포함 된 줄에 주어진된 타일에 모든 스레드를 실행 실행 현재 타일에 커서.

모든 스레드를 실행 하 여 커서에 표시 된 위치에

  1. 중지 된 스레드에 대 한 바로 가기 메뉴에서 선택 재개.

  2. 코드 편집기에서 30 줄에 커서를 놓습니다.

  3. 코드 편집기의 바로 가기 메뉴에서 선택 커서를 현재 타일 실행.

    21 줄에서 장애물에 이전에 차단 된 스레드 24 32 줄으로 진행 했습니다.이 표시 되는 GPU 스레드 창.

참고 항목

작업

방법: GPU 스레드 창 사용

방법: 병렬 조사식 창 사용

개념

C++ AMP 개요

기타 리소스

GPU 코드 디버깅

C + + AMP 동시성 시각화 도우미 코드 분석