Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
отчет по практике Баулина.doc
Скачиваний:
1
Добавлен:
01.04.2025
Размер:
1.98 Mб
Скачать

3.3 Расширение языка c

Перейдем непосредственно к рассмотрению тех добавлений, которые технология CUDA привнесла в язык С, и постараемся по возможности кратко и доступно рассказать о каждом из них.

Как уже упоминалось ранее, программы для CUDA (соответствующие файлы обычно имеют расширение .cu) пишутся на "расширенном" С/С++ и компилируются при помощи команды nvcc.

Сама технология CUDA вводит ряд дополнительных расширений для языка C, которые необходимы для написания кода для GPU:

  1. Спецификаторы функций, которые показывают, как и откуда буду выполняться функции.

  2. Спецификаторы переменных, которые служат для указания типа используемой памяти GPU.

  3. Спецификаторы запуска ядра GPU.

  4. Встроенные переменные для идентификации нитей, блоков и др. параметров при исполнении кода в ядре GPU .

  5. Дополнительные типы переменных.

Теперь рассмотрим каждое из этих расширений более детально.

Как было сказано, спецификаторы функций определяют, как и откуда буду вызываться функции. Всего в CUDA три таких спецификатора:

Спецификатор

Выполняется на

Может вызываться из

__device__

device

device

__global__

device

host

__host__

host

host

  • __host__ (по умолчанию)— функция, выполняемая на CPU, вызываемая с CPU (в принципе его можно и не указывать).

  • __global__ — функция, вызываемая с CPU и выполняемая потоками на GPU; означает ядро.

  • __device__ — функция, вызываемая (одним потоком) с GPU выполняется на GPU,

При этом спецификаторы __host__ и __device__ могут быть использованы вместе (это значит, что соответствующая функция может выполняться как на GPU, так и на CPU - соответствующий код для обеих платформ будет автоматически сгенерирован компилятором). В этом случае функции компилируются в двух видах.

При написании программы важно учитывать ограничения, накладывающиеся на функции, выполняемые на GPU, на __global__функции:

  • тип возвращаемого результата всегда void;

  • аргументы передаются через разделяемую/константную память;

  • не поддерживается переменное число входных аргументов;

  • не поддерживаются static-переменные внутри функции;

  • не поддерживается рекурсия;

  • указатели на __global__функции со стороны CPU поддерживаются;

на __device__функции:

  • не поддерживается переменное число входных аргументов;

  • не поддерживаются static-переменные внутри функции;

  • поддерживается рекурсия для устройств с вычислительными возможностями 2.0 и выше;

  • указатели на __ device __функции со стороны CPU поддерживаются для устройств с вычислительными возможностями 2.0 и выше, указатели со стоны CPU не поддерживаются;

  • встраиваются по умолчанию для устройств с вычислительными возможностями 1.х.

Для задания размещения в памяти GPU переменных используются следующие спецификаторы – __device__, __constant__ и __shared__.

__device__ объявляет переменную, размещаемую на устройстве в глобальном пространстве памяти. Время жизни этой переменной совпадает со временем жизни приложения. Доступ к ней может быть осуществлен из всех потоков на устройстве, а также с хоста через библиотеки времени выполнения.

__constant__ объявляет переменную, размещенную на устройстве в константной области памяти и доступную только для чтения.

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

На их использование также накладывается ряд ограничений:

  • эти спецификаторы не могут быть применены к полям структуры (struct или union);

  • соответствующие переменные могут использоваться только в пределах одного файла, их нельзя объявлять как extern;

  • запись в переменные типа __constant__ может осуществляться только CPU при помощи специальных функций;

  • __shared__ переменные не могут инициализироваться при объявлении

Так же стоит упомянуть о встроенных переменных, которые понадобятся при написании программы:

  • gridDim – переменная типа dim3, содержит размер гридa (решетки), выделенного при текущем вызове ядра.

  • blockDim – переменная типа dim3, содержит размер блока, выделенного при текущем вызове ядра.

  • blockIdx – переменная типа uint3, содержит индекс текущего блока внутри грида в вычислении на GPU.

  • threadIdx – переменная типа uint3, содержит индекс текущей нити в вычислении на GPU.

  • warpSize – переменная типа int, содержит размер варпа.

Помните, что данные переменные предназначены только для чтения. Следует заметить, что нет встроенной переменной для получения индекса потока среди всех потоков (т.е. всех потоков всех блоков). Но он может быть вычислен через значения других встроенных переменных. Если считать, что используется только х-компонента индексов, то искомый индекс idx находится по формуле

Idx=blockldx.x*blockDom.x+threadldx.x,

где blockldx.x*blockDom.x – смещение нулевого потока данного блока относительно нулевого потока нулевого блока, threadldx.x – смещение данного потока относительно нулевого потока данного блока.

Теперь поговорим о добавленных типах переменных. В язык вводятся одно- , двух- , трех- , четырех- мерные вектора из базовых типов – [u]char1[1..4], [u]short[1..4], [u]int[1..4], [u]long[1..4], float[1..4] и double2. Они являются структурами и обращение к компонентам вектора идет по именам - .x, .y, .z и .w. Для создания значений-векторов заданного типа служит конструкция вида make_<type name>.

Обратите внимание, что для этих типов не поддерживаются векторные покомпонентные операции, т.е. нельзя просто сложить два вектора при помощи оператора "+" - это необходимо явно делать для каждой компоненты.

Также для задания размерности служит тип dim3, основанный на типе uint3, но обладающий нормальным конструктором, инициализирующим все не заданные компоненты единицами, используется для задания числа блоков и потоков при вызове ядер.

Эти типы могут использоваться в хостовой (C/C++) части кода. Итак, мы рассмотрели расширения языка С и дали их краткую характеристику. Далее опишем наиболее важный набор функций, который поможет организовать совместную работу CPU и GPU. Более подробную информацию о предоставляемых CUDA возможностях можно найти в инструкции.