Сообщение компилятора об ошибке

Опубликовано марта 6, 2018 в Технология CUDA

На первый взгляд кажется, что компилятор должен сообщить об ошибке, потому что мы присваиваем значение переменной tid, но нигде не объявляем переменную biockldx.

Однако объявлять переменную blockldx и не нужно; это одна из встроенных переменных, предоставляемых исполняющей средой CUDA. Более того, мы используем ее в полном соответствии с ее именем. Она содержит индекс того блока устройства, в котором исполняется текущий код.

Но почему, спросите вы, не просто biockldx? Почему biockldx. х? А потому что CUDA С позволяет определять двумерную группу блоков. Для двумерных задач, например операций над матрицами или обработки изображений, часто удобно использовать двумерную индексацию, чтобы не переходить каждый раз от линейных индексов к прямоугольным. Не переживайте, если вы не сталкивались с такими задачами; просто поверьте, что иногда двумерная индексация удобнее одномерной. Но никто не заставляет вас ей пользоваться. Не хотите — не надо: мы не обидимся.

При запуске ядра мы задали N в качестве числа параллельных блоков. Набор параллельных блоков называется сеткой (grid). Таким образом, мы говорим исполняющей среде, что хотим получить одномерную сетку из N блоков (скалярные значения считаются одномерными). Значение blockldx. х для нитей в этих блоках изменяется от 0 до N-1. Представьте себе четыре блока, каждый из которых исполняет один и тот же код, но значения blockldx. х в них разные. Вот как выглядит фактически исполняемый в этих блоках код после того, как исполняющая среда подставит вместо blockldx. х индекс соответствующего блока:

Сообщение компилятора об ошибке

Вспомните пример для CPU, с которого мы начали. Тогда для сложения двух векторов нам пришлось обходить элементы с индексами от 0 до N-1. Но поскольку исполняющая среда уже запустила ядро в N блоках и в каждом блоке представлен ровно один индекс, то практически вся работа уже сделана. А так как мы ленивы, то это не может не радовать. У нас остается время написать что-нибудь в свой блог, например о том, насколько мы ленивы.

И последний вопрос: зачем мы проверяем, что переменная tid меньше N? Она ведь обязана быть меньше N, потому что ядро запускалось так, что это условие непременно выполняется. Но стремление лениться имеет оборотную сторону -навязчивую боязнь того, что кто-то нарушит предположения, сделанные нами о поведении программы. А если предположения нарушены, то код перестает работать. А это извещения об ошибках, сидение допоздна в компании отладчика и вообще куча ненужной работы, стоящей между нами и нашим блогом. Если мы не проверим, что tid меньше N и из-за этого заберемся в чужую память, ничего хорошего не жди. На самом деле это, скорее всего, приведет к насильственному завершению ядра, потому что в GPU встроены изощренные схемы управления памятью, которые завершают процессы, нарушающие правила работы с памятью.

Если вы столкнетесь с подобной проблемой, то один из макросов HANDLE. ERROR (), которые мы так щедро рассыпали по всему коду, обнаружит ошибку и известит о том, что произошло. Как и в традиционном программировании на С, урок заключается в том, что если функция возвращает код ошибки, то тому есть причина. Хотя всегда хочется эти коды проигнорировать, мы очень хотим избавить вас от многочасовых страданий, которые мы претерпели, и потому настоятельно рекомендуем проверять исход любой операции, которая теоретически может завершиться неудачно. Часто бывает, что такие ошибки не прекращают выполнение программы, но почти наверняка вызывают непредсказуемые и крайне неблагоприятные последствия где-то в другом месте.

Итак, мы имеем код, параллельно исполняемый графическим процессором. Быть может, вы слышали, что это трудно или что для программирования задач общего назначения на GPU нужно разбираться в компьютерной графике. Надеемся, что теперь вы понимаете, насколько CUDA С упрощает написание параллельного кода для GPU. В этом примере мы складывали векторы длины 10. Если вам интересно посмотреть, как создается массивно-параллельное приложение, замените 10 в директиве «define N 10 на 10 ООО или 50 000, чтобы запустить десятки тысяч параллельно исполняемых блоков. Но имейте в виду: ни одна из размерностей массива блоков не должна превышать 65 535. Это ограничение налагается оборудованием, так что попытка запустить больше блоков приведет к ошибке. В следующей главе мы покажем, как обойти это ограничение.