Пошаговое руководство. Отладка приложения C++ AMP

В этом разделе показано, как отлаживать приложение, которое использует C++ Accelerated Massive Parallelism (C++ AMP), чтобы воспользоваться преимуществами графических устройств обработки данных (GPU).Он использует программу параллельного сокращения, которая суммирует большой массив целых чисел.В данном пошаговом руководстве рассмотрены следующие задачи:

  • Запуск отладчика GPU

  • Тестирование потоков GPU в Окне потоков GPU.

  • Использование окна "Параллельные стеки" для одновременного контроля стеков вызова нескольких потоков GPU.

  • Использование окна "Контроль параллельных данных" для проверки значений одного выражения между несколькими потоками в одно и то же время.

  • Маркировка, замораживание, размораживание и группировка потоков графического процессора.

  • Выполнение всех потоков в мозаике в определенном месте кода.

Обязательные компоненты

Перед началом данного пошагового руководства:

ПримечаниеПримечание

На вашем компьютере названия некоторых элементов интерфейса пользователя Visual Studio или их расположение могут отличаться от указанных в нижеследующих инструкциях. Это зависит от имеющегося выпуска Visual Studio и используемых параметров. Дополнительные сведения см. в разделе Параметры Visual Studio.

Создание примера проекта

  1. Запустите Visual Studio.

  2. В строке меню выберите Файл, Создать, Проект.

  3. В Установлено в области шаблонов выберите Visual C++.

  4. Выберите Консольное приложение Win32, тип AMPMapReduce в поле Имя и затем нажмите кнопку ОК.

  5. Нажмите кнопку Далее.

  6. Убериет флажок Предкомпилированный заголовок, а затем нажмите кнопку Готово.

  7. В Обозревателе решений удалите из проекта файлы stdafx.h, targetver.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. В строке меню выберите Построение, Построить решение.

Отладка кода ЦП

В этой процедуре будет использоваться локальный отладчик Windows, чтобы убедиться в правильности кода ЦП в этом приложении.Особенно интересным сегментом кода ЦП в этом приложении является цикл for в функции reduction_sum_gpu_kernel.Он контролирует параллельное сокращение на основе деревьев, которое выполняется на GPU.

Чтобы отладить код ЦП

  1. В Обозревателе решений откройте контекстное меню для AMPMapReduce, а затем выберите Свойства.

  2. В диалоговом окне Страницы свойств под Свойствами конфигурации выберите Отладка.Убедитесь, что в списке Загружаемый отладчик выбран Локальный отладчик Windows.

  3. Вернитесь к редактору кода.

  4. Установите точки останова в строках кода, показанных на следующем рисунке (приблизительно строки 67-70).

    Точки останова ЦП

    Точки останова ЦП

  5. В строке меню выберите Отладка, Начать отладку.

  6. В окне Локальные переменные наблюдайте значение stride_size, пока не будет достигнута точка останова в строке 70.

  7. В строке меню выберите Отладка, Остановить отладку.

Отладка кода GPU

В этом разделе показано, как выполнить отладку кода графического процессора, который является кодом, содержащимся в функции sum_kernel_tiled.Код графического процессора вычисляет сумму целых чисел для каждого «блока» параллельно.

Чтобы отладить код GPU

  1. В Обозревателе решений откройте контекстное меню для AMPMapReduce, а затем выберите Свойства.

  2. В диалоговом окне Страницы свойств под Свойствами конфигурации выберите Отладка.

  3. В списке Загружаемый отладчик выберите Локальный отладчик Windows.

  4. В списке Тип отладчика выберите Только GPU.

  5. Нажмите кнопку ОК.

  6. Задайте точку останова в строке 30, как показано на следующем рисунке.

    Точка останова GPU

    Точки останова GPU

  7. В строке меню выберите Отладка, Начать отладку.Точки останова в коде ЦП в строках 67 и 70 не выполняются во время отладки GPU, поскольку эти строки кода выполняются ЦП.

