CUDA C/C++ Basics

advertisement
CUDA C/C++ Basics
Supercomputing 2011 Tutorial
Cyril Zeller, NVIDIA Corporation
© NVIDIA Corporation 2011
What is CUDA?
 CUDA Architecture

Expose GPU computing for general purpose
 Retain performance
 CUDA C/C++

Based on industry-standard C/C++
 Small set of extensions to enable heterogeneous programming
 Straightforward APIs to manage devices, memory etc.
 This session introduces CUDA C/C++
© NVIDIA Corporation 2011
Introduction to CUDA C/C++
 What will you learn in this session?
Start from “Hello World!”
 Write and execute C code on the GPU
 Manage GPU memory
 Manage communication and synchronization

© NVIDIA Corporation 2011
Prerequisites
 You (probably) need experience with C or C++
 You don’t need GPU experience
 You don’t need parallel programming experience
 You don’t need graphics experience
© NVIDIA Corporation 2011
Heterogeneous Computing
Blocks
Threads
CONCEPTS
Indexing
Shared memory
__syncthreads()
Asynchronous operation
Handling errors
Managing devices
© NVIDIA Corporation 2011
CONCEPTS
Heterogeneous Computing
Blocks
Threads
Indexing
Shared memory
__syncthreads()
Asynchronous operation
HELLO WORLD!
© NVIDIA Corporation 2011
Handling errors
Managing devices
Heterogeneous Computing
 Terminology:

Host
The CPU and its memory (host memory)
 Device The GPU and its memory (device memory)
Host
© NVIDIA Corporation 2011
Device
Heterogeneous Computing
#include <iostream>
#include <algorithm>
using namespace std;
#define N
1024
#define RADIUS 3
#define BLOCK_SIZE 16
__global__ void stencil_1d(int *in, int *out) {
__shared__ int temp[BLOCK_SIZE + 2 * RADIUS];
int gindex = threadIdx.x + blockIdx.x * blockDim.x;
int lindex = threadIdx.x + RADIUS;
// Read input elements into shared memory
temp[lindex] = in[gindex];
if (threadIdx.x < RADIUS) {
temp[lindex - RADIUS] = in[gindex - RADIUS];
temp[lindex + BLOCK_SIZE] = in[gindex + BLOCK_SIZE];
}
device code
// Synchronize (ensure all the data is available)
__syncthreads();
parallel function
// Apply the stencil
int result = 0;
for (int offset = -RADIUS ; offset <= RADIUS ; offset++)
result += temp[lindex + offset];
// Store the result
out[gindex] = result;
}
void fill_ints(int *x, int n) {
fill_n(x, n, 1);
}
serial function
int main(void) {
int *in, *out;
// host copies of a, b, c
int *d_in, *d_out;
// device copies of a, b, c
int size = (N + 2*RADIUS) * sizeof(int);
// Alloc space for host copies and setup values
in = (int *)malloc(size); fill_ints(in, N + 2*RADIUS);
out = (int *)malloc(size); fill_ints(out, N + 2*RADIUS);
// Alloc space for device copies
cudaMalloc((void **)&d_in, size);
cudaMalloc((void **)&d_out, size);
host code
// Copy to device
cudaMemcpy(d_in, in, size, cudaMemcpyHostToDevice);
cudaMemcpy(d_out, out, size, cudaMemcpyHostToDevice);
// Launch stencil_1d() kernel on GPU
stencil_1d<<<N/BLOCK_SIZE,BLOCK_SIZE>>>(d_in + RADIUS, d_out + RADIUS);
// Copy result back to host
cudaMemcpy(out, d_out, size, cudaMemcpyDeviceToHost);
// Cleanup
free(in); free(out);
cudaFree(d_in); cudaFree(d_out);
return 0;
}
© NVIDIA Corporation 2011
serial code
parallel code
serial code
Simple Processing Flow
PCI Bus
1. Copy input data from CPU memory to GPU
memory
© NVIDIA Corporation 2011
Simple Processing Flow
PCI Bus
1. Copy input data from CPU memory to GPU
memory
2. Load GPU code and execute it,
caching data on chip for performance
© NVIDIA Corporation 2011
Simple Processing Flow
PCI Bus
1. Copy input data from CPU memory to GPU
memory
2. Load GPU program and execute,
caching data on chip for performance
3. Copy results from GPU memory to CPU
memory
© NVIDIA Corporation 2011
Hello World!
int main(void) {
printf("Hello World!\n");
return 0;
}
Output:
 Standard C that runs on the host
 NVIDIA compiler (nvcc) can be used to compile
