Технология nvidia cuda · Весенний семестр, 2016. ... (kepler, 2304 cores,...

120
Технология NVIDIA CUDA Михаил Георгиевич Курносов Email: [email protected] WWW: http://www.mkurnosov.net Курс «Параллельные и распределенные вычисления» Школа анализа данных Яндекс (Новосибирск) Весенний семестр, 2016

Upload: others

Post on 26-May-2020

12 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Технология NVIDIA CUDA

Михаил Георгиевич Курносов

Email: [email protected]

WWW: http://www.mkurnosov.net

Курс «Параллельные и распределенные вычисления»

Школа анализа данных Яндекс (Новосибирск)

Весенний семестр, 2016

Page 2: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

GPU – Graphics Processing Unit

Graphics Processing Unit (GPU) – графический процессор, специализированный многопроцессорный ускоритель с общей памятью

Большая часть площади чипа занята элементарными ALU/FPU/Load/Storeмодулями

Устройство управления (control unit) относительно простое по сравнению с CPU

GPU управляется с CPU: копирование данных между оперативной памятью узла и GPU, запуск программ и др.

NVIDIA GeForce GTX 780 (Kepler, 2304 cores, GDDR5 3 GB)

AMD Radeon HD 8970(2048 cores, GDDR5 3 GB)

Page 3: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Гибридные вычислительные узлы

Несколько GPU

CPU управляет GPU через драйвер

Узкое место –передача данных между памятью CPU и GPU

GPU не является bootable-устройством

74 системы из Top500 (Nov. 2014) оснащены ускорителями (NVIDIA, ATI, Intel Xeon Phi, PEZY SC )

CPU1

Core1 Core2 Core3 Core4

GPU1

Cores

GPU Memory

Memory (DDR3) Memory (DDR3)

QPI

I/O Hub

QPI QPI

PCI Express

CPU1

Core1 Core2 Core3 Core4

GPU2

Cores

GPU Memory

Page 4: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

NVIDIA CUDA

NVIDIA CUDA – программно-аппаратная платформа для организации параллельных вычислений на графических процессорах

NVIDIA CUDA SDK:o архитектура виртуальной машины CUDAo компилятор C/C++o драйвер GPU

ОС: GNU/Linux, Apple Mac OS X, Microsoft Windows

2006 – CUDA 1.0

2016 – CUDA 7.5 (Unified memory, Multi-GPU)

Микроархитектуры: Tesla (GeForce 8), Fermi (GeForce 400, 500), Kepler (GeForce 600, 700), Maxwell (GeForce 700, 800, 900)

http://developer.nvidia.com/cuda

Page 5: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Хост (host) – узел с CPU и его память

Устройство (device) – графический процессор

и его память

Ядро (kernel) – это фрагмент программы,

предназначенный для выполнения

на GPU

CUDA-программа и ее входные данные находятся в памяти хоста

Программа компилируется и запускается на хосте

В CUDA-программе выполняются следующие шаги:

1. Копирование данных из памяти хоста в память устройства

2. Запуск ядра (kernel) на устройстве

3. Копирование данных из памяти устройства в память хоста

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

RAM

CPU

RAM

GPU

DeviceHost

PCI Express

Server/workstation

Page 6: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

CUDA Hello World

/* * hello.cu: * */

#include <stdio.h>

__global__ void mykernel() {

}

int main(){

/* Запуск ядра на GPU – один поток */mykernel<<<1,1>>>();printf("Hello, CUDA World!\n");return 0;

}

Спецификатор __global__ сообщает

компилятору, что функция предназначена

для выполнения на GPU

“<<< >>>” – запуск заданного числа

потоков на GPU

Page 7: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

NVIDIA GeForce 680 (GK104, Kepler microarch.)

http://www.nvidia.com/content/PDF/product-specifications/GeForce_GTX_680_Whitepaper_FINAL.pdf

4 Graphics Processing Clusters (GPC)2 Streaming Multiprocessor (SMX)

Streaming Multiprocessor (SMX)192 cores, 32 special function units, 32 LD/ST units, 4 warp schedulers

Page 8: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Информация об устройстве

./deviceQuery Starting...

CUDA Device Query (Runtime API) version (CUDART static linking)

Detected 1 CUDA Capable device(s)

Device 0: "GeForce GTX 680"CUDA Driver Version / Runtime Version 7.5 / 7.5CUDA Capability Major/Minor version number: 3.0Total amount of global memory: 2048 MBytes (2147287040 bytes)( 8) Multiprocessors, (192) CUDA Cores/MP: 1536 CUDA CoresGPU Max Clock rate: 1058 MHz (1.06 GHz)Memory Clock rate: 3004 MhzMemory Bus Width: 256-bitL2 Cache Size: 524288 bytesWarp size: 32Maximum number of threads per multiprocessor: 2048Maximum number of threads per block: 1024Max dimension size of a thread block (x,y,z): (1024, 1024, 64)Max dimension size of a grid size (x,y,z): (2147483647, 65535, 65535)...

/opt/NVIDIA_CUDA-7.5_Samples

Page 9: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Вычислительные потоки CUDA

Номер потока (thread index) – это трехкомпонентный

вектор (координаты потока)

Потоки логически сгруппированы в одномерный,

двухмерный или трёхмерный блок (thread block)

Количество потоков в блоке ограничено (в Kepler 1024)

Блоки распределяются по потоковым

мультипроцессорам SMX

Предопределенные переменные

o threadIdx.{x, y, z} – номер потока

o blockDim.{x, y, z} – размерность блока

o blockIdx.{x, y, z} – номер блока

Thread Block

Thread Thread Thread

Thread Thread Thread

Page 10: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Вычислительные потоки CUDA

Grid of thread blocks

Thread Block

Thread Thread Thread

Thread Thread Thread

Thread Block

Thread Thread Thread

Thread Thread Thread

Thread Block

Thread Thread Thread

Thread Thread Thread

Thread Block

Thread Thread Thread

Thread Thread Thread

Thread Block

Thread Thread Thread

Thread Thread Thread

Thread Block

Thread Thread Thread

Thread Thread Thread

gridDim.y

gridDim.x

blockDim.xblockDim.z

blockDim.y

Page 11: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Выполнение CUDA-программы

CUDA Program

Block 0 Block 1 Block 2

Block 3 Block 4 Block 5

GPU 1

Block 0 Block 1

Block 2 Block 3

Block 4 Block 5

SMX 1 SMX 2

GPU 2

Block 0 Block 1 Block 2

Block 3 Block 4 Block 5

SMX 1 SMX 2 SMX 3

Page 12: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Сложение векторов (CPU version)

/* * Host code (CPU version)*/void vadd(float *a, float *b, float *c, int n){

for (int i = 0; i < n; i++)c[i] = a[i] + b[i];

}

Page 13: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Сложение векторов (GPU version)

#include <cuda_runtime.h>

enum { NELEMS = 1024 * 1024 };

int main(){

/* Allocate vectors on host */size_t size = sizeof(float) * NELEMS;float *h_A = malloc(size);float *h_B = malloc(size);float *h_C = malloc(size);if (h_A == NULL || h_B == NULL || h_C == NULL) {

fprintf(stderr, "Allocation error.\n");exit(EXIT_FAILURE);

}

for (int i = 0; i < NELEMS; ++i) {h_A[i] = rand() / (float)RAND_MAX;h_B[i] = rand() / (float)RAND_MAX;

}

Page 14: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Сложение векторов (GPU version)

/* Allocate vectors on device */float *d_A = NULL, *d_B = NULL, *d_C = NULL;if (cudaMalloc((void **)&d_A, size) != cudaSuccess) {

fprintf(stderr, "Allocation error\n");exit(EXIT_FAILURE);

}if (cudaMalloc((void **)&d_B, size) != cudaSuccess) {

fprintf(stderr, "Allocation error\n");exit(EXIT_FAILURE);

}if (cudaMalloc((void **)&d_C, size) != cudaSuccess) {

fprintf(stderr, "Allocation error\n");exit(EXIT_FAILURE);

}

Page 15: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Сложение векторов (GPU version)

/* Copy the host vectors to device */if (cudaMemcpy(d_A, h_A, size, cudaMemcpyHostToDevice) != cudaSuccess) {

fprintf(stderr, "Host to device copying failed\n");exit(EXIT_FAILURE);

}if (cudaMemcpy(d_B, h_B, size, cudaMemcpyHostToDevice) != cudaSuccess) {

fprintf(stderr, "Host to device copying failed\n");exit(EXIT_FAILURE);

}

/* Launch the kernel */int threadsPerBlock = 1024;int blocksPerGrid =(NELEMS + threadsPerBlock - 1) / threadsPerBlock;vadd<<<blocksPerGrid, threadsPerBlock>>>(d_A, d_B, d_C, NELEMS);if (cudaGetLastError() != cudaSuccess) {

fprintf(stderr, "Failed to launch kernel!\n");exit(EXIT_FAILURE);

}

𝑏𝑙𝑜𝑐𝑘𝑠 =𝑁

𝑡ℎ𝑟𝑒𝑎𝑑𝑠=

𝑁 + 𝑡ℎ𝑟𝑒𝑎𝑑𝑠 − 1

𝑡ℎ𝑟𝑒𝑎𝑑𝑠

Page 16: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Сложение векторов (GPU version)

/* Copy the device vectors to host */if (cudaMemcpy(h_C, d_C, size, cudaMemcpyDeviceToHost) != cudaSuccess) {

fprintf(stderr, "Device to host copying failed\n");exit(EXIT_FAILURE);

}

for (int i = 0; i < NELEMS; ++i) {if (fabs(h_A[i] + h_B[i] - h_C[i]) > 1e-5) {

fprintf(stderr, "Result verification failed at element %d!\n", i);exit(EXIT_FAILURE);

}}

cudaFree(d_A); cudaFree(d_B); cudaFree(d_C);free(h_A);free(h_B);free(h_C);cudaDeviceReset();return 0;

}

Page 17: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Thread Block 2

Thread 0 Thread 1 Thread …

Сложение векторов (GPU version)

__global__ void vadd(const float *a, const float *b, float *c, int n){

int i = blockDim.x * blockIdx.x + threadIdx.x;if (i < n)

c[i] = a[i] + b[i];}

Thread Block 0

Thread 0 Thread 1 … Thread … …

Thread Block 1

Thread 0 Thread 1 Thread …

Thread Block 3

Thread 0 Thread 1 Thread …… ……

Grid of blocks:

Page 18: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Сложение векторов (scalability)

NELEMScngpu1

GeForce GTX 680cngpu2

GeForce GT 630

cngpu3GeForce GTS 250

(threadsPerBlock = 512)

2^20

CPU version (sec.): 0.001658GPU version (sec.): 0.000161Memory ops. (sec.): 0.002341Memory bw. (MiB/sec.): 5125.94CPU perf (MFLOPS): 603.15GPU perf (MFLOPS): 6213.78Speedup: 10.30Speedup (with mem ops.): 0.66

CPU version (sec.): 0.002311GPU version (sec.): 0.001054Memory ops. (sec.): 0.002514Memory bw. (MiB/sec.): 4773.49CPU perf (MFLOPS): 432.71GPU perf (MFLOPS): 948.72Speedup: 2.19Speedup (with mem ops.): 0.65

CPU version (sec.): 0.002195GPU version (sec.): 0.001993Memory ops. (sec.): 0.009481Memory bw. (MiB/sec.): 1265.70CPU perf (MFLOPS): 455.61GPU perf (MFLOPS): 501.77Speedup: 1.10Speedup (with mem ops.): 0.19

10 * 2^20

CPU version (sec.): 0.015133GPU version (sec.): 0.000892Memory ops. (sec.): 0.021551Memory bw. (MiB/sec.): 5568.15CPU perf (MFLOPS): 660.81GPU perf (MFLOPS): 11211.72Speedup: 16.97Speedup (with mem ops.): 0.67

CPU version (sec.): 0.014987GPU version (sec.): 0.009118Memory ops. (sec.): 0.021882Memory bw. (MiB/sec.): 5484.00CPU perf (MFLOPS): 667.25GPU perf (MFLOPS): 1096.72Speedup: 1.64Speedup (with mem ops.): 0.48

CPU version (sec.): 0.017394GPU version (sec.): 0.006778Memory ops. (sec.): 0.101824Memory bw. (MiB/sec.): 1178.51CPU perf (MFLOPS): 574.91GPU perf (MFLOPS): 1475.36Speedup: 2.57Speedup (with mem ops.): 0.16

Page 19: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

NVIDIA GeForce 680 (GK104, Kepler)

http://www.nvidia.com/content/PDF/product-specifications/GeForce_GTX_680_Whitepaper_FINAL.pdf

Streaming Multiprocessor (SM)192 cores, 32 special function units, 32 LD/ST units, 4 warp schedulers

Архитектура SIMT (Single-Instruction, Multiple-Thread)

Блоки потоков распределяются по SM

Если число блоков меньше количества SM, то часть процессоров GPU простаивает

Порядок выполнения блоков заранее неизвестен –алгоритмы для GPU не должны быть чувствительны к порядку выполнения блоков

CUDA Program

Block 0 Block 1 Block 2

Block 3 Block 4 Block 5

GPU 1

Block 0 Block 1

Block 2 Block 3

Block 4 Block 5

SMX 1 SMX 2

GPU 2

Block 0 Block 1 Block 2

Block 3 Block 4 Block 5

SMX 1 SMX 2 SMX 3

Page 20: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

NVIDIA GeForce 680 (GK104, Kepler)

http://www.nvidia.com/content/PDF/product-specifications/GeForce_GTX_680_Whitepaper_FINAL.pdf

Streaming Multiprocessor (SM)192 cores, 32 special function units, 32 LD/ST units, 4 warp schedulers

Архитектура SIMT (Single-Instruction, Multiple-Thread)

Блоки назначенные на SM логически разбиваются на группы (warps) по 32 потока

Потоки всегда одинаково распределяются по warp-ам (детерминировано, на базе номера потока)

Если размер блока не кратен размеру группы, то последняя группа (warp) потоков блокируется

http://mc.stanford.edu/cgi-bin/images/3/34/Darve_cme343_cuda_3.pdf

Page 21: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

NVIDIA GeForce 680 (GK104, Kepler)

http://www.nvidia.com/content/PDF/product-specifications/GeForce_GTX_680_Whitepaper_FINAL.pdf

Streaming Multiprocessor (SM)192 cores, 32 special function units, 32 LD/ST units, 4 warp schedulers

Архитектура SIMT (Single-Instruction, Multiple-Thread)

Планировщик (warp scheduler) запускает на выполнение группу потоков, у которых «готовы» входные данные

Все активные потоки группы в каждый момент времени выполняют одну и ту же инструкцию

Планировщик выполняет переключение контекста между группами потоков

http://mc.stanford.edu/cgi-bin/images/3/34/Darve_cme343_cuda_3.pdf

Page 22: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

NVIDIA GeForce 680 (GK104, Kepler)

http://www.nvidia.com/content/PDF/product-specifications/GeForce_GTX_680_Whitepaper_FINAL.pdf

Streaming Multiprocessor (SM)192 cores, 32 special function units, 32 LD/ST units, 4 warp schedulers

Архитектура SIMT (Single-Instruction, Multiple-Thread)

Control Flow Divergence

Все потоки группы в каждый момент времени выполняют одну и ту же инструкцию

Что если потоки управления расходятся?

__global__ void kernel(){

if (val < 50) {// branch 1

} else {// branch 2

}}

Потоки группы выполняют обе ветви

Потоки, которые не должны выполнять ветвь, блокируются

Page 23: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

NVIDIA GeForce 680 (GK104, Kepler)

http://www.nvidia.com/content/PDF/product-specifications/GeForce_GTX_680_Whitepaper_FINAL.pdf

Streaming Multiprocessor (SM)192 cores, 32 special function units, 32 LD/ST units, 4 warp schedulers

Архитектура SIMT (Single-Instruction, Multiple-Thread)

Control Flow Divergence

__global__ void kernel(){

if (val < 50) {// branch 1// branch 1// branch 1

} else {// branch 2// branch 2

}}

T F T T T FT F

x x x x x

x x x

Page 24: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Информация об устройстве (cngpu1)

./deviceQuery Starting...

CUDA Device Query (Runtime API) version (CUDART static linking)

Detected 1 CUDA Capable device(s)

Device 0: "GeForce GTX 680"CUDA Driver Version / Runtime Version 7.5 / 7.5CUDA Capability Major/Minor version number: 3.0Total amount of global memory: 2048 MBytes (2147287040 bytes)( 8) Multiprocessors, (192) CUDA Cores/MP: 1536 CUDA CoresGPU Max Clock rate: 1058 MHz (1.06 GHz)Memory Clock rate: 3004 MhzMemory Bus Width: 256-bitL2 Cache Size: 524288 bytesWarp size: 32Maximum number of threads per multiprocessor: 2048Maximum number of threads per block: 1024Max dimension size of a thread block (x,y,z): (1024, 1024, 64)Max dimension size of a grid size (x,y,z): (2147483647, 65535, 65535)...

/opt/NVIDIA_CUDA-7.5_Samples

Page 25: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Иерархия потоков

Сетка потоков (1D, 2D, 3D) – совокупность блоков потоков

Сетка потоков, CUDA-программа, выполняется GPU

Блок потоков (1D, 2D, 3D) – совокупность потоков

Блоки распределяются по потоковым мультипроцессорам

Device 0: "GeForce GTX 680"Max dimension size of a thread block (x,y,z): (1024, 1024, 64)Max dimension size of a grid size (x,y,z): (2147483647, 65535, 65535)...

Page 26: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Иерархия потоков

Предопределенные переменные

o threadIdx.{x, y, z} –номер потока в блоке

o blockDim.{x, y, z} –размерность блока

o blockIdx.{x, y, z} –номер блока в сетке

Здание структуры сетки

mykernel<<<B,T>>>(arg1, … );

o B – структура сеткиo T – структура блока

Grid of thread blocks

Thread Block

Thread Thread Thread

Thread Thread Thread

Thread Block

Thread Thread Thread

Thread Thread Thread

Thread Block

Thread Thread Thread

Thread Thread Thread

Thread Block

Thread Thread Thread

Thread Thread Thread

Thread Block

Thread Thread Thread

Thread Thread Thread

Thread Block

Thread Thread Thread

Thread Thread Thread

gridDim.y

gridDim.x

blockDim.xblockDim.z

blockDim.y

Page 27: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Сложение матриц: CPU

C A B

= +

void matadd_host(float *a, float *b, float *c, int m, int n){

for (int i = 0; i < m; i++) {for (int j = 0; j < n; j++) {

int idx = i * n + j;c[idx] = a[idx] + b[idx];

} }

}

Page 28: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Сложение матриц: CUDA 1D

Каждый поток вычисляет один элемент c[i * n + j] матрицы

Одномерные сетка блоков потоков и одномерные блоки потоков

__global__ void matadd(const float *a, const float *b, float *c, int m, int n){

int idx = blockIdx.x * blockDim.x + threadIdx.x;if (idx < m * n)

c[idx] = a[idx] + b[idx];}

{ int threadsPerBlock = 1024;int blocksPerGrid =(ROWS * COLS + threadsPerBlock - 1) / threadsPerBlock;matadd<<<blocksPerGrid, threadsPerBlock>>>(d_A, d_B, d_C, ROWS, COLS);

}

0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7

blockIdx.x = 0 blockIdx.x = 1 blockIdx.x = 2

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

threadIdx.x1D-сетка из 3-х 1D-блоков потоков

Thread global ID

C A B

= +

Page 29: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Сложение матриц: CUDA 2D

Каждый поток вычисляет один элемент c[i * n + j] матрицы

2D-сетка блоков потоков и 2D-блоки потоков

__global__ void matadd(const float *a, const float *b, float *c, int m, int n){

int x = blockIdx.x * blockDim.x + threadIdx.x; int y = blockIdx.y * blockDim.y + threadIdx.y;int idx = y * n + x;if (idx < m * n) c[idx] = a[idx] + b[idx];

}

{int threadsPerBlockDim = 32;dim3 blockDim(threadsPerBlockDim, threadsPerBlockDim, 1); // Grid: X x Y x Z=1int blocksPerGridDimX = ceilf(COLS / (float)threadsPerBlockDim);int blocksPerGridDimY = ceilf(ROWS / (float)threadsPerBlockDim);dim3 gridDim(blocksPerGridDimX, blocksPerGridDimY, 1); // Blocks: X x Y x Z=1matadd<<<gridDim, blockDim>>>(d_A, d_B, d_C, ROWS, COLS);

}

(0,0) (1,0) (2,0) (0,0) (1,0) (2,0) (0,0) (1,0) (2,0) (0,0) (1,0) (2,0)

(0,1) (1,1) (2,1) (0,1) (1,1) (2,1) (0,1) (1,1) (2,1) (0,1) (1,1) (2,1)

(0,2) (1,2) (2,2) (0,2) (1,2) (2,2) (0,2) (1,2) (2,2) (0,2) (1,2) (2,2)

(0,0) (1,0) (2,0) (0,0) (1,0) (2,0) (0,0) (1,0) (2,0) (0,0) (1,0) (2,0)

(0,1) (1,1) (2,1) (0,1) (1,1) (2,1) (0,1) (1,1) (2,1) (0,1) (1,1) (2,1)

(0,2) (1,2) (2,2) (0,2) (1,2) (2,2) (0,2) (1,2) (2,2) (0,2) (1,2) (2,2)

threadIdx.xthreadIdx.yblockIdx.x = 0 blockIdx.x = 1 blockIdx.x = 2 blockIdx.x = 3

blockIdx.y = 0

blockIdx.y = 1

Page 30: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Умножение матриц (CPU)

void sgemm_host(float *a, float *b, float *c, int n){

for (int i = 0; i < n; i++) {for (int j = 0; j < n; j++) {

float s = 0.0;for (int k = 0; k < n; k++)

s += a[i * n + k] * b[k * n + j];c[i * n + j] = s;

} }

}

Page 31: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Умножение матриц (CUDA)

Каждый поток вычисляет один элемент результирующей матрицы C Общее число потоков N * N

http://users.wfu.edu/choss/CUDA/docs/Lecture%205.pdf

__global__ void sgemm(const float *a, const float *b, float *c, int n){

int row = blockIdx.y * blockDim.y + threadIdx.y;int col = blockIdx.x * blockDim.x + threadIdx.x;

if (row < n && col < n) {float s = 0.0;for (int k = 0; k < n; k++)

s += a[row * n + k] * b[k * n + col];c[row * n + col] = s;

}}{

int threadsPerBlockDim = 32;dim3 blockDim(threadsPerBlockDim, threadsPerBlockDim, 1); int blocksPerGridDimX = ceilf(N / (float)threadsPerBlockDim);int blocksPerGridDimY = ceilf(N / (float)threadsPerBlockDim);dim3 gridDim(blocksPerGridDimX, blocksPerGridDimY, 1);sgemm<<<gridDim, blockDim>>>(d_A, d_B, d_C, N);

}

Page 32: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Иерархия памяти (comp. capability >= 3.0)

Глобальная память (global memory)o Относительно медленнаяo Кешируетсяo Содержимое сохраняется между запусками ядер

Память констант (constant memory)o Содержит константы и аргументы ядерo Кешируетсяo 64 KiB / GPU (8 KiB const. cache per SM)

Разделяемая память (shared memory)o Быстраяo Некешируется

Локальная память (local memory)o Часть глобальной памятиo Кешируется

Регистры (32 bit, 65536 / block)

Page 33: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Иерархия памяти (comp. capability >= 3.0)

Объявление ПамятьОбласть

видимостиВремя жизни

int localVar; register thread thread

int localArray[10]; local thread thread

__shared__ int sharedVar; shared block block

__device__ int globalVar; global grid program

__constant__ int constVar; constant grid program

Page 34: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Иерархия памяти (comp. capability >= 3.0)

Объявление ПамятьНакладные

расходы

int localVar; register 1x

int localArray[10]; local 100x

__shared__ int sharedVar; shared 1x

__device__ int globalVar; global 100x

__constant__ int constVar; constant 1x

Page 35: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Иерархия памяти

Page 36: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Использование иерархии памяти (thread local)

// Загружаем данные из глобальной памяти в регистрfloat localA = dA[blockIdx.x * blockDim.x + threadIdx.x];

// Вычисления над данными в регистрахfloat res = f(localA);

// Записываем результат в глобальную памятьdA[blockIdx.x * blockDim.x + threadIdx.x] = res;

Page 37: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Использование иерархии памяти (block local)

// Загружаем данные из глобальной памяти в разделяемую__shared__ float sharedA[BLOCK_SIZE]; int idx = blockIdx.x * blockDim.x + threadIdx.x;sharedA[threadIdx.x] = dA[idx];

__syncthreads(); // барьерная синхронизация потоков блока

// Вычисления над данными в разделяемой памятиfloat res = f(shared[threadIdx.x]);

// Записываем результат в глобальную памятьdA[idx] = res;

Page 38: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Умножение матриц (CPU)

void sgemm_host(float *a, float *b, float *c, int n){

for (int i = 0; i < n; i++) {for (int j = 0; j < n; j++) {

float s = 0.0;for (int k = 0; k < n; k++)

s += a[i * n + k] * b[k * n + j];c[i * n + j] = s;

} }

}

Page 39: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Умножение матриц (CUDA naive)

Каждый поток вычисляет один элемент результирующей матрицы C Общее число потоков N * N

http://users.wfu.edu/choss/CUDA/docs/Lecture%205.pdf

__global__ void sgemm(const float *a, const float *b, float *c, int n){

int row = blockIdx.y * blockDim.y + threadIdx.y;int col = blockIdx.x * blockDim.x + threadIdx.x;

if (row < n && col < n) {float s = 0.0;for (int k = 0; k < n; k++)

s += a[row * n + k] * b[k * n + col];c[row * n + col] = s;

}}{

int threadsPerBlockDim = 32;dim3 blockDim(threadsPerBlockDim, threadsPerBlockDim, 1); int blocksPerGridDimX = ceilf(N / (float)threadsPerBlockDim);int blocksPerGridDimY = ceilf(N / (float)threadsPerBlockDim);dim3 gridDim(blocksPerGridDimX, blocksPerGridDimY, 1);sgemm<<<gridDim, blockDim>>>(d_A, d_B, d_C, N);

}

(0,0) (1,0) (2,0) (0,0) (1,0) (2,0) (0,0) (1,0) (2,0) (0,0) (1,0) (2,0)

(0,1) (1,1) (2,1) (0,1) (1,1) (2,1) (0,1) (1,1) (2,1) (0,1) (1,1) (2,1)

(0,2) (1,2) (2,2) (0,2) (1,2) (2,2) (0,2) (1,2) (2,2) (0,2) (1,2) (2,2)

(0,0) (1,0) (2,0) (0,0) (1,0) (2,0) (0,0) (1,0) (2,0) (0,0) (1,0) (2,0)

(0,1) (1,1) (2,1) (0,1) (1,1) (2,1) (0,1) (1,1) (2,1) (0,1) (1,1) (2,1)

(0,2) (1,2) (2,2) (0,2) (1,2) (2,2) (0,2) (1,2) (2,2) (0,2) (1,2) (2,2)

threadIdx.xthreadIdx.yblockIdx.x = 0 blockIdx.x = 1 blockIdx.x = 2 blockIdx.x = 3

blockIdx.y = 0

blockIdx.y = 1

Каждый поток обращается к глобальной памяти

(n loads + 1 store)

Page 40: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Умножение матриц c разделяемой памятью (tiled)

Каждый элемент матрицы С вычисляется одним потоком Вычисления разбиты на несколько стадий – по числу

подматриц

c[i,j] = a[i,0]*b[0,j] + a[i,1]*b[1,j] + a[i,2]*b[2,j] + a[i,3]*b[3,j] +

a[i,4]*b[4,j] + a[i,5]*b[5,j] + a[i,6]*b[6,j] + a[i,7]*b[7,j] +

a[i,8]*b[8,j] + a[i,9]*b[9,j] + a[i,10]*b[10,j] + a[i,11]*b[11,j] +

a[i,12]*b[12,j] + a[i,13]*b[13,j] + a[i,14]*b[14,j] + a[i,15]*b[15,j];

На каждой стадии загружаем подматрицы в разделяемую память и вычисляем часть результатоввсеми потоками блока

B

CAAstart = blockIdx.y * blockDim.y * NAstep = blockDim.x

Bstart = blockIdx.x * blockDim.xBstep = N * blockDim.x

Page 41: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

__global__ void sgemm_tailed(const float *a, const float *b, float *c, int n){

int tail_size = blockDim.x;int tx = threadIdx.x;int ty = threadIdx.y;int bx = blockIdx.x;int by = blockIdx.y;

// Result for c[i, j]float sum = 0.0;

// Index of first tail (sub-matrix) in A int Astart = by * n * tail_size;int Aend = Astart + n - 1;int Astep = tail_size;

// Index of first tail (sub-matrix) in Bint Bstart = bx * tail_size; int Bstep = n * tail_size;

Умножение матриц c разделяемой памятью (tiled)

B

CA

Page 42: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

__shared__ float as[BLOCK_SIZE][BLOCK_SIZE];__shared__ float bs[BLOCK_SIZE][BLOCK_SIZE];

int ai = Astart;int bi = Bstart;while (ai <= Aend) {

// Load tail to shared memory - each thread load one itemas[ty][tx] = a[ai + ty * n + tx];bs[ty][tx] = b[bi + ty * n + tx];

// Wait all threads__syncthreads();

// Compute partial result for (int k = 0; k < tail_size; k++)

sum += as[ty][k] * bs[k][tx];

// Wait for all threads before overwriting of as and bs__syncthreads();

ai += Astep;bi += Bstep;

}

int Cstart = by * n * tail_size + bx * tail_size;c[Cstart + ty * n + tx] = sum;

}

Умножение матриц c разделяемой памятью (tiled)

Page 43: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Умножение матриц

Tailed versionCUDA kernel launch with 1024 (32 32) blocks of 1024 (32 32) threadsCPU version (sec.): 3.517567GPU version (sec.): 0.009149Memory ops. (sec.): 0.002338Memory bw. (MiB/sec.): 5133.26CPU GFLOPS: 0.61GPU GFLOPS: 234.72Speedup: 384.47Speedup (with mem ops.): 306.23

Naive version CUDA kernel launch with 1024 (32 32) blocks of 1024 (32 32) threadsCPU version (sec.): 2.824214GPU version (sec.): 0.023105Memory ops. (sec.): 0.002345Memory bw. (MiB/sec.): 5117.08CPU GFLOPS: 0.76GPU GFLOPS: 92.94Speedup: 122.23Speedup (with mem ops.): 110.97

Naive versionCUDA kernel launch with 1024 (32 32) blocks of 1024 (32 32) threadsCPU version (sec.): 3.101753GPU version (sec.): 0.254254Memory ops. (sec.): 0.002534Memory bw. (MiB/sec.): 4735.76CPU GFLOPS: 0.69GPU GFLOPS: 8.45Speedup: 12.20Speedup (with mem ops.): 12.08

Tailed versionCUDA kernel launch with 1024 (32 32) blocks of 1024 (32 32) threadsCPU version (sec.): 3.009662GPU version (sec.): 0.089633Memory ops. (sec.): 0.002516Memory bw. (MiB/sec.): 4769.42CPU GFLOPS: 0.71GPU GFLOPS: 23.96Speedup: 33.58Speedup (with mem ops.): 32.66

x2.8x2.5

GeForce GTX 680 GeForce GT 630

Page 44: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Гистограмма цветов изображения

https://commons.wikimedia.org/wiki/File:Image_and_histogram.png?uselang=ru

Задано изображение – двумерный массив целых чисел из интервала [0..255]

Необходимо построить гистограмму, таблицу hist[0..255], элемент hist[i] которой равен числу пикселей с цветом i

Page 45: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Гистограмма цветов изображения (CPU)

// Выделение памяти под изображениеsize_t size = sizeof(uint8_t) * width * height;uint8_t *image = (uint8_t *)malloc(size);if (image == NULL) {

fprintf(stderr, "Allocation error.\n");exit(EXIT_FAILURE);

}// Инициализация изображения srand(0);for (size_t i = 0; i < size; i++)