Чтобы использовать окно "Потоки GPU"

  1. Чтобы открыть окно "Потоки GPU", в строке меню выберите Отладка, Окна, Потоки GPU.

    Можно проверить состояние потоков GPU в появившемся окне "Потоки GPU".

  2. Закрепите окно потоки GPU в нижней части окна Visual Studio.Нажмите кнопку Развернуть переключатель потока для отображения текстовых полей мозаики и потока.Окно "Потоки GPU" показывает общее число активных и заблокированных потоков GPU, как показано на следующей иллюстрации.

    Окно "Потоки GPU"

    Окно "Потоки GPU" с 4 активными потоками

    Для этого вычисления выделено 313 мозаик.Каждая мозаика содержит 32 потока.Поскольку локальная отладка GPU происходит на эмуляторе программного обеспечения, существуют четыре активных потока GPU.Четыре потока одновременно выполняют инструкции и затем перемещаются к следующей инструкции.

    В окне "Потоки GPU" есть 4 активных и 28 заблокированных потоков GPU в операторе tile_barrier::wait, определенном около строки 21 (t_idx.barrier.wait();).Все 32 потока GPU принадлежат первой мозаике, tile[0].Стрелка указывает на строку, которая содержит текущий поток.Чтобы переключиться на другой поток, используйте один из следующих методов:

    • В строке для потока, на который нужно переключиться, в окне "Потоки GPU", откройте контекстное меню и выберите Переключиться на поток.Если строка представляет более одного потока, то произойдет переключение на первый поток в соответствии с координатами потока.

    • Введите значения потока и мозаики потока в соответствующие текстовые поля, а затем нажмите кнопку Переключить поток.

    В окне "Стек вызовов" отображается стек вызовов текущего потока GPU.

Чтобы использовать окно "Параллельные стеки"

  1. Чтобы открыть окно "Параллельные стеки" в строке меню выберите Отладка, Окна, Параллельные стеки.

    Можно использовать окно "Параллельные стеки" для одновременной проверки кадров стека нескольких потоков GPU.

  2. Закрепите окно "Параллельные стеки" в нижней части окна Visual Studio.

  3. Убедитесь, что в списке в верхнем левом углу выбрано значение Потоки.На следующей иллюстрации окно "Параллельные стеки" показывает представление сфокусированное на стеке вызовов потоков GPU, которые можно было увидеть в окне "Потоки GPU".

    Окно Параллельные стеки

    Окно "Параллельные стеки" с 4 активными потоками

    32 потока пошло из _kernel_stub в лямда-выражение в вызове функции parallel_for_each и затем к функции sum_kernel_tiled, где происходит параллельное сокращение.28 из 32 потоков продвинулись до оператора tile_barrier::wait и остаются заблокированными в строке 22, тогда как 4 потока остаются активными в функции sum_kernel_tiled в строке 30.

    Можно проверить свойства потока GPU, которые доступны в окне "Потоки GPU" в расширенных советах окна "Параллельные стеки".Чтобы сделать это, наведите указатель мыши на кадр стека sum_kernel_tiled.На следующем рисунке показаны советы.

    Советы о потоках GPU

    DataTip для окна "Параллельные стеки"

    Дополнительные сведения об окне "Параллельные стеки" см Использование окна "Параллельные стеки".