programs with no device code
© NVIDIA Corporation 2011
$ nvcc
hello_world.cu
$ a.out
Hello World!
$
Hello World! with Device Code
__global__ void mykernel(void) {
}
int main(void) {
mykernel<<<1,1>>>();
printf("Hello World!\n");
return 0;
}
 Two new syntactic elements…
© NVIDIA Corporation 2011
Hello World! with Device Code
__global__ void mykernel(void) {
}
 CUDA C/C++ keyword __global__ indicates a function that:

Runs on the device
 Is called from host code
 nvcc separates source code into host and device components

Device functions (e.g. mykernel()) processed by NVIDIA compiler
 Host functions (e.g. main()) processed by standard host compiler
- gcc, cl.exe
© NVIDIA Corporation 2011
Hello World! with Device Code
mykernel<<<1,1>>>();
 Triple angle brackets mark a call from host code to device code
Also called a “kernel launch”
 We’ll return to the parameters (1,1) in a moment

 That’s all that is required to execute a function on the GPU!
© NVIDIA Corporation 2011
Hello World! with Device Code
__global__ void mykernel(void) {
}
int main(void) {
mykernel<<<1,1>>>();
printf("Hello World!\n");
return 0;
}
 mykernel() does nothing, somewhat
anticlimactic!
© NVIDIA Corporation 2011
Output:
$ nvcc hello.cu
$ a.out
Hello World!
$
Parallel Programming in CUDA C/C++
 But wait… GPU computing is about massive
parallelism!
 We need a more interesting example…
 We’ll start by adding two integers and build up
to vector addition
a
© NVIDIA Corporation 2011
b
c
Addition on the Device
 A simple kernel to add two integers
__global__ void add(int *a, int *b, int *c) {
*c = *a + *b;
}
 As before __global__ is a CUDA C/C++ keyword meaning

add() will execute on the device

add() will be called from the host
© NVIDIA Corporation 2011
Addition on the Device
 Note that we use pointers for the variables
__global__ void add(int *a, int *b, int *c) {
*c = *a + *b;
}
 add() runs on the device, so a, b and c must point to device memory
 We need to allocate memory on the GPU
© NVIDIA Corporation 2011
Memory Management
 Host and device memory are separate entities

Device pointers point to GPU memory
May be passed to/from host code
May not be dereferenced in host code

Host pointers point to CPU memory
May be passed to/from device code
May not be dereferenced in device code
 Simple CUDA API for handling device memory
cudaMalloc(), cudaFree(), cudaMemcpy()
 Similar to the C equivalents malloc(), free(), memcpy()

© NVIDIA Corporation 2011
Addition on the Device: add()
 Returning to our add() kernel
__global__ void add(int *a, int *b, int *c) {
*c = *a + *b;
}
 Let’s take a look at main()…