image[i] = (rand() / (double)RAND_MAX) * 255;

// Инициализация гистограммыint hist[256];memset(hist, 0, sizeof(*hist) * 256);

Page 46: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Гистограмма цветов изображения (CPU)

void hist_host(uint8_t *image, int width, int height, int *hist)

{for (int i = 0; i < height; i++)

for (int j = 0; j < width; j++)hist[image[i * width + j]]++;

}

Нерегулярный шаблон доступа к памяти –

массиву hist

Page 47: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Гистограмма цветов изображения (GPU)

Каждый поток обрабатывает один пиксель изображения

// Гистограмма в памяти GPUint *d_hist = NULL;cudaMalloc((void **)&d_hist, sizeof(*d_hist) * 256);cudaMemset(d_hist, 0, sizeof(*d_hist) * 256);

uint8_t *d_image = NULL;cudaMalloc((void **)&d_image, size);cudaMemcpy(d_image, image, size, cudaMemcpyHostToDevice);

int threadsPerBlock = 1024;int blocksPerGrid = (size + threadsPerBlock - 1) / threadsPerBlock;hist_gpu<<<blocksPerGrid, threadsPerBlock>>>(d_image, width,

height, d_hist);

cudaMemcpy(hist, d_hist, sizeof(*d_hist) * 256, cudaMemcpyDeviceToHost);

