CUDA Memory Architecture: GPGPU Class Week 4
CUDA Memory Architecture: GPGPU Class Week 4
CUDA Memory Architecture: GPGPU Class Week 4
GPGPU class
Week 4
CPU – GPU HW Differences
● CPU
● Most die area used for memory cache
● Relatively few transistors for ALUs
● GPU
● Most die area used for ALUs
● Relatively small caches
CPU – GPU HW Differences
● Situation is slowly changing
● Many-core CPUs
● More caches on GPU die
CPU – GPU Differences
● What does that mean for SW?
● CPU
● Hides memory latency via hierarchy of caches
– L1, L2 and L3 caches
● Little need for thread programming
– This is currently changing
● GPU
● Memory latency not hidden by large cache
– Only texture cache (roughly specialized L1 cache)
– Needs many (active) threads to hide latency!
● Only many-threads applications are useful
– Extra bonus of CUDA: threads can easily communicate (with limits)
A View on the G80 Architecture
● “Graphics mode:”
Host
SP SP SP SP SP SP SP SP SP SP SP SP SP SP SP SP
Thread Processor
TF TF TF TF TF TF TF TF
L1 L1 L1 L1 L1 L1 L1 L1
L2 L2 L2 L2 L2 L2
FB FB FB FB FB FB
A View on the G80 Architecture
● “CUDA mode:”
Host
Input Assembler
Parallel Data Parallel Data Parallel Data Parallel Data Parallel Data Parallel Data Parallel Data Parallel Data
Cache Cache Cache Cache Cache Cache Cache Cache
Texture
Texture Texture Texture Texture Texture Texture Texture Texture
Global Memory
CUDA Memory Types
Each thread can:
● Read/write per-thread registers Grid
Constant Memory
CUDA Memory Types & Uses
● Compute Capability 1.x
● Global memory (read and write)
– Slow & uncached
– Requires sequential & aligned 16 byte reads and writes to be fast (coalesced read/write)
● Texture memory (read only)
– Cache optimized for 2D spatial access pattern
● Constant memory
– This is where constants and kernel arguments are stored
– Slow, but with cache (8 kb)
● Shared memory (16 kb per MP)
– Fast, but take care of bank conflicts
– Exchange data between threads in a block
● Local memory (used for whatever does not fit into registers)
– Slow & uncached, but automatic coalesced reads and writes
● Registers (8192-16384 32-bit registers per MP)
– Fastest, scope is thread local
CUDA Memory Types & Uses
● Compute Capability 2.x
● Global memory (read and write)
– Slow, but now with cache
● Texture memory (read only)
– Cache optimized for 2D spatial access pattern
● Constant memory
– Slow, but with cache (8 kb)
– Special “LoaD Uniform” (LDU) instruction
● Shared memory (48kb per MP)
– Fast, but slightly different rules for bank conflicts now
● Local memory
– Slow, but now with cache
● Registers (32768 32-bit registers per MP)
CUDA Memory Limitations
● Global memory
● Best if 64 or 128 bytes (16 or 32 words) are read
– Parallel read/writes from threads in a block
– Sequential memory locations
– With appropriate alignment
– Called “coalesced” read/write
● Otherwise: a sequence of reads/writes
– >10x slower!
● Shared memory
● Fastest if
– All threads read from the same shared memory location
– All threads index a shared array via permutation
● E.g. linear reads/writes
● Otherwise: bank conflicts
– Not as bad as uncoalesced global memory reads/writes
CUDA Type Qualifiers
● Type Qualifier table
● Notes:
● _device__ not required for __local__, __shared__, or __constant__
● Automatic variables without any qualifier reside in a register
– Except arrays that reside in local memory
– Or not enough registers available for automatic variables
CUDA Type Qualifiers
● Type Qualifier table / performance
Variable declaration Memory Performance
penalty
int LocalVar; register 1x
int LocalArray[10]; local 100x
[__device__] __shared__ int SharedVar; shared 1x
__device__ int GlobalVar; global 100x
[__device__] __constant__ int ConstantVar; constant 1x
Declared outside of
Declared in the kernel
any Function
Pointers & CUDA
● Pointers can only point to global memory
● Typical usage: as array argument to kernels
– __global__ void kernel(float * d_ptr);
● Alternative: explicit pointer assignment
– float * ptr = &globalVar;
● Use pointers only to access global memory
– Simple, regular read/write patterns
– No pointer chains (linked lists)
– No C wizard pointer magic
● But index magic is fine
A Common Programming Scenario 1
● Task:
● Load data from global memory
● Do thread-local computations
● Store result to global memory
● Solution (statements in kernel)
● Load data to registers (coalesced)
– float a = d_ptr[blockIdx.x*blockDim.x + threadIdx.x];
● Do computation with registers
– float res = f(a);
● Store back result (coalesced)
– d_ptr[blockIdx.x*blockDim.x + threadIdx.x] = res;
A Common Programming Scenario 1
● Full kernel code
__global__ void kernel(float * d_ptr)
{
// Coalesced read if blockDim.x is a multiple of 16
float a = d_ptr[blockIdx.x*blockDim.x + threadIdx.x];
atomicAdd(&partial_sum, src[pos]);