© NVIDIA Corporation 2011
Addition on the Device: main()
int main(void) {
int a, b, c;
int *d_a, *d_b, *d_c;
int size = sizeof(int);
// host copies of a, b, c
// device copies of a, b, c
// Allocate space for device copies of a, b, c
cudaMalloc((void **)&d_a, size);
cudaMalloc((void **)&d_b, size);
cudaMalloc((void **)&d_c, size);
// Setup input values
a = 2;
b = 7;
© NVIDIA Corporation 2011
Addition on the Device: main()
// Copy inputs to device
cudaMemcpy(d_a, &a, size, cudaMemcpyHostToDevice);
cudaMemcpy(d_b, &b, size, cudaMemcpyHostToDevice);
// Launch add() kernel on GPU
add<<<1,1>>>(d_a, d_b, d_c);
// Copy result back to host
cudaMemcpy(&c, d_c, size, cudaMemcpyDeviceToHost);
// Cleanup
cudaFree(d_a); cudaFree(d_b); cudaFree(d_c);
return 0;
}
© NVIDIA Corporation 2011
CONCEPTS
Heterogeneous Computing
Blocks
Threads
Indexing
Shared memory
__syncthreads()
Asynchronous operation
RUNNING IN PARALLEL
© NVIDIA Corporation 2011
Handling errors
Managing devices
Moving to Parallel
 GPU computing is about massive parallelism

So how do we run code in parallel on the device?
add<<< 1, 1 >>>();
add<<< N, 1 >>>();
 Instead of executing add() once, execute N times in parallel
© NVIDIA Corporation 2011
Vector Addition on the Device
 With add() running in parallel we can do vector addition
 Terminology: each parallel invocation of add() is referred to as a block

The set of blocks is referred to as a grid
 Each invocation can refer to its block index using blockIdx.x
__global__ void add(int *a, int *b, int *c) {
c[blockIdx.x] = a[blockIdx.x] + b[blockIdx.x];
}
 By using blockIdx.x to index into the array, each block handles a
different element of the array
© NVIDIA Corporation 2011
Vector Addition on the Device
__global__ void add(int *a, int *b, int *c) {
c[blockIdx.x] = a[blockIdx.x] + b[blockIdx.x];
}
 On the device, each block can execute in parallel:
Block 0
c[0]
= a[0] + b[0];
Block 2
c[2]
© NVIDIA Corporation 2011
= a[2] + b[2];
Block 1
c[1]
= a[1] + b[1];
Block 3
c[3]
= a[3] + b[3];
Vector Addition on the Device: add()
 Returning to our parallelized add() kernel
__global__ void add(int *a, int *b, int *c) {
c[blockIdx.x] = a[blockIdx.x] + b[blockIdx.x];
}
 Let’s take a look at main()…
© NVIDIA Corporation 2011
Vector Addition on the Device: main()
#define N 512
int main(void) {
int *a, *b, *c;
int *d_a, *d_b, *d_c;
int size = N * sizeof(int);
// Alloc space for device
cudaMalloc((void **)&d_a,
cudaMalloc((void **)&d_b,
cudaMalloc((void **)&d_c,
// Alloc
a = (int
b = (int
c = (int
© NVIDIA Corporation 2011
// host copies of a, b, c
// device copies of a, b, c
copies of a, b, c
size);
size);
size);
space for host copies of a, b, c and setup input values
*)malloc(size); random_ints(a, N);
*)malloc(size); random_ints(b, N);
*)malloc(size);
Vector Addition on the Device: main()
// Copy inputs to device
cudaMemcpy(d_a, a, size, cudaMemcpyHostToDevice);
cudaMemcpy(d_b, b, size, cudaMemcpyHostToDevice);
// Launch add() kernel on GPU with N blocks
add<<<N,1>>>(d_a, d_b, d_c);
// Copy result back to host
cudaMemcpy(c, d_c, size, cudaMemcpyDeviceToHost);
// Cleanup
free(a); free(b); free(c);
cudaFree(d_a); cudaFree(d_b); cudaFree(d_c);
return 0;
}
© NVIDIA Corporation 2011
Review (1 of 2)
 Difference between host and device

Host
CPU
 Device GPU
 Using __global__ to declare a function as device code

Executes on the device
 Called from the host
 Passing parameters from host code to a device function