Чтобы использовать окно "Контроль параллельных данных"

  1. Чтобы открыть окно "Контроль параллельных данных" в строке меню выберите Отладка, Окна, Контроль параллельных данных, Параллельное контрольное значение 1.

    Окно "Контроль параллельных данных" можно использовать для проверки значений выражения между несколькими потоками.

  2. Закрепите окно "Параллельное контрольное значение 1" в нижней части окна Visual Studio.Имеются 32 строки в таблице окна "Контроль параллельных данных".Каждая строка соответствует потоку GPU, появившемуся и в окне "Потоки GPU", и в окне "Параллельные стеки".Теперь можно ввести выражение, значения которого требуется проверить по всем 32 потокам GPU.

  3. Выберите заголовок столбца Добавить контрольное значение, введите localIdxи нажмите клавишу ВВОД.

  4. Снова выберите заголовок столбца Добавить контрольное значение, введите globalIdxи нажмите клавишу ВВОД.

  5. Выберите заголовок столбца Добавить контрольное значение еще раз, введите localA localA[localIdx[0]], а затем нажмите клавиш ВВОД.

    Можно сортировать по указанному выражению, выбрав заголовок соответствующего столбца.

    Выберите заголовок столбца localA[localIdx[0]] для сортировки столбца.На следующем рисунке показаны результаты сортировки по localA [localIdx [0].

    Результаты сортировки

    Окно "Контроль параллельных данных" с отсортированными результатами

    Содержимое окна "Контроль параллельных данных" можно экспортировать в Excel, нажав кнопку «Excel», а затем выбрав Открыть в Excel.Если на компьютере разработчика установлена программа Excel при этом откроется лист Excel с содержимым окна.

  6. В правом верхнем углу окна "Контроль параллельных данных" имеется элемент управления фильтра, который можно использовать для фильтрации содержимого с помощью логических выражений.Введите в текстовое поле элемента управления фильтра localA[localIdx[0]] > 20000 , и нажмите клавишу ВВОД.

    Окно теперь содержит только те потоки, для которых значение localA[localIdx[0]] больше, чем 20000.Содержимое по-прежнему отсортировано по столбцу localA[localIdx[0]], что соответствует выполненной ранее сортировке.

Пометка потоков GPU

Можно пометить определенные потоки GPU путем пометки их в окне "Потоки GPU", окне "Контроль параллельных данных" или советах в окне "Параллельные стеки".Если строка в окне "Потоки GPU" содержит более одного потока, пометка этой строки помечает все потоки, которые содержатся в строке.

Чтобы пометить потоки GPU

  1. Выберите заголовок столбца [Поток] в окне "Параллельное контрольное значение 1", чтобы осуществить сортировку по индексу мозаики и индексу потока.

  2. В строке меню выберите Отладка, Продолжить, что приведет к выполнению четырех потоков, которые были активны, до следующего барьера (определено в строке 32 AMPMapReduce.cpp).

  3. Выберите символ флага слева от строки, содержащей четыре потока, которые сейчас активны.

    На следующей иллюстрации показаны четыре активных помеченных потока в окне "Потоки GPU".

    Активные потоки в окне "Потоки GPU"

    Окно "Потоки GPU" с помеченными потоками

    Окно "Параллельные контрольные значения" и советы окна "Параллельные стеки" оба показывают отмеченные потоки.

  4. Если нужно сосредоточить внимание на четырех потоках, которые были помечены, можно выбрать в окнах "Потоки GPU", "Контроль параллельных данных" и "Параллельные стеки", отображение только помеченных потоков.

    Нажмите кнопку "Показать только помеченные" в любом из окон или на панели инструментов Место отладки.На приведенном ниже рисунке показана кнопка "Показать только помеченные" на панели инструментов Место отладки.

    Кнопка "Показать только помеченные"

    Панель инструментов "Место отладки" со значком "Показать только помеченные"

    Теперь окна "Потоки GPU", "Контроль параллельных данных" и "Параллельные стеки" отображают только помеченные потоки.

Замораживание и размораживание потоков GPU

Можно заморозить (приостановить) и разморозить (возобновить) потоки GPU или из окна "Потоки GPU", или из окна "Контроль параллельных данных".Можно замораживать и размораживать потоки GPU одинаковыми способами; для сведений см. Практическое руководство. Использование окна потоков.

Чтобы заморозить и разморозить потоки GPU

  1. Нажмите кнопку Показывать только отмеченные для отображения всех потоков.

  2. В строке меню выберите Отладка, Продолжить.

  3. Откройте контекстное меню для активной строки и выберите Заморозить.

    Следующая иллюстрация окна "Потоки GPU" показывает, что все четыре потока зоморожены.

    Замороженные потоки в окне "Потоки GPU"

    Окно "Потоки GPU", в котором показаны замороженные потоки

    Аналогичным образом в окно "Контроль параллельных данных" показано, что заморожены все четыре потока.

  4. В строке меню выберите Отладка, Продолжить, чтобы разрешить следующим четырем потокам GPU продвинуться за барьер в строке 22 и достигнуть точки останова в строке 30.Окно "Потоки GPU" показывает, что четыре ранее замороженных потока остаются замороженными и в активном состоянии.

  5. В строке меню выберите Отладка, Продолжить.

  6. Из окна "Контроль параллельных данных" можно также разморозить один или несколько потоков GPU.

Группировка потоков GPU

  1. В контекстном меню для одного из потоков в окне Потоки GPU выберите Группировать по, Адрес.

    Потоки в окне "Потоки GPU" сгруппируются по адресу.Адрес соответствует инструкции в дизассемблированном коде, где расположена каждая группа потоков.24 потока находятся в строке 22, где выполняется Метод tile_barrier::wait.12 потоков находятся в инструкции для барьера в строке 32.Четыре из них помечены.8 потоков на точке останова в строке 30.Четыре из них заморожены.На следующей иллюстрации в окне "Потоки GPU" показаны сгруппированные потоки.

    Группированные потоки в окне "Потоки GPU"

    Окно "Потоки GPU" с потоками, сгруппированными по адресам

  2. Можно также выполнить операцию Группировать по, открыв контекстное меню для сетки данных в окне "Контроль параллельных данных", выбрав Группировать пои затем выбрав пункт меню, соответствующий нужному способу группировки потоков.

Выполнение всех потоков в определенное месте кода

Можно запустить все потоки заданной мозаики до строки, которая содержит курсор, с помощью Выполнить текущую мозаику до курсора.

Чтобы выполнить все потоки до места, помеченного курсором

  1. В контекстном меню для замороженных потоков выберите Разморозить.

  2. В редакторе кода поместите курсор на строку 30.

  3. В контекстном меню редактора кода выберите Выполнить текущую мозаику до курсора.

    24 потока, который ранее были блокированы на барьере на строке 21, выполнятся до строки 32.Это показано в окне Потоки GPU.

См. также

Задачи

Практическое руководство. Использование окна потоков GPU

Практическое руководство. Использование окна параллельных контрольных значений

Основные понятия

Общие сведения о C++ AMP

Другие ресурсы

Отладка кода GPU

Анализ кода C++ AMP с визуализатором параллелизма