Главная страница
Навигация по странице:

  • Архитектура видеокарт NVidia

  • GeForce 8800 Ultra GeForce 8800 GTX GeForce 8800 GTS GeForce 8800 GT GeForce 8800 GS

  • Частота памяти (МГц) 1080 900 800 900 800 Объем памяти 768MB 768MB 640MB и 320MB 512MB 384MB Интерфейс памяти

  • Типы видеокарт, поддерживающих CUDA

  • Функция cudaGetDeviceProperties()

  • Глава 3. Простейшая программа на CUDA

  • Замер времени работы части кода программы

  • ТЕХНОЛОГИЯ ПРОГРАММИРОВАНИЯ CUD. Учебное пособие казань 2017 2 удк 004. 4, 681. 3 Ббк 32. 81 Печатается по постановлению Редакционноиздательского совета


    Скачать 1.28 Mb.
    НазваниеУчебное пособие казань 2017 2 удк 004. 4, 681. 3 Ббк 32. 81 Печатается по постановлению Редакционноиздательского совета
    АнкорТЕХНОЛОГИЯ ПРОГРАММИРОВАНИЯ CUD
    Дата22.09.2022
    Размер1.28 Mb.
    Формат файлаpdf
    Имя файлаTumakov___Tekhnologiya_programmirovaniya_CUDA.pdf
    ТипУчебное пособие
    #690313
    страница2 из 6
    1   2   3   4   5   6
    Глава 2. Свойства устройства
    В этом разделе опишем, какие существуют видеокарты, и приведем характеристики некоторых из них. Также рассмотрим функцию, благодаря которой можно узнать параметры и характеристики видеокарты, установленной непосредственно на устройстве.
    Архитектура видеокарт NVidia
    Рассмотрим подробнее физическое устройство видеокарт. На рис. 4 приведена схема архитектуры видеокарты GeForce 8800 GT. Центральный процессор обменивается данными с GPU через мост (Bridge). Вычислительная часть видеокарты состоит из семи текстурных процессорных кластеров (Texture
    Processor Cluster), каждый из которых содержит два мультипроцессора (SM), блок текстур (Texture Unit) и совмещенный текстурный кэш и кэш первого уровня (Tex/L1).
    Рис. 4. Архитектура видеокарты GeForce 8800 GT

    17
    Блоки текстур осуществляют выборку и фильтрацию текстурных данных, необходимых для построения сцены. Число текстурных блоков в видеочипе определяет текстурную производительность и скорость выборки из текстур.
    Текстурная производительность является одним из важнейших параметров видеочипов. Особое влияние этот параметр оказывает на скорость при использовании трилинейной и анизотропной фильтраций, требующих дополнительных текстурных выборок.
    Мультипроцессор, в свою очередь, содержит 8 потоковых процессоров
    (Streaming Processors) или ядер CUDA (CUDA cores). Таким образом, общее число SP = 14 * 8 = 112. Также каждый SM содержит по два блока для вычисления специальных (трансцендентных) функций (SFU), таких как синус, косинус и т.д. В состав SM входит разделяемая память (Shared Memory), блок инструкций нитей (Multithreaded Instruction Unit), константный кэш (C-Cache) и кэш инструкций (L-Cache).
    Таблица 2. Сравнительные характеристики видеокарт GF 8800
    GeForce
    8800 Ultra
    GeForce
    8800 GTX
    GeForce
    8800 GTS
    GeForce
    8800 GT
    GeForce
    8800 GS
    Stream
    процессоры
    128 128 96 112 96
    Частота ядра
    (МГц)
    612 575 500 600 550
    Частота
    шейдерного
    блока (МГц)
    1500 1350 1200 1500 1375
    Частота памяти
    (МГц)
    1080 900 800 900 800
    Объем памяти
    768MB
    768MB
    640MB и
    320MB
    512MB
    384MB
    Интерфейс
    памяти
    384-bit
    384-bit
    320-bit
    256-bit
    192-bit
    Полоса
    пропускания
    памяти (ГБ/с)
    103.7 86.4 64 57.6 38.4

    18
    Вычислительная часть устройства соединяется с видеопамятью (DRAM) посредством кэша второго уровня (L2) и блоков операций растеризации (ROP).
    Блоки опреаций растеризации осуществляют операции записи рассчитанных видеокартой пикселей в буферы и операции их смешивания (блендинга).
    Производительность блоков ROP влияет на филлрейт (cкорость заполнения пикселами), а это является одной из основных характеристик видеокарт.
    Рис. 5. Архитектура нового мультипроцессора семейства Volta GV100

    19
    Надо иметь в виду, что видеокарты одной линейки имеют одинаковую производительность, и, следовательно, одинаковое число SP в MP. Однако остальные характеристики могут отличаться. В таблице 2 приведены характеристики для GeForce 8800.
    Рассмотрим также новый, недавно анонсированный, мультипроцессор семейства Volta (Рис. 5). Каждый SM содержит четыре блока, в которых есть помимо ядер выполняющих целочисленные вычисления, ядра для работы с вещественными 32 и 64-битными числами, а также новые, появившиеся в данной линейке процессоров, тензорные ядра (Tensor Cores), разработанные специально для обучения нейросетей в задачах глубокого обучения.
    На Рис. 5 таже представлены планировщик варпов (Warp Scheduler), блок выборки (Dispatch Unit), регистры мультипроцессора (Register File) и блоки загрузки и сохранения (LD/ST – Load/Store Units).
    Типы видеокарт, поддерживающих CUDA
    Приведем в таблице 1 характеристики некоторых видеокарт, поддерживающих технологию CUDA.
    Первая колонка таблицы – название видеокарты: указывается модель и объем памяти, в названии GT обозначает обычные видеокарты низкого уровня производительности, GTX обозначает видеоадаптеры среднего и высокого уровня, Ti или Titan обозначает десктопные карты.
    Вторая колонка обозначает маркировку кристаллов: GT – Tesla, GF –
    Fermi, GK – Kepler, GM – Maxwell, GP – Pascal. Третья и четверая колонки фактически показывают производительность видеокарты. Цифры в последней колонке, разрядности шины памяти, указывают на пропускную способность памяти.
    Отметим, что частота ядра, как правило, немного ниже частот шейдерных блоков. Частота же памяти в современных видеокартах существенно выше частоты ядра.

    20
    Таблица 1. Характеристики некоторых видеокарт
    GeForce
    GPU / кристалл
    Кол-во процессоров- ядер / текстурных блоков / ROP – блоков растеризации
    Частота ядра
    GPU / шейдерного блока / памяти в
    MHz.
    Разрядность шины пямяти
    Titan X 12gb
    GP102 3584/224/96 1417/1530/10000 384 bit
    GTX 1080 8gb
    GP104 2560/160/64 1607/1733/10000 256 bit
    GTX 1060 6gb
    GP106 1280/80/48 1506/1709/8000 192 bit
    GTX 980 Ti 4gb
    GM200 2816/176/96 1000/1075/7000 384 bit
    GTX 980 4 gb
    GM204 2048/104/64 1126/1216/7000 256 bit
    GTX 960 2 gb
    GM206 1024/64/32 1126/1178/7000 128 bit
    Titan Z 12gb
    2xGK110 5760/448/96 705/876/7000 2×384
    GTX 780 Ti 3gb
    GK110 2880/240/48 875/928/7000 384 bit
    GTX 760 2 gb
    GK104 1152/96/32 980/1033/6008 256 bit
    GTX 750 1 gb
    GM107 512/32/16 1020/1085/5000 128 bit
    GTX 680 2 gb
    GK104 1538/128/32 1006/1058/6008 256 bit
    GTX 660 2 gb
    GK106 960/80/24 980/1033/6008 192 bit
    GT 640 2 gb
    GK107 384/32/16 900/900/1784 128 bit
    GT 630 1 gb
    GF108 96/16/4 810/1620/3200 128 bit
    GT 610 1 gb
    GF119 48/8/4 810/1620/1800 64 bit
    GTX 580 1.5 gb
    GF110 512/64/48 772/1544/4008 384 bit
    GTX 480 1.5 gb
    GF100 480/60/48 701/1402/3700 384 bit
    GTX 560 1 gb
    GF114 336/56/32 675/1620/4004 256 bit
    GTX 460 1 gb
    GF104 336/56/32 675/1350/3600 256 bit
    GTX 550 Ti 1gb
    GF116 192/32/24 900/1800/4100 192 bit
    GTS 450 1 gb
    GF106 192/32/16 783/1566/3600 128 bit
    GT 440 1 gb
    GF108 96/16/4 810/1620/3200 128 bit
    GT 520 1 gb
    GF119 48/8/4 810/1620/1800 64 bit
    GTX 280 1 gb
    GT200 240/80/32 602/1296/2215 512 bit
    GTS 250 1 gb
    G92b
    128/64/16 738/1836/2200 256 bit
    GT 240 1 gb
    GT215 96/32/8 550/1340/3400 128 bit
    GT 220 1 gb
    GT216 48/16/8 625/1360/1580 128 bit
    GT 210 1 gb
    GT218 16/8/4 589/1402/1000 64 bit
    9800 GT 1 gb
    G92 112/56/16 600/1500/1800 256 bit
    9600 GT 1 gb
    G94 64/32/16 650/1625/1800 128 bit
    9500 GT 1 gb
    G96 32/16/8 550/1400/1600 128 bit
    8400 GS 512 mb
    G86 16\8\4 450/900/800 64 bit

    21
    Функция cudaGetDeviceProperties()
    Получим от видеокарты информацию о ее возможностях и разберемся, что они дают. Для определения свойств видеокарты используется функция cudaGetDeviceProperties(), принимающая в качестве параметра номер видеокарты и возвращающая в структуре cudaDeviceProp интересующие нас значения. Выведем на экран полученные переменные.
    Приведем основные переменные структуры cudaDeviceProp: struct cudaDeviceProp { char name[256]; size_t totalGlobalMem; size_t sharedMemPerBlock; int regsPerBlock; int warpSize; size_t memPitch; int maxThreadsPerBlock; int maxThreadsDim[3]; int maxGridSize[3]; size_t totalConstMem; int clockRate; int deviceOverlap; int multiProcessorCount; int kernelExecTimeoutEnabled; int integrated; int canMapHostMemory; int concurrentKernels;

    }
    Здесь:

    name[256] представляет собой ASCII строку идентификации устройства;

    totalGlobalMem – общий объем глобальной памяти, доступной на устройстве в байтах;

    22

    sharedMemPerBlock – максимальный объем общей памяти, доступной для нити блока в байтах;

    regsPerBlock – максимальное количество 32-битных регистров, доступных для нити блока;

    warpSize – размер Варпа в потоках;

    maxThreadsPerBlock – максимальное количество потоков на блок;

    maxThreadsDim[3] содержит максимальный размер каждого измерения блока;

    maxGridSize[3] содержит максимальный размер каждого измерения сетки;

    totalConstMem – общий объем константной памяти, доступной на устройстве в байтах;

    clockRate – тактовая частота в килогерцах;

    deviceOverlap равно 1, если устройство может одновременно копировать память между хостом и устройством во время выполнения ядра, или 0, если нет;

    multiProcessorCount – число мультипроцессоров на устройстве;

    kernelExecTimeoutEnabled равно 1, если существует ограничение времени выполнения для ядер, выполненных на устройстве, или 0, если нет.

    integrated равен 1, если устройство является интегрированным
    (материнская плата) GPU и 0, если оно является дискретным.

    canMapHostMemory равно 1, если устройство может отображать память хоста в адресное пространство CUDA для использования с cudaHostAlloc() / cudaHostGetDevicePointer(), или 0, если нет;

    concurrentKernels равно 1, если устройство поддерживает выполнение нескольких ядер в том же контексте одновременно, или 0, если нет.

    23
    Приведем пример использования функции cudaGetDeviceProperties: cudaDeviceProp deviceProp; cudaGetDeviceProperties(&deviceProp, 0);//определение параметров GPU с номером 0 printf("Device name : %s\n", deviceProp.name); printf("Total global memory : %d MB\n", deviceProp.totalGlobalMem / 1024 / 1024); printf("Shared memory per block : %d\n", deviceProp.sharedMemPerBlock); printf("Registers per block : %d\n", deviceProp.regsPerBlock); printf("Warp size : %d\n", deviceProp.warpSize); printf("Memory pitch : %d\n", deviceProp.memPitch); printf("Max threads per block : %d\n", deviceProp.maxThreadsPerBlock); printf("Max threads dimensions : x = %d, y = %d, z =
    %d\n", deviceProp.maxThreadsDim[0], deviceProp.maxThreadsDim[1], deviceProp.maxThreadsDim[2]); printf("Max grid size: x = %d, y = %d, z = %d\n", deviceProp.maxGridSize[0], deviceProp.maxGridSize[1], deviceProp.maxGridSize[2]); printf("Clock rate: %d\n", deviceProp.clockRate); printf("Total constant memory: %d\n", deviceProp.totalConstMem); printf("Compute capability: %d.%d\n", deviceProp.major, deviceProp.minor); printf("Texture alignment: %d\n", deviceProp.textureAlignment); printf("Device overlap: %d\n", deviceProp.deviceOverlap); printf("Multiprocessor count: %d\n", deviceProp.multiProcessorCount);

    24 printf("Kernel execution timeout enabled: %s\n", deviceProp.kernelExecTimeoutEnabled ? "true" :
    "false"); scanf("");
    Посмотрим, что выдала программа:
    Device name : GeForce 940M
    Total global memory : 2048 MB
    Shared memory per block : 49152
    Registers per block : 65536
    Warp size : 32
    Memory pitch : 2147483647
    Max threads per block : 1024
    Max threads dimensions : x = 1024, y = 1024, z = 64
    Max grid size: x = 2147483647, y = 65535, z = 65535
    Clock rate: 1176000
    Total constant memory: 65536
    Compute capability: 5.0
    Texture alignment: 512
    Device overlap: 1
    Multiprocessor count: 3
    Kernel execution timeout enabled: true
    Также важным моментом является и определение версии видеокарты.
    Следующий код выводит производительность видеокарты cudaDeviceProp deviceProp; cudaGetDeviceProperties(&deviceProp, 0); printf("Device has Compute Capability %d.%d\n", deviceProp.major, deviceProp.minor);

    25
    Вопросы к разделу
    1. Опишите примерную архитектуру современных видеокарт NVidia.
    2. Какие параметры видеокарты характеризуют её производительность?
    3. Какие основные характеристики видеокарты можно узнать с помощью функции cudaGetDeviceProperties()?
    Лабораторная работа
    1.
    Определите следующие параметры видеокарты с поддержкой технологии CUDA:

    наименование;

    общий объем графической памяти;

    объем разделяемой памяти в пределах блока;

    число регистров в пределах блока;

    размер варпа;

    максимально допустимое число потоков в блоке;

    версию вычислительных возможностей;

    число потоковых мультипроцессоров;

    тактовую частоту ядра;

    объем кэша второго уровня;

    ширину шины памяти видеокарты;

    максимальную размерность при конфигурации потоков в блоке и блоков в сетке.
    2.
    Сравните результаты, полученные программно, с информацией от производителя.
    Домашняя работа

    26 1. Доработайте лабораторное задание 1, добавив в него вывод объема константной памяти и пиковую частоту видеокарты в МГц.

    27
    Глава 3. Простейшая программа на CUDA
    Каждая программа, использующая технологию CUDA, делится на две части: последовательная часть (код, выполняющийся на CPU) и параллельная часть (код, выполняющийся на GPU). Напишем программу «Hello, world!», которая будет вызвана на двух блоках и пяти нитях.
    #include "cuda_runtime.h"
    #include "device_launch_parameters.h"
    #include
    __global__ void HelloWorld()
    { printf("Hello world, %d, %d\n", blockIdx.x, threadIdx.x);
    } int main()
    {
    HelloWorld << <2, 5 >> >();
    // хост ожидает завершения работы девайса cudaDeviceSynchronize();
    // ожидаем нажатия любой клавиши getchar(); return 0;
    }
    Ядро, так называется функция, выполняющаяся на GPU. Эта функция имеет спецификатор __global__, который сообщает компилятору, что функция вызывается с CPU и выполняется на GPU. На ядро налагается ряд некоторых ограничений: не содержать рекурсию, не иметь переменное число аргументов, не иметь переменные static внутри себя. Вызов функции осуществляются следующим образом: имя_функции <<>> (параметры).

    28
    Тройные угловые скобки перед функцией, выполняющейся на GPU, – это синтаксис CUDA. Параметры в угловых скобках определяют количество и конфигурацию параллельных процессов GPU: размер сетки и размер блока.
    Функция копирования
    Рассмотрим более сложный пример использования технологии CUDA.
    Для написания программы, приведенной ниже, необходимо изучить принципы работы с глобальной памятью GPU.
    Глобальная память является самой медленной, и если необходима высокая производительность, то количество операций с глобальной памятью следует свести к минимуму.
    Назначение функции
    Синтаксис функции
    Выделение памяти cudaMalloc( void **devPtr, size_t size);
    Освобождение памяти cudaFree( void **devPtr);
    Копирование данных cudaMemcpy( void *dst, const void *src, size_t size, enum cudaMemcpyKind kind)
    В качестве параметра kind, задающее направление копирования, могут выступать: cudamemcpyHostToDevice, cudamemcpyDeviceToHost, cudamemcpyDeviceToDevice, cudamemcpyHostToHost.
    Итак, рассмотрим немного более сложную программу. Данная программа складывает два целых числа на GPU.
    //подключение библиотек
    #include "cuda_runtime.h"

    29
    #include "device_launch_parameters.h"
    #include
    #include
    // ядро
    __global__ void add( int *a, int *b, int *c ) {
    *c = *a + *b;
    }
    //главная функция int main()
    {
    // переменные на CPU int a, b, c;
    // переменные на GPU int *dev_a, *dev_b, *dev_c; int size = sizeof( int ); //размерность
    // выделяем память на GPU cudaMalloc( (void**)&dev_a, size ); cudaMalloc( (void**)&dev_b, size ); cudaMalloc( (void**)&dev_c, size );
    // инициализация переменных a = 2; b = 7;
    // копирование информации с CPU на GPU cudaMemcpy( dev_a, &a, size, cudaMemcpyHostToDevice
    ); cudaMemcpy( dev_b, &b, size, cudaMemcpyHostToDevice
    );
    // вызов ядра add<<< 1, 1 >>>( dev_a, dev_b, dev_c );
    // копирование результата работы ядра с GPU на CPU

    30 cudaMemcpy( &c, dev_c, size, cudaMemcpyDeviceToHost
    );
    // вывод информации printf("%d + %d = %d\n", a, b, c);
    // очищение памяти на GPU cudaFree( dev_a ); cudaFree( dev_b ); cudaFree( dev_c ); return 0;
    }
    Замер времени работы части кода программы
    Одним из важных параметров при написании программы является время, затраченное на ее выполнение. Для замера времени выполнения операций на
    CPU есть множество возможностей. Для того, чтобы замерить время, которое было затрачено на выполнение вычислений на GPU, можно воспользоваться механизмом событий (CUDA events). Событие – это объект типа cudaEvent_t, используемый для обозначения «точки» среды вызовов CUDA.
    Приведем пример фрагмента кода, замеряющий время выполнения ядра на GPU:
    // инициализируем события cudaEvent_t start, stop; float elapsedTime;
    // создаем события cudaEventCreate(&start); cudaEventCtreate(&stop);
    // запись события cudaEventRecord(start, 0);
    // вызов ядра

    31 cudaEventRecord(stop,0);
    // ожидание завершения работы ядра cudaEventSynchronize(stop); cudaEventElapsedTime(&elapsedTime, start, stop);
    // вывод информации printf("Time spent executing by the GPU: %.2f millseconds\n", elapsedTime);
    // уничтожение события cudaEventDestroy(start); cudaEventDestroy(stop);
    Обработка ошибок
    После выполнения функций на GPU, они возвращают значения типа cudaError_t. Функции, отработавшие на девайсе, возвращают значение cudaSuccess, в противном случае возвращается код ошибки.
    Назначение функции
    Синтаксис функции
    Возвращает описание ошибки в виде строки cudaGetErrorString(cudaError_t error);
    Возвращает код последней ошибки cudaGetLastError();
    Основные выводы ответа при обработке ошибок:
    1. cudaSuccess – при удачном выполнении.
    2. cudaErrorMemoryAllocation – при ошибке выделения памяти.
    3. cudaErrorInvalidValue – неверные параметры аргумента (например, размер копирования отрицателен).
    4. cudaErrorInvalidDevicePointer – неверный указатель памяти в видеокарте.
    5. cudaErrorInvalidMemcpyDirection – неверное направление (например, перепутан источник и место-назначение копирования).
    6. cudaErrorInitializationError – ошибка инициализации.
    7. cudaErrorPriorLaunchFailure – ошибка при предыдущем асинхронном

    32 запуске функции.
    8. cudaErrorInvalidResourceHandle – неверный обработчик события.
    Приведем пример использования функции cudaGetLastError(). cudaError_t err = cudaGetLastError(); if (err != cudaSuccess) printf("%s ", cudaGetErrorString(err));
    В данном примере проверяется, есть ли ошибка у последней команды, выполненной на девайсе. Если ошибка есть, то выводится ее описание.
    Вопросы к разделу
    1. На какие условно две части можно разделить CUDA-программу?
    2. Для чего нужна глобальная память? Какие имеются функции в CUDA для работы с глобальной памятью?
    3. Опишите механизм, использующийся в CUDA-программах, для замера времени работы ядра.
    4. Какие механизмы в CUDA-программах используются для обработки ошибок?
    Лабораторная работа
    1. Измерьте скорость копирования данных (ГБ/сек) между CPU и GPU.
    2. Напишите программу, вычисляющую число пи методом интегрирования четверти окружности единичного радиуса (можно использовать формулу для площади четверти окружности:
    4
    /
    1 1
    0 2
    π
    =

    =

    dx
    x
    S
    ). В программе предусмотрите проверку на ошибку выполнения функции.

    33
    Домашняя работа
    1. Напишите программу вычисления дзета-функции Римана путем суммирования ряда из обратных степеней.

    34
    1   2   3   4   5   6


    написать администратору сайта