© NVIDIA Corporation 2011
Review (2 of 2)
 Basic device memory management



cudaMalloc()
cudaMemcpy()
cudaFree()
 Launching parallel kernels

Launch N copies of add() with add<<<N,1>>>(…);
 Use blockIdx.x to access block index
© NVIDIA Corporation 2011
CONCEPTS
Heterogeneous Computing
Blocks
Threads
Indexing
Shared memory
__syncthreads()
Asynchronous operation
INTRODUCING THREADS
© NVIDIA Corporation 2011
Handling errors
Managing devices
CUDA Threads
 Terminology: a block can be split into parallel threads
 Let’s change add() to use parallel threads instead of parallel blocks
__global__ void add(int *a, int *b, int *c) {
c[threadIdx.x]
a[threadIdx.x]
+ b[threadIdx.x];
c[blockIdx.x] ==a[blockIdx.x]
+ b[blockIdx.x];
}
 We use threadIdx.x instead of blockIdx.x
 Need to make one change in main()…
© NVIDIA Corporation 2011
Vector Addition Using Threads: main()
#define N 512
int main(void) {
int *a, *b, *c;
int *d_a, *d_b, *d_c;
int size = N * sizeof(int);
// Alloc space for device
cudaMalloc((void **)&d_a,
cudaMalloc((void **)&d_b,
cudaMalloc((void **)&d_c,
// Alloc
a = (int
b = (int
c = (int
© NVIDIA Corporation 2011
// host copies of a, b, c
// device copies of a, b, c
copies of a, b, c
size);
size);
size);
space for host copies of a, b, c and setup input values
*)malloc(size); random_ints(a, N);
*)malloc(size); random_ints(b, N);
*)malloc(size);
Vector Addition Using Threads: main()
// Copy inputs to device
cudaMemcpy(d_a, a, size, cudaMemcpyHostToDevice);
cudaMemcpy(d_b, b, size, cudaMemcpyHostToDevice);
// Launch add() kernel on GPU with N blocks
threads
add<<<N,1>>>(d_a, d_b, d_c);
add<<<1,N>>>(d_a,
// Copy result back to host
cudaMemcpy(c, d_c, size, cudaMemcpyDeviceToHost);
// Cleanup
free(a); free(b); free(c);
cudaFree(d_a); cudaFree(d_b); cudaFree(d_c);
return 0;
}
© NVIDIA Corporation 2011
CONCEPTS
Heterogeneous Computing
Blocks
Threads
Indexing
Shared memory
__syncthreads()
Asynchronous operation
COMBINING THREADS
AND BLOCKS
© NVIDIA Corporation 2011
Handling errors
Managing devices
Combining Blocks and Threads
 We’ve seen parallel vector addition using:


Several blocks with one thread each
One block with several threads
 Let’s adapt vector addition to use both blocks and threads
 Why? We’ll come to that…
 First let’s discuss data indexing…
© NVIDIA Corporation 2011
Indexing Arrays with Blocks and Threads
 No longer as simple as using blockIdx.x and threadIdx.x

Consider indexing an array with one element per thread (8 threads/block)
threadIdx.x
threadIdx.x
threadIdx.x
threadIdx.x
0 1 2 3 4 5 6 7 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
blockIdx.x = 3
 With M threads per block, a unique index for each thread is given by:
int index = threadIdx.x + blockIdx.x * M;
© NVIDIA Corporation 2011
Indexing Arrays: Example
 Which thread will operate on the red element?
0
1
2
3
4
5
6
7
8
9
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
M = 8
0
1
2
3
4
threadIdx.x = 5
5
6
7
0
1
2
3
4
5
6
7
0
1
2
3
4
5
6
blockIdx.x = 2
int index = threadIdx.x + blockIdx.x * M;
=
= 21;
© NVIDIA Corporation 2011
5
+
2
* 8;
7
0
1
2
3
4
5
6
7
Vector Addition with Blocks and Threads
 Use the built-in variable blockDim.x for threads per block
int index = threadIdx.x + blockIdx.x * blockDim.x;
 Combined version of add() to use parallel threads and parallel blocks
__global__ void add(int *a, int *b, int *c) {
int index = threadIdx.x + blockIdx.x * blockDim.x;
c[index] = a[index] + b[index];
}
 What changes need to be made in main()?
© NVIDIA Corporation 2011
Addition with Blocks and Threads: main()
#define N (2048*2048)
#define THREADS_PER_BLOCK 512
int main(void) {
int *a, *b, *c;
int *d_a, *d_b, *d_c;
int size = N * sizeof(int);
// Alloc space for device
cudaMalloc((void **)&d_a,
cudaMalloc((void **)&d_b,
cudaMalloc((void **)&d_c,
// Alloc
a = (int
b = (int
c = (int
© NVIDIA Corporation 2011
// host copies of a, b, c
// device copies of a, b, c
copies of a, b, c
size);
size);
size);
space for host copies of a, b, c and setup input values
*)malloc(size); random_ints(a, N);
*)malloc(size); random_ints(b, N);
*)malloc(size);
Addition with Blocks and Threads: main()
// Copy inputs to device
cudaMemcpy(d_a, a, size, cudaMemcpyHostToDevice);
cudaMemcpy(d_b, b, size, cudaMemcpyHostToDevice);
// Launch add() kernel on GPU
add<<<N/THREADS_PER_BLOCK,THREADS_PER_BLOCK>>>(d_a, d_b, d_c);
// Copy result back to host
cudaMemcpy(c, d_c, size, cudaMemcpyDeviceToHost);
// Cleanup
free(a); free(b); free(c);
cudaFree(d_a); cudaFree(d_b); cudaFree(d_c);
return 0;
}
© NVIDIA Corporation 2011
Handling Arbitrary Vector Sizes
 Typical problems are not friendly multiples of blockDim.x
 Avoid accessing beyond the end of the arrays:
__global__ void add(int *a, int *b, int *c, int n) {
int index = threadIdx.x + blockIdx.x * blockDim.x;
if (index < n)
c[index] = a[index] + b[index];
}
 Update the kernel launch:
add<<<(N + M-1) / M,M>>>(d_a, d_b, d_c, N);
© NVIDIA Corporation 2011
Why Bother with Threads?
 Threads seem unnecessary

They add a level of complexity
 What do we gain?
 Unlike parallel blocks, threads have mechanisms to efficiently:

Communicate
 Synchronize
 To look closer, we need a new example…
© NVIDIA Corporation 2011
CONCEPTS
Heterogeneous Computing
Blocks
Threads
Indexing
Shared memory
__syncthreads()
Asynchronous operation
COOPERATING
THREADS
© NVIDIA Corporation 2011
Handling errors
Managing devices
1D Stencil
 Consider applying a 1D stencil to a 1D array of elements

Each output element is the sum of input elements within a radius
 If radius is 3, then each output element is the sum of 7 input elements:
in
out
© NVIDIA Corporation 2011
Implementing Within a Block
 Each thread processes one output element

blockDim.x elements per block
radius
 Input elements are read several times

With radius 3, each input element is read seven times
7
6
5
4
3
2
1
0
in
out
Thread
Thread
Thread
Thread
Thread
Thread
Thread
Thread
Thread
0
1
2
3
4
5
6
7
8
© NVIDIA Corporation 2011
radius
Sharing Data Between Threads
 Terminology: within a block, threads share data via shared memory
 Extremely fast on-chip memory

By opposition to device memory, referred to as global memory
 Like a user-managed cache
 Declare using __shared__, allocated per block
 Data is not visible to threads in other blocks
© NVIDIA Corporation 2011
Implementing With Shared Memory
 Cache data in shared memory

Read (blockDim.x + 2 * radius) input elements from global memory to
shared memory
 Compute blockDim.x output elements
 Write blockDim.x output elements to global memory
 Each block needs a halo of radius elements at each boundary
in
halo on right
halo on left
out
blockDim.x output elements
© NVIDIA Corporation 2011
Stencil Kernel
__global__ void stencil_1d(int *in, int *out) {
__shared__ int temp[BLOCK_SIZE + 2 * RADIUS];
int gindex = threadIdx.x + blockIdx.x * blockDim.x;
int lindex = threadIdx.x + RADIUS;
// Read input elements into shared memory
temp[lindex] = in[gindex];
if (threadIdx.x < RADIUS) {
temp[lindex - RADIUS] = in[gindex - RADIUS];
temp[lindex + BLOCK_SIZE] = in[gindex + BLOCK_SIZE];
}
© NVIDIA Corporation 2011
Stencil Kernel
// Apply the stencil
int result = 0;
for (int offset = -RADIUS ; offset <= RADIUS ; offset++)
result += temp[lindex + offset];
// Store the result
out[gindex] = result;
}
© NVIDIA Corporation 2011
Data Race!
 The stencil example will not work…
 Suppose thread 15 reads the halo before thread 0 has fetched it…
...
temp[lindex] = in[gindex];
Store at temp[18]
if (threadIdx.x < RADIUS) {
temp[lindex – RADIUS] = in[gindex – RADIUS];
temp[lindex + BLOCK_SIZE] = in[gindex + BLOCK_SIZE];
}
int result = 0;
for (int offset = -RADIUS ; offset <= RADIUS ; offset++)
result += temp[lindex + offset]; Load from temp[19]
...
© NVIDIA Corporation 2011
Skipped since threadId.x > RADIUS
__syncthreads()
 void __syncthreads();
 Synchronizes all threads within a block

Used to prevent RAW / WAR / WAW hazards
 All threads must reach the barrier

© NVIDIA Corporation 2011
In conditional code, the condition must be uniform across the block
Stencil Kernel
__global__ void stencil_1d(int *in, int *out) {
__shared__ int temp[BLOCK_SIZE + 2 * RADIUS];
int gindex = threadIdx.x + blockIdx.x * blockDim.x;
int lindex = threadIdx.x + radius;
// Read input elements into shared memory
temp[lindex] = in[gindex];
if (threadIdx.x < RADIUS) {
temp[lindex – RADIUS] = in[gindex – RADIUS];
temp[lindex + BLOCK_SIZE] = in[gindex + BLOCK_SIZE];
}
// Synchronize (ensure all the data is available)
__syncthreads();
© NVIDIA Corporation 2011
Stencil Kernel
// Apply the stencil
int result = 0;
for (int offset = -RADIUS ; offset <= RADIUS ; offset++)
result += temp[lindex + offset];
// Store the result
out[gindex] = result;
}
© NVIDIA Corporation 2011
Review (1 of 2)
 Launching parallel threads

Launch N blocks with M threads per block with kernel<<<N,M>>>(…);
 Use blockIdx.x to access block index within grid
 Use threadIdx.x to access thread index within block
 Allocate elements to threads:
int index = threadIdx.x + blockIdx.x * blockDim.x;
© NVIDIA Corporation 2011
Review (2 of 2)
 Use __shared__ to declare a variable/array in shared memory

Data is shared between threads in a block
 Not visible to threads in other blocks
 Use __syncthreads() as a barrier

© NVIDIA Corporation 2011
Use to prevent data hazards
CONCEPTS
Heterogeneous Computing
Blocks
Threads
Indexing
Shared memory
__syncthreads()
Asynchronous operation
MANAGING THE DEVICE
© NVIDIA Corporation 2011
Handling errors
Managing devices
Coordinating Host & Device
 Kernel launches are asynchronous

Control returns to the CPU immediately
 CPU needs to synchronize before consuming the results
cudaMemcpy()
Blocks the CPU until the copy is complete
Copy begins when all preceding CUDA calls have completed
cudaMemcpyAsync()
Asynchronous, does not block the CPU
cudaDeviceSynchronize()
Blocks the CPU until all preceding CUDA calls have completed
© NVIDIA Corporation 2011
Reporting Errors
 All CUDA API calls return an error code (cudaError_t)

Error in the API call itself
OR
 Error in an earlier asynchronous operation (e.g. kernel)
 Get the error code for the last error:
cudaError_t cudaGetLastError(void)
 Get a string to describe the error:
char *cudaGetErrorString(cudaError_t)
printf("%s\n", cudaGetErrorString(cudaGetLastError()));
© NVIDIA Corporation 2011
Device Management
 Application can query and select GPUs
cudaGetDeviceCount(int *count)
cudaSetDevice(int device)
cudaGetDevice(int *device)
cudaGetDeviceProperties(cudaDeviceProp *prop, int device)
 Multiple host threads can share a device
 A single host thread can manage multiple devices
cudaSetDevice(i) to select current device
cudaMemcpy(…) for peer-to-peer copies
© NVIDIA Corporation 2011
Introduction to CUDA C/C++
 What have we learned?

Write and launch CUDA C/C++ kernels
-

Manage GPU memory
-

© NVIDIA Corporation 2011
__global__, <<<>>>, blockIdx, threadIdx, blockDim
cudaMalloc(), cudaMemcpy(), cudaFree()
Manage communication and synchronization
-
__shared__, __syncthreads()
-
cudaMemcpy() vs cudaMemcpyAsync(), cudaDeviceSynchronize()
Topics we skipped
 We skipped some details, you can learn more:

CUDA Programming Guide
 CUDA Zone – tools, training, webinars and more
http://developer.nvidia.com/cuda
 Need a quick primer for later:

Compute capability
 Multi-dimensional indexing
 Textures
© NVIDIA Corporation 2011
Compute Capability
 The compute capability of a device describes its architecture, e.g.



Number of registers
Sizes of memories
Features & capabilities
Compute
Capability
Selected Features
(see CUDA C Programming Guide for complete list)
Tesla models
1.0
Fundamental CUDA support
1.3
Double precision, improved memory accesses, atomics
10-series
2.0
Caches, fused multiply-add, 3D grids, surfaces, ECC, P2P,
concurrent kernels/copies, function pointers, recursion
20-series
 The following presentations concentrate on Fermi devices

© NVIDIA Corporation 2011
Compute Capability >= 2.0
870
IDs and Dimensions
 A kernel is launched as a grid of blocks
of threads
- blockIdx and threadIdx are 3D
- We showed only one dimension (x)
Device
Grid 1
 Built-in variables:




© NVIDIA Corporation 2011
threadIdx
blockIdx
blockDim
gridDim
Block
(0,0,0)
Block
(1,0,0)
Block
(2,0,0)
Block
(0,1,0)
Block
(1,1,0)
Block
(2,1,0)
Block (1,1,0)
Thread
(0,0,0)
Thread
(1,0,0)
Thread
(2,0,0)
Thread
(3,0,0)
Thread
(4,0,0)
Thread
(0,1,0)
Thread
(1,1,0)
Thread
(2,1,0)
Thread
(3,1,0)
Thread
(4,1,0)
Thread
(0,2,0)
Thread
(1,2,0)
Thread
(2,2,0)
Thread
(3,2,0)
Thread
(4,2,0)
Textures
 Read-only object

0
1
2
3
4
Dedicated cache
1
 Dedicated filtering hardware
(Linear, bilinear, trilinear)
 Addressable as 1D, 2D or 3D
 Out-of-bounds address handling
(Wrap, clamp)
© NVIDIA Corporation 2011
0
2
(2.5, 0.5)
(1.0, 1.0)
Questions?
© NVIDIA Corporation 2011
Download