Page 48: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Гистограмма цветов изображения (GPU)

Каждый поток обрабатывает один пиксель изображения

__global__ void hist_gpu(uint8_t *image, int width, int height, int *hist)

{size_t size = width * height;int i = blockIdx.x * blockDim.x + threadIdx.x;if (i < size) {

hist[image[i]]++;}

}

Page 49: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Гистограмма цветов изображения: тест

$ ./histSum (CPU) = 104857600.000000CUDA kernel launch with 102400 blocks of 1024 threadsSum (GPU) = 3928061.000000CPU version (sec.): 0.052969GPU version (sec.): 0.068165Memory ops. (sec.): 0.000019Speedup: 0.78Speedup (with mem ops.): 0.78

$ ./histSum (CPU) = 104857600.000000CUDA kernel launch with 102400 blocks of 1024 threadsSum (GPU) = 3931478.000000CPU version (sec.): 0.052965GPU version (sec.): 0.068171Memory ops. (sec.): 0.000020Speedup: 0.78Speedup (with mem ops.): 0.78

Page 50: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Гистограмма цветов изображения (GPU)

Каждый поток обрабатывает один пиксель изображения

__global__ void hist_gpu(uint8_t *image, int width, int height, int *hist)

{size_t size = width * height;int i = blockIdx.x * blockDim.x + threadIdx.x;if (i < size) {

hist[image[i]]++;}

}

Потоки одновременно читают и записывают данные в одни и те же ячейки таблицы hist[0..255] – data race

Page 51: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

CUDA Atomic Functions

Атомарная функция (CUDA atomic function) – функция, выполняющая операцию read-modify-write (RMW) над 32 или 64 битным значением в глобальной или разделяемой памяти GPU

int atomicAdd(int* address, int val); unsigned long long int atomicAdd(unsigned long long int* address, unsigned long long int val); float atomicAdd(float* address, float val); // compute capability >= 2.0

int atomicSub(int* address, int val); int atomicMin(int* address, int val); int atomicAnd(int* address, int val); int atomicXor(int* address, int val); int atomicCAS(int* address, int compare, int val); ...

https://docs.nvidia.com/cuda/cuda-c-programming-guide/#atomic-functions

Page 52: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

atomicAdd для double (CAS-based)

__device__ double atomicAdd(double* address, double val){

unsigned long long int* addr = (unsigned long long int*)address;unsigned long long int old = *addr, assumed;

do {assumed = old;old = atomicCAS(addr, assumed,

__double_as_longlong(val +__longlong_as_double(assumed)));

} while (assumed != old);

return __longlong_as_double(old);}

int atomicCAS(int* address, int compare, int val);

1. Загружает в old значение по адресу address2. Записывает обратно значение:

(old == compare) ? val : old3. Возвращает old

Page 53: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Гистограмма цветов изображения (GPU)

__global__ void hist_gpu_atomic(uint8_t *image, int width, int height, int *hist)

{size_t size = width * height;int i = blockIdx.x * blockDim.x + threadIdx.x;

if (i < size) {atomicAdd(&hist[image[i]], 1);

}}

Page 54: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Гистограмма цветов изображения (GPU)

__global__ void hist_gpu_atomic(uint8_t *image, int width, int height, int *hist)

{size_t size = width * height;int i = blockIdx.x * blockDim.x + threadIdx.x;int stride = blockDim.x * gridDim.x;

while (i < size) {atomicAdd(&hist[image[i]], 1);i += stride;

}}

Если пикселей больше числа потоков

Page 55: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Глобальная память (Fermi/Kepler)

Compute Capability >= 2.0

Длина строки кеш-памяти L1: 128 байт

Длина строки кеш-памяти L2: 32 байта

Операция чтения данных (load)

o Default: Поиск в L1, затем в L2, далее в GMEM

o Альтернатива (–Xptxas –dlcm=cg): Поиск L2, далее в GMEM

Операция записи данных (store)

Invalidate L1, write-back L2

Page 56: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Операция обращения к памяти (load/store) выполняется всеми потоками группы (warp)

Потоки предоставляют адреса

GPU:

1. Определяет сколько запросов к памяти необходимо выполнить потокам warp’a

2. Группирует запросы (coalesce) в один (1x latency vs. 32x latency)

Поддерживаемые размеры слов: 1, 2, 4, 8 и 16 байт

Адрес слова должен быть выровнен на его размер:

o float – на границу в 4 байта

o double – на границу в 8 байт

cudaMalloc() – адрес корректно выравнен для любого предопределенного типа (по меньшей мере на границу в 256 байт)

Глобальная память

Page 57: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Caching load: aligned data

Потоки группы (warp) запросили 32 смежных корректно выравненных слова по 4 байта

Всего группе требуется: 128 байт

Данные передаются за один запрос из L1

http://gpgpu.org/wp/wp-content/uploads/2013/09/06-opti-memory.pdf

Page 58: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Non-caching load: aligned data

Потоки группы (warp) запросили 32 смежных корректно выравненных слова по 4 байта

Всего группе требуется: 128 байт

Адреса попали в 4 сегмента (четыре L2-строки)

Данные передаются за один запрос

Page 59: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Caching load: aligned data, permuted

Потоки группы (warp) запросили 32 несмежных выравненных слова по 4 байта

Всего группе требуется: 128 байт

Адреса попали в 1 строку L1

Данные передаются за один запрос

Page 60: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Non-caching load: aligned data, permuted

Потоки группы (warp) запросили 32 несмежных выравненных слова по 4 байта

Всего группе требуется: 128 байт

Данные передаются за один запрос

Page 61: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Caching load: misaligned data

Потоки группы (warp) запросили 32 смежных не выравненных слова по 4 байта

Всего группе требуется: 128 байт

Передается 256 байт – две строки L1

Page 62: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Non-caching load: misaligned data

Потоки группы (warp) запросили 32 смежных не выравненных слова по 4 байта

Всего группе требуется: 128 байт

Данные попали в 5 сегментов (5 строк L2)

Передается 160 байт – 5 строк L2

Page 63: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Caching load: same 4 byte

Потоки группы (warp) запросили одно и тоже 4-х байтовое слово

Группе требуется 4 байта

Данные попали в 1 строку L1 (128 байт), передается 128 байт

Page 64: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Non-caching load: same 4 byte

Потоки группы (warp) запросили одно и тоже 4-х байтовое слово

Группе требуется 4 байта

Данные попали в 1 сегмент L2 (32 байта), передается 32 байта

Page 65: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Сaching load: scattered

Потоки группы (warp) запросили 32 слова с шагом > 128 байт

Группе требуется 128 байт

Данные попали в N строк L1 (128 байт), передается 128N байт

Page 66: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Non-caching load: scattered

Потоки группы (warp) запросили 32 слова с шагом > 128 байт

Группе требуется 128 байт

Данные попали в N сегментов L2 (32 байта), передается 32N байт

Page 67: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Пример 1

__global__ void saxpy_1(float* x, float* y, float a, unsigned long N) {

unsigned long i = threadIdx.x + blockDim.x * blockIdx.x;if (i < N)

y[i] = y[i] + a * x[i];}

Load y[i], Load x[i], Store y[i]

Количество строк кеш-памяти?

Page 68: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Пример 1

__global__ void saxpy_1(float* x, float* y, float a, unsigned long N) {

unsigned long i = threadIdx.x + blockDim.x * blockIdx.x;if (i < N)

y[i] = y[i] + a * x[i];}

Количество строк кеш-памяти?

o Потоки обращаются по последовательным адресам

o Одна загрузка 128 байтовой строки для y

o Одна загрузка 128 байтовой строки для x

o 128 байт на запись y

X

128 байт

Пропускная способность:Bus utilization / bw efficiency =

128 / 128 = 100 %

Page 69: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Пример 2

__global__ void saxpy_2(float* x, float* y, float a, unsigned long N) {

unsigned long i = threadIdx.x + blockDim.x * blockIdx.x;if (i < N)

if (i % 2 == 0) y[i] = y[i] + a * x[i];}

Только четные индексы: Load y[i], Load x[i], Store y[i]

Количество строк кеш-памяти?

Page 70: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Пример 2

__global__ void saxpy_2(float* x, float* y, float a, unsigned long N) {

unsigned long i = threadIdx.x + blockDim.x * blockIdx.x;if (i < N)

if (i % 2 == 0) y[i] = y[i] + a * x[i];}

X

128 байт

Количество строк кеш-памяти?

o Потоки обращаются по последовательным адресам

o Одна загрузка 128 байтовой строки для y

o Одна загрузка 128 байтовой строки для x

o 128 байт на запись y

Пропускная способность используется менее эффективно:

Bus utilization / bw efficiency = 128 / 64 = 50 %

Page 71: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Пример 3

__global__ void saxpy_2(float* x, float* y, float a, unsigned long N) {

unsigned long i = threadIdx.x + blockDim.x * blockIdx.x;if (i < N)

if (i % 2 == 0) y[i] = y[i] + a * x[i];else y[i] = y[i] - a * x[i];

}

Количество строк кеш-памяти?

Page 72: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Пример 3

__global__ void saxpy_2(float* x, float* y, float a, unsigned long N) {

unsigned long i = threadIdx.x + blockDim.x * blockIdx.x;if (i < N)

if (i % 2 == 0) y[i] = y[i] + a * x[i]; else y[i] = y[i] - a * x[i];

}

Количество строк кеш-памяти?

Первая ветвь (16 потоков): загрузка 2-х строк по 128 байт

Вторая ветвь (16 потоков): данные читаются из кеша

X

128 байт

Пропускная способность используется менее эффективно:

Bus utilization / bw efficiency = 128 / 64 = 50 %

Page 73: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Пример 4

__global__ void saxpy_2(float* x, float* y, float a, unsigned long N) {

unsigned long i = threadIdx.x + blockDim.x * blockIdx.x;if (i < N) {

float yy = y[i]; float xx = x[i]; // Load to regsif (i % 2 == 0) yy = yy + a * xx;else yy = yy - a * xx; y[i] = yy; // Store

}}

Page 74: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Пример 4

__global__ void saxpy_2(float* x, float* y, float a, unsigned long N) {

unsigned long i = threadIdx.x + blockDim.x * blockIdx.x;if (i < N) {

float yy = y[i]; float xx = x[i]; // Load to regsif (i % 2 == 0) yy = yy + a * xx;else yy = yy - a * xx; y[i] = yy; // Store

}}

Потоки обращаются по последовательным адресам

Две загрузки 128 байт, одна запись 128 байт

128 байт на запись y

X

128 байт

Пропускная способность:Bus utilization / bw efficiency =

128 / 128 = 100 %

Page 75: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Пример 5

__global__ void saxpy_2(float* x, float* y, float a, unsigned long N) {

unsigned long i = threadIdx.x + blockDim.x * blockIdx.x;if (i < N / 2)

y[i] = y[i] + a * x[i]else if (i < N)

y[i] = y[i] i a * x[i]}

Page 76: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Пример 5

__global__ void saxpy_2(float* x, float* y, float a, unsigned long N) {

unsigned long i = threadIdx.x + blockDim.x * blockIdx.x;if (i < N / 2)

y[i] = y[i] + a * x[i]else if (i < N)

y[i] = y[i] i a * x[i]}

Все потоки группы (warp) попадают в одну из ветвей: две загрузки 128 байт, запись 128 байт

Потоки группы пересекают границу (N / 2):

o Первая ветвь (16 потоков): загрузка 2-х строк по 128 байт

o Вторая ветвь (16 потоков): данные читаются из кеша

X

128 байт

Пропускная способность:Bus utilization / bw efficiency =

128 / 128 = 100 %

Пропускная способность используется менее

эффективно:Bus utilization / bw efficiency =

128 / 64 = 50 %

Page 77: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Оптимизация структур

Array of Structures (AOS) vs. Structure of Arrays (SOA)

struct point {float x; float y; float z; float w;};struct point *points;cudaMalloc(&(void**)points, N * sizeof(*points));

struct points {float *x; float *y; float *z; float *w;};struct points points;cudaMalloc(&(void**)points.x, N * sizeof(float));cudaMalloc(&(void**)points.y, N * sizeof(float));cudaMalloc(&(void**)points.z, N * sizeof(float));cudaMalloc(&(void**)points.w, N * sizeof(float));

VS.

Page 78: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Оптимизация структур

Array of Structures (AOS) vs. Structure of Arrays (SOA)

struct point {float x; float y; float z; float w;};struct point *points;cudaMalloc(&(void**)points, N * sizeof(*points));

struct points {float *x; float *y; float *z; float *w;};struct points points;cudaMalloc(&(void**)points.x, N * sizeof(float));cudaMalloc(&(void**)points.y, N * sizeof(float));cudaMalloc(&(void**)points.z, N * sizeof(float));cudaMalloc(&(void**)points.w, N * sizeof(float));

VS.

Обращения по несмежным адресам, больше обращений к памяти (строкам)

Обращения по смежным адресам, меньше обращений к памяти (строкам)

Page 79: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Задача N-тел (NBody)

const float eps = 0.0001f;const float dt = 0.01f;const int N = 128 * 1024;

#define coord struct bodystruct body {

float x;float y;float z;

};

__global__ void integrate(coord *new_p, coord *new_v, coord *p, coord *v, int n, float dt)

{int index = blockIdx.x * blockDim.x + threadIdx.x; if (index >= n)

return;

// Каждый поток вычисляет силу, действующую на тело indexcoord body_pos = p[index]; coord body_vel = v[index]; coord f;f.x = 0;f.y = 0;f.z = 0;

Page 80: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Задача N-тел (NBody)

for (int i = 0; i < n; i++) {coord pi = p[i]; coord r;// Вектор от тела p[i] к телу body_posr.x = pi.x - body_pos.x; r.y = pi.y - body_pos.y; r.z = pi.z - body_pos.z;

float invDist = 1.0 / sqrtf(r.x * r.x + r.y * r.y + r.z * r.z + eps * eps);float s = invDist * invDist * invDist;// Корректируем силу - вносим вклад тела if.x += r.x * s;f.y += r.y * s;f.z += r.z * s;

}

// Корректируем параметры тела: скорость, положениеbody_vel.x += f.x * dt;body_vel.y += f.y * dt;body_vel.z += f.z * dt;body_pos.x += body_vel.x * dt;body_pos.y += body_vel.y * dt;body_pos.z += body_vel.z * dt;

new_p[index] = body_pos;new_v[index] = body_vel;

}

Page 81: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Задача N-тел (NBody)

int main(){

double tgpu = 0, tmem = 0;size_t size = sizeof(coord) * N;coord *p = (coord *)malloc(size); coord *v = (coord *)malloc(size);coord *d_p[2] = {NULL, NULL}; coord *d_v[2] = {NULL, NULL};init_rand(p, N); init_rand(v, N);

tmem = -wtime();cudaMalloc((void **)&d_p[0], size);cudaMalloc((void **)&d_p[1], size);cudaMalloc((void **)&d_v[0], size);cudaMalloc((void **)&d_v[1], size);cudaMemcpy(d_p[0], p, size, cudaMemcpyHostToDevice);cudaMemcpy(d_v[0], v, size, cudaMemcpyHostToDevice);tmem += wtime();

tgpu = -wtime();int threadsPerBlock = 1024;dim3 block(threadsPerBlock);dim3 grid((N + threadsPerBlock - 1) / threadsPerBlock); int index = 0;for (int i = 0; i < 2; i++, index ^= 1)

integrate<<<grid, block>>>(d_p[index ^ 1], d_v[index ^ 1], d_p[index], d_v[index], N, dt);cudaDeviceSynchronize();tgpu += wtime();

Page 82: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Задача N-тел (NBody)

tmem -= wtime();cudaMemcpy(p, d_p[index], size, cudaMemcpyDeviceToHost);cudaMemcpy(v, d_v[index], size, cudaMemcpyDeviceToHost);tmem += wtime();

printf("sizeof(coord) = %d\n", sizeof(coord));printf("GPU version (sec.): %.6f\n", tgpu);printf("Memory ops. (sec.): %.6f\n", tmem);printf(" Total time (sec.): %.6f\n", tgpu + tmem);

cudaFree(d_p[0]);cudaFree(d_p[1]);cudaFree(d_v[0]);cudaFree(d_v[1]);free(p);free(v);cudaDeviceReset();return 0;

}sizeof(coord) = 12GPU version (sec.): 2.382872Memory ops. (sec.): 0.252231Total time (sec.): 2.635103

Page 83: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Задача N-тел (NBody): float3

const float eps = 0.0001f;const float dt = 0.01f;const int N = 128 * 1024;

#define coord float3

__global__ void integrate(coord *new_p, coord *new_v, coord *p, coord *v, int n, float dt)

{int index = blockIdx.x * blockDim.x + threadIdx.x; if (index >= n)

return;

// Каждый поток вычисляет силу, действующую на тело indexcoord body_pos = p[index]; coord body_vel = v[index]; coord f;f.x = 0;f.y = 0;f.z = 0;

Page 84: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Задача N-тел (NBody): padding

const float eps = 0.0001f;const float dt = 0.01f;const int N = 128 * 1024;

#define coord struct bodystruct body {

float x;float y;float z; float __padding;

};

__global__ void integrate(coord *new_p, coord *new_v, coord *p, coord *v, int n, float dt)

{int index = blockIdx.x * blockDim.x + threadIdx.x; if (index >= n)

return;

// Каждый поток вычисляет силу, действующую на тело indexcoord body_pos = p[index]; coord body_vel = v[index]; coord f;f.x = 0;f.y = 0;f.z = 0;

sizeof(coord) = 16GPU version (sec.): 1.946044Memory ops. (sec.): 0.228796Total time (sec.): 2.174840

Page 85: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Задача N-тел (NBody): float4

const float eps = 0.0001f;const float dt = 0.01f;const int N = 128 * 1024;

#define coord float4

__global__ void integrate(coord *new_p, coord *new_v, coord *p, coord *v, int n, float dt)

{int index = blockIdx.x * blockDim.x + threadIdx.x; if (index >= n)

return;

// Каждый поток вычисляет силу, действующую на тело indexcoord body_pos = p[index]; coord body_vel = v[index]; coord f;f.x = 0;f.y = 0;f.z = 0;

sizeof(coord) = 16GPU version (sec.): 1.720787Memory ops. (sec.): 0.226160Total time (sec.): 1.946947

Page 86: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Задача N-тел (NBody): float4 + shared memory

__global__ void integrate(coord *new_p, coord *new_v, coord *p, coord *v, int n, float dt){

int index = blockIdx.x * blockDim.x + threadIdx.x; if (index >= n)

return;

coord body_pos = p[index];coord body_vel = v[index];coord f;f.x = 0;f.y = 0;f.z = 0;

__shared__ coord sp[block_size];

// Assert: n % block_size == 0for (int ind = 0; ind < n; ind += block_size) {

sp[threadIdx.x] = p[ind + threadIdx.x];

__syncthreads();

Page 87: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Задача N-тел (NBody): float4 + shared memory

for (int i = 0; i < block_size; i++) {// Vector from p[i] to bodycoord r;r.x = sp[i].x - body_pos.x; r.y = sp[i].y - body_pos.y; r.z = sp[i].z - body_pos.z;

float invDist = 1.0 / sqrtf(r.x * r.x + r.y * r.y + r.z * r.z + eps * eps);float s = invDist * invDist * invDist;// Add force of body if.x += r.x * s;f.y += r.y * s;f.z += r.z * s;

}__syncthreads();

}// Correct velocitybody_vel.x += f.x * dt; body_vel.y += f.y * dt; body_vel.z += f.z * dt;body_pos.x += body_vel.x * dt; body_pos.y += body_vel.y * dt; body_pos.z += body_vel.z * dt;

new_p[index] = body_pos; new_v[index] = body_vel;}

GPU version (sec.): 2.055868Memory ops. (sec.): 0.249816Total time (sec.): 2.305684

Page 88: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Редукция (reduction)

Задан массив v[0..n - 1] из n элементов и ассоциативная операция

Необходимо вычислить результаты редукции

r = v[0] v[1] … v[n - 1]

Пример:

v[0..5] = [5, 8, 3, 12, 1, 7]

r = ((((5 + 8) + 3) + 12) + 1) + 7 = 36

Page 89: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Последовательная версия (float)

void reduce_cpu(float *v, int n, float *sum){

float s = 0.0;for (int i = 0; i < n; i++)

s += v[i]; *sum = s;

}

int n = 10000;for (size_t i = 0; i < n; i++)

v[i] = i + 1.0;

reduce_cpu(v, n, &sum);

# Real sum = 50005000Sum (CPU) = 50002896.000000, err = 2104.000000

Floating-point Summation // http://www.drdobbs.com/floating-point-summation/184403224

David Goldberg. What Every Computer Scientist Should Know About Floating-Point Arithmetic // ACM Computing Surveys, Vol. 23, #1, March 1991, pp. 5-48.

Page 90: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Последовательная версия (int)

void reduce_cpu(int *v, int n, int *sum){

int s = 0.0;for (int i = 0; i < n; i++)

s += v[i]; *sum = s;

}

Page 91: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Параллельная редукция v1

Каждый блок потоков обрабатывает часть массива v[0..n - 1]

Поток 0 каждого блока сохраняет результат редукции в массив per_block_sum[0..blocks - 1]

Для выполнения редукции массива per_block_sum[] нужен еще один шаг на GPU или CPU:

Block 0 Block 1 Block 2

per_block_sum[]

cudaMemcpy(sums, per_block_sum, sizeof(int) * blocks, cudaMemcpyDeviceToHost);int sum_gpu = 0;for (int i = 0; i < blocks; i++)

sum_gpu += sums[i];https://docs.nvidia.com/cuda/samples/6_Advanced/reduction/doc/reduction.pdf

Page 92: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Параллельная редукция v1

Каждый поток по своему номеру tid определяет с кем ему взаимодействовать

На шаге 1 каждый 2-й поток взаимодействует с соседом справа на расстоянии s = 1

На шаге 2 каждый 4-й поток взаимодействует с соседом справа на расстоянии s = 2

На шаге 3 каждый 8-й поток взаимодействует с соседом справа на расстоянии s = 4

Всего шагов O(logn)https://docs.nvidia.com/cuda/samples/6_Advanced/reduction/doc/reduction.pdf

for (int s = 1; s < blockDim.x; s *= 2) {if (tid % (2 * s) == 0)

sdata[tid] += sdata[tid + s];}

Page 93: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Параллельная редукция v1const int block_size = 1024;const int n = 4 * (1 << 20);

int main(){

size_t size = sizeof(int) * n;int *v = (int *)malloc(size);for (size_t i = 0; i < n; i++)

v[i] = i + 1;

int sum;reduce_cpu(v, n, &sum);

/* Allocate on device */int threads_per_block = block_size;int blocks = (n + threads_per_block - 1) / threads_per_block;int *dv, *per_block_sum;int *sums = (int *)malloc(sizeof(int) * blocks);tmem = -wtime();cudaMalloc((void **)&per_block_sum, sizeof(int) * blocks);cudaMalloc((void **)&dv, size);cudaMemcpy(dv, v, size, cudaMemcpyHostToDevice);tmem += wtime();

Page 94: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Параллельная редукция v1

/* Compute per block sum: stage 1 */tgpu = -wtime();reduce_per_block<<<blocks, threads_per_block>>>(dv, n, per_block_sum);cudaDeviceSynchronize();tgpu += wtime();

tmem = -wtime();cudaMemcpy(sums, per_block_sum, sizeof(int) * blocks, cudaMemcpyDeviceToHost);tmem += wtime();

/* Compute block sum: stage 2 */tgpu -= wtime();int sum_gpu = 0;for (int i = 0; i < blocks; i++)

sum_gpu += sums[i];tgpu += wtime();

printf("CPU version (sec.): %.6f\n", tcpu);printf("GPU version (sec.): %.6f\n", tgpu);printf("GPU bandwidth (GiB/s): %.2f\n", 1.0e-9 * size / (tgpu + tmem));printf("Speedup: %.2f\n", tcpu / tgpu);printf("Speedup (with mem ops.): %.2f\n", tcpu / (tgpu + tmem));

Page 95: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Параллельная редукция v1

__global__ void reduce_per_block(int *v, int n, int *per_block_sum){

__shared__ int sdata[block_size];int tid = threadIdx.x;int i = blockIdx.x * blockDim.x + threadIdx.x;

if (i < n) {sdata[tid] = v[i];__syncthreads();

for (int s = 1; s < blockDim.x; s *= 2) {if (tid % (2 * s) == 0)

sdata[tid] += sdata[tid + s];__syncthreads();

}if (tid == 0)

per_block_sum[blockIdx.x] = sdata[0];}

}

CUDA kernel launch with 4096 blocks of 1024 threadsSum (CPU) = 2097152Sum (GPU) = 2097152CPU version (sec.): 0.005956GPU version (sec.): 0.002198GPU bandwidth (GiB/s): 7.56Speedup: 2.71Speedup (with mem ops.): 2.68

Page 96: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Параллельная редукция v1

__global__ void reduce_per_block(int *v, int n, int *per_block_sum){

__shared__ int sdata[block_size];int tid = threadIdx.x;int i = blockIdx.x * blockDim.x + threadIdx.x;

if (i < n) {sdata[tid] = v[i];__syncthreads();

for (int s = 1; s < blockDim.x; s *= 2) {if (tid % (2 * s) == 0)

sdata[tid] += sdata[tid + s];__syncthreads();

}if (tid == 0)

per_block_sum[blockIdx.x] = sdata[0];}

}

Результат условия зависит от номера потока – часть потоков деактивируется

(control flow divergence)

Page 97: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Параллельная редукция v2

for (int s = 1; s < blockDim.x; s *= 2) {if (tid % (2 * s) == 0)

sdata[tid] += sdata[tid + s];__syncthreads();

}

for (int s = 1; s < blockDim.x; s *= 2) {int index = 2 * s * tid;if (index < blockDim.x)

sdata[index] += sdata[index + s];__syncthreads();

}

Сontrol flow divergence!

https://docs.nvidia.com/cuda/samples/6_Advanced/reduction/doc/reduction.pdf

Page 98: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Параллельная редукция v2

__global__ void reduce_per_block(int *v, int n, int *per_block_sum){

__shared__ int sdata[block_size];int tid = threadIdx.x;int i = blockIdx.x * blockDim.x + threadIdx.x;

if (i < n) {sdata[tid] = v[i];__syncthreads();

for (int s = 1; s < blockDim.x; s *= 2) {int index = 2 * s * tid;

if (index < blockDim.x)sdata[index] += sdata[index + s];

__syncthreads();}if (tid == 0)

per_block_sum[blockIdx.x] = sdata[0];}

}

CUDA kernel launch with 4096 blocks of 1024 threadsSum (CPU) = 2097152Sum (GPU) = 2097152CPU version (sec.): 0.006481GPU version (sec.): 0.001968GPU bandwidth (GiB/s): 8.44Speedup: 3.29Speedup (with mem ops.): 3.26

Page 99: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Параллельная редукция v2

__global__ void reduce_per_block(int *v, int n, int *per_block_sum){

__shared__ int sdata[block_size];int tid = threadIdx.x;int i = blockIdx.x * blockDim.x + threadIdx.x;

if (i < n) {sdata[tid] = v[i];__syncthreads();

for (int s = 1; s < blockDim.x; s *= 2) {int index = 2 * s * tid;

if (index < blockDim.x)sdata[index] += sdata[index + s];

__syncthreads();}if (tid == 0)

per_block_sum[blockIdx.x] = sdata[0];}

}

Обращение к разделяемой памяти выполняется через 32 параллельно функционирующих банка

Каждый банк 4 байта (настраивается)

Номер банка = адрес % 32

Одновременный доступ к одному банку сериализуется!

TID S = 1 S = 2 S = 4

0 0-1 // banks 0, 1 0-2 // banks 0, 1 0-4 // banks 0, 1

1 2-3 // banks 2, 3 4-6 // banks 2, 3

2 4-5 // banks 4, 5 8-10 // banks 4, 5

3 6-7 // banks 6, 7 12-14 // banks 6, 7 24-28 // banks 24, 28

4 8-9 // banks 8, 9 16-18 // banks 8, 9 32-36 // banks 0, 2

8 32-34 // banks 0, 2

9 36-38 // banks 4, 6

http://gpgpu.org/wp/wp-content/uploads/2013/09/08-opti-smem-instr.pdf

Page 100: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Shared memory bank conflicts

http://acceleware.com/blog/maximizing-shared-memory-bandwidth-nvidia-kepler-gpus

Page 101: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Параллельная редукция v3

for (int s = blockDim.x / 2; s > 0; s >>= 1) {if (tid < s)

sdata[tid] += sdata[tid + s];__syncthreads();

}

for (int s = 1; s < blockDim.x; s *= 2) {int index = 2 * s * tid;if (index < blockDim.x)

sdata[index] += sdata[index + s];__syncthreads();

}

Bank conflicts

Page 102: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Параллельная редукция v3

CUDA kernel launch with 4096 blocks of 1024 threadsSum (CPU) = 2097152Sum (GPU) = 2097152CPU version (sec.): 0.004661GPU version (sec.): 0.001163GPU bandwidth (GiB/s): 14.17Speedup: 4.01Speedup (with mem ops.): 3.94

__global__ void reduce_per_block(int *v, int n, int *per_block_sum){

__shared__ int sdata[block_size];int tid = threadIdx.x;int i = blockIdx.x * blockDim.x + threadIdx.x;

if (i < n) {sdata[tid] = v[i];__syncthreads();

for (int s = blockDim.x / 2; s > 0; s >>= 1) {if (tid < s)

sdata[tid] += sdata[tid + s];__syncthreads();

}if (tid == 0)

per_block_sum[blockIdx.x] = sdata[0];}

}

Page 103: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Conway's Game of Life

Игра «Жизнь» (Game of Life, Дж. Конвей, 1970)

Игровое поле — размеченная на клетки плоскость

Каждая клетка может находиться в двух состояниях:

«живая» и «мёртвая», и имеет восемь соседей

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

первым поколением. Каждое следующее поколение

рассчитывается на основе предыдущего:

1) в мертвой клетке, рядом с которой три живые клетки,

зарождается жизнь

2) если у живой клетки есть две или три живые соседки,

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

(соседей < 2 или > 3) клетка умирает

http://en.wikipedia.org/wiki/Conway's_Game_of_Life

Page 104: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Периодические граничные условия (periodic boundary conditions)

Как вычислять состояния граничных ячеек (ячеек слева, справа, снизу, сверху может не существовать)?

Одно из решений –периодические граничные условия (periodic boundary conditions)

Игровое поле бесконечно продолжается по всем направлениям

В массиве требуется хранить теневые ячейки (ghost cells, shadow cells)

https://www.pdc.kth.se/education/tutorials/summer-school/mpi-exercises/mpi-lab-1-program-structure-and-point-to-point-communication-

in-mpi/background-for-the-game-of-life

0

1

N + 1

N

0 1 N + 1N

Page 105: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Последовательная реализация (1_gol/gol.c)

#define IND(i, j) ((i) * (N + 2) + (j))

enum {N = 1024,ITERS_MAX = 1 << 10

};

typedef uint8_t cell_t;

int main(int argc, char* argv[]){

// Grid with periodic boundary conditions (ghost cells)size_t ncells = (N + 2) * (N + 2);size_t size = sizeof(cell_t) * ncells;cell_t *grid = malloc(size);cell_t *newgrid = malloc(size);

// Initial populationsrand(0);for (int i = 1; i <= N; i++)

for (int j = 1; j <= N; j++)grid[IND(i, j)] = rand() % 2;

0

1

N + 1

N

0 1 N + 1N

0

1

N + 1

N

0 1 N + 1N

grid[N + 2][N + 2] newgrid[N + 2][N + 2]

Page 106: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Последовательная реализация (продолжение)

double t = wtime();int iter;for (iter = 0; iter < ITERS_MAX; iter++) {

// Copy ghost columnsfor (int i = 1; i <= N; i++) {

grid[IND(i, 0)] = grid[IND(i, N)]; // left ghost columngrid[IND(i, N + 1)] = grid[IND(i, 1)]; // right ghost column

}// Copy ghost rowsfor (int i = 0; i <= N + 1; i++) {

grid[IND(0, i)] = grid[IND(N, i)]; // top ghost rowgrid[IND(N + 1, i)] = grid[IND(1, i)]; // bottom ghost row

}

0

1

N + 1

N

0 1 N + 1N

Page 107: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Последовательная реализация (продолжение)

for (int i = 1; i <= N; i++) {for (int j = 1; j <= N; j++) {

int nneibs = grid[IND(i + 1, j)] + grid[IND(i - 1, j)] +grid[IND(i, j + 1)] + grid[IND(i, j - 1)] +grid[IND(i + 1, j + 1)] + grid[IND(i - 1, j - 1)] +grid[IND(i - 1, j + 1)] + grid[IND(i + 1, j - 1)];

cell_t state = grid[IND(i, j)];cell_t newstate = state;if (state == 1 && nneibs < 2)

newstate = 0;else if (state == 1 && (nneibs == 2 || nneibs == 3))

newstate = 1;else if (state == 1 && nneibs > 3)

newstate = 0;else if (state == 0 && nneibs == 3)

newstate = 1;newgrid[IND(i, j)] = newstate;

}}cell_t *p = grid; grid = newgrid; newgrid = p;

}t = wtime() - t;

Page 108: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Последовательная реализация (окончание)

size_t total = 0;for (int i = 1; i <= N; i++) {

for (int j = 1; j <= N; j++)total += grid[IND(i, j)];

}printf("Game of Life: N = %d, iterations = %d\n", N, iter);printf("Total alive cells: %lu\n", total);printf("Iters per sec.: %.2f\n", iter / t);printf("Total time (sec.): %.6f\n", t);

free(grid);free(newgrid);return 0;

}

Game of Life: N = 1024, iterations = 1024Total alive cells: 47026Iters per sec.: 280.05Total time (sec.): 3.656496

Cluster Oak / cngpu1 (Intel Core i5-3320M)

Page 109: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Модификация последовательной реализации (2_gol_state)

Game of Life: N = 1024, iterations = 1024Total alive cells: 47026Iters per sec.: 448.07Total time (sec.): 2.285372 Speedup x1.6

Cluster Oak / cngpu1 (Intel Core i5-3320M)

int states[2][9] = {{0, 0, 0, 1, 0, 0, 0, 0, 0}, /* New states for a dead cell */{0, 0, 1, 1, 0, 0, 0, 0, 0} /* New states for an alive cell */

};

for (int i = 1; i <= N; i++) {for (int j = 1; j <= N; j++) {

int nneibs = grid[IND(i + 1, j)] + grid[IND(i - 1, j)] +grid[IND(i, j + 1)] + grid[IND(i, j - 1)] +grid[IND(i + 1, j + 1)] + grid[IND(i - 1, j - 1)] +grid[IND(i - 1, j + 1)] + grid[IND(i + 1, j - 1)];

cell_t state = grid[IND(i, j)];newgrid[IND(i, j)] = states[state][nneibs];

}}

Page 110: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Реализация на CUDA (2_gol_cuda)

#define IND(i, j) ((i) * (N + 2) + (j))enum {

N = 1024,ITERS_MAX = 1 << 10,BLOCK_SIZE = 16

};

typedef uint8_t cell_t;

int main(int argc, char* argv[]){

// Grid with periodic boundary conditions (ghost cells)size_t ncells = (N + 2) * (N + 2);size_t size = sizeof(cell_t) * ncells;cell_t *grid = (cell_t *)malloc(size);

// Initial populationsrand(0);for (int i = 1; i <= N; i++)

for (int j = 1; j <= N; j++)grid[IND(i, j)] = rand() % 2;

Page 111: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Реализация на CUDA (2_gol_cuda)

cell_t *d_grid, *d_newgrid;double tmem = -wtime();cudaMalloc((void **)&d_grid, size);cudaMalloc((void **)&d_newgrid, size);cudaMemcpy(d_grid, grid, size, cudaMemcpyHostToDevice);tmem += wtime();

// 1d drids for copying ghost cellsdim3 block(BLOCK_SIZE, 1, 1);dim3 cols_grid((N + block.x - 1) / block.x, 1, 1);dim3 rows_grid((N + 2 + block.x - 1) / block.x, 1, 1);

// 2d grid for updating cells: one thread per celldim3 block2d(BLOCK_SIZE, BLOCK_SIZE, 1);int nblocks = (N + BLOCK_SIZE - 1) / BLOCK_SIZE; dim3 grid2d(nblocks, nblocks, 1);

Page 112: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Реализация на CUDA (2_gol_cuda)

double t = wtime();int iter = 0;for (iter = 0; iter < ITERS_MAX; iter++) {

// Copy ghost cells: 1d grid for rows, 1d grid for columnscopy_ghost_cols<<<cols_grid, block>>>(d_grid, N);copy_ghost_rows<<<rows_grid, block>>>(d_grid, N);

// Update cells: 2d gridupdate_cells<<<grid2d, block2d>>>(d_grid, d_newgrid, N);

// Swap gridscell_t *p = d_grid; d_grid = d_newgrid; d_newgrid = p;

}cudaDeviceSynchronize();t = wtime() - t;

tmem -= wtime();cudaMemcpy(grid, d_grid, size, cudaMemcpyDeviceToHost);tmem += wtime();

Page 113: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Реализация на CUDA (2_gol_cuda)

__global__ void copy_ghost_rows(cell_t *grid, int n){

int i = blockIdx.x * blockDim.x + threadIdx.x;if (i <= n + 1) {

// Bottom ghost row: [N + 1][0..N + 1] <== [1][0..N + 1]grid[IND(N + 1, i)] = grid[IND(1, i)];// Top ghost row: [0][0..N + 1] <== [N][0..N + 1]grid[IND(0, i)] = grid[IND(N, i)];

}}

__global__ void copy_ghost_cols(cell_t *grid, int n){

int i = blockIdx.x * blockDim.x + threadIdx.x + 1;if (i <= n) {

// Right ghost column: [1..N][N + 1] <== [1..N][1]grid[IND(i, N + 1)] = grid[IND(i, 1)];// Left ghost column: [1..N][1] <== [1..N][N]grid[IND(i, 0)] = grid[IND(i, N)];

}}

Page 114: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Реализация на CUDA (2_gol_cuda)

__global__ void update_cells(cell_t *grid, cell_t *newgrid, int n){

int i = blockIdx.y * blockDim.y + threadIdx.y + 1;int j = blockIdx.x * blockDim.x + threadIdx.x + 1;

if (i <= n && j <= n) {int states[2][9] = {

{0, 0, 0, 1, 0, 0, 0, 0, 0}, /* New states for a dead cell */{0, 0, 1, 1, 0, 0, 0, 0, 0} /* New states for an alive cell */

};

int nneibs = grid[IND(i + 1, j)] + grid[IND(i - 1, j)] +grid[IND(i, j + 1)] + grid[IND(i, j - 1)] +grid[IND(i + 1, j + 1)] + grid[IND(i - 1, j - 1)] +grid[IND(i - 1, j + 1)] + grid[IND(i + 1, j - 1)];

cell_t state = grid[IND(i, j)];newgrid[IND(i, j)] = states[state][nneibs];

}}

Page 115: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Реализация на CUDA (2_gol_cuda)

size_t total = 0;for (int i = 1; i <= N; i++) {

for (int j = 1; j <= N; j++)total += grid[IND(i, j)];

}printf("Game of Life: N = %d, iterations = %d\n", N, iter);printf("Total alive cells: %lu\n", total);printf("Iterations time (sec.): %.6f\n", t);printf("GPU memory ops. time (sec.): %.6f\n", tmem);printf("Iters per sec.: %.2f\n", iter / t);printf("Total time (sec.): %.6f\n", t + tmem);

free(grid);cudaFree(d_grid);cudaFree(d_newgrid); return 0;

}

Game of Life: N = 1024, iterations = 1024Total alive cells: 47026Iterations time (sec.): 0.911321GPU memory ops. time (sec.): 0.238265Iters per sec.: 1123.64Total time (sec.): 1.149586 Speedup 1.99

Cluster Oak / cngpu1 (GeForce GTX 680)

Page 116: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Реализация на CUDA v2 (2_gol_cuda_constant)

__constant__ int states[2][9] = {{0, 0, 0, 1, 0, 0, 0, 0, 0}, /* New states for a dead cell */{0, 0, 1, 1, 0, 0, 0, 0, 0} /* New states for an alive cell */

};

__global__ void update_cells(cell_t *grid, cell_t *newgrid, int n){

int i = blockIdx.y * blockDim.y + threadIdx.y + 1;int j = blockIdx.x * blockDim.x + threadIdx.x + 1;

if (i <= n && j <= n) { int nneibs = grid[IND(i + 1, j)] + grid[IND(i - 1, j)] +

grid[IND(i, j + 1)] + grid[IND(i, j - 1)] +grid[IND(i + 1, j + 1)] + grid[IND(i - 1, j - 1)] +grid[IND(i - 1, j + 1)] + grid[IND(i + 1, j - 1)];

cell_t state = grid[IND(i, j)];newgrid[IND(i, j)] = states[state][nneibs];

}}

Page 117: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Реализация на CUDA v2 (2_gol_cuda_constant)

__constant__ int states[2][9] = {{0, 0, 0, 1, 0, 0, 0, 0, 0}, /* New states for a dead cell */{0, 0, 1, 1, 0, 0, 0, 0, 0} /* New states for an alive cell */

};

__global__ void update_cells(cell_t *grid, cell_t *newgrid, int n){

int i = blockIdx.y * blockDim.y + threadIdx.y + 1;int j = blockIdx.x * blockDim.x + threadIdx.x + 1;

if (i <= n && j <= n) { int nneibs = grid[IND(i + 1, j)] + grid[IND(i - 1, j)] +

grid[IND(i, j + 1)] + grid[IND(i, j - 1)] +grid[IND(i + 1, j + 1)] + grid[IND(i - 1, j - 1)] +grid[IND(i - 1, j + 1)] + grid[IND(i + 1, j - 1)];

cell_t state = grid[IND(i, j)];newgrid[IND(i, j)] = states[state][nneibs];

}}

Game of Life: N = 1024, iterations = 1024Total alive cells: 47026Iterations time (sec.): 0.221800GPU memory ops. time (sec.): 0.231555Iters per sec.: 4616.77Total time (sec.): 0.453355 Speedup 5.0

Cluster Oak / cngpu1 (GeForce GTX 680)

Page 118: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

CUDA GPU Occupancy Calculatorhttp://developer.download.nvidia.com/compute/cuda/CUDA_Occupancy_calculator.xls

В программеблоки

1d: 1x162d: 16x16

Page 119: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Реализация на CUDA v2 (2_gol_cuda_occupancy)

enum {BLOCK_1D_SIZE = 1024, BLOCK_2D_SIZE = 32

};

int main(int argc, char* argv[]){

// ...// 1d grids for copying ghost cellsdim3 block(BLOCK_1D_SIZE, 1, 1);dim3 cols_grid((N + block.x - 1) / block.x, 1, 1);dim3 rows_grid((N + 2 + block.x - 1) / block.x, 1, 1);

// 2d grid for updating cells: one thread per celldim3 block2d(BLOCK_2D_SIZE, BLOCK_2D_SIZE, 1);int nblocks = (N + BLOCK_2D_SIZE - 1) / BLOCK_2D_SIZE; dim3 grid2d(nblocks, nblocks, 1);// ...

}

Page 120: Технология NVIDIA CUDA · Весенний семестр, 2016. ... (Kepler, 2304 cores, GDDR5 3 GB) AMD Radeon HD 8970 (2048 cores, GDDR5 3 GB) Гибридные вычислительные

Реализация на CUDA v2 (2_gol_cuda_occupancy)

enum {BLOCK_1D_SIZE = 1024, BLOCK_2D_SIZE = 32

};

int main(int argc, char* argv[]){

// ...// 1d grids for copying ghost cellsdim3 block(BLOCK_1D_SIZE, 1, 1);dim3 cols_grid((N + block.x - 1) / block.x, 1, 1);dim3 rows_grid((N + 2 + block.x - 1) / block.x, 1, 1);

// 2d grid for updating cells: one thread per celldim3 block2d(BLOCK_2D_SIZE, BLOCK_2D_SIZE, 1);int nblocks = (N + BLOCK_2D_SIZE - 1) / BLOCK_2D_SIZE; dim3 grid2d(nblocks, nblocks, 1);// ...

}

Game of Life: N = 1024, iterations = 1024Total alive cells: 47026Iterations time (sec.): 0.169622GPU memory ops. time (sec.): 0.238457Iters per sec.: 6036.95Total time (sec.): 0.408079 Speedup 5.7

Cluster Oak / cngpu1 (GeForce GTX 680)