51
Introduction to CUDA Programming Scans Andreas Moshovos Winter 2009 Based on slides from: Wen Mei Hwu (UIUC) and David Kirk (NVIDIA) White Paper/Slides by Mark Harris (NVIDIA)

Introduction to CUDA Programming Scans Andreas Moshovos Winter 2009 Based on slides from: Wen Mei Hwu (UIUC) and David Kirk (NVIDIA) White Paper/Slides

Embed Size (px)

Citation preview

Page 1: Introduction to CUDA Programming Scans Andreas Moshovos Winter 2009 Based on slides from: Wen Mei Hwu (UIUC) and David Kirk (NVIDIA) White Paper/Slides

Introduction to CUDA ProgrammingScans

Andreas MoshovosWinter 2009

Based on slides from:Wen Mei Hwu (UIUC) and David Kirk (NVIDIA)

White Paper/Slides by Mark Harris (NVIDIA)

Page 2: Introduction to CUDA Programming Scans Andreas Moshovos Winter 2009 Based on slides from: Wen Mei Hwu (UIUC) and David Kirk (NVIDIA) White Paper/Slides

Scan / Parallel Prefix Sum

• Given an array A = [a0, a1, …, an-1] and a binary associative operator @ with identity I

– scan (A) = [I, a0, (a0 @ a1), …, (a0 @ a1 @ … @ an-2)]

• This is the exclusive scan We’ll focus on this

3 1 7 0 4 1 6 3

0 3 4 11 11 15 16 22

Page 3: Introduction to CUDA Programming Scans Andreas Moshovos Winter 2009 Based on slides from: Wen Mei Hwu (UIUC) and David Kirk (NVIDIA) White Paper/Slides

Inclusive Scan

• Given an array A = [a0, a1, …, an-1] and a binary associative operator @ with identity I

– scan (A) = [a0, (a0 @ a1), …, (a0 @ a1 @ … @ an-1)]

• This is the inclusive scan

3 1 7 0 4 1 6 3

253 4 11 11 15 16 22

Page 4: Introduction to CUDA Programming Scans Andreas Moshovos Winter 2009 Based on slides from: Wen Mei Hwu (UIUC) and David Kirk (NVIDIA) White Paper/Slides

Applications of Scan

• Scan is used as a building block for many parallel algorithms– Radix sort– Quicksort– String comparison– Lexical analysis– Run-length encoding– Histograms– Etc.

• See:– Guy E. Blelloch. “Prefix Sums and Their Applications”. In

John H. Reif (Ed.), Synthesis of Parallel Algorithms, Morgan Kaufmann, 1990. http://www.cs.cmu.edu/afs/cs.cmu.edu/project/scandal/public/papers/CMU-CS-90-190.html

Page 5: Introduction to CUDA Programming Scans Andreas Moshovos Winter 2009 Based on slides from: Wen Mei Hwu (UIUC) and David Kirk (NVIDIA) White Paper/Slides

Scan Background• Pre-GPU

– First proposed in APL by Iverson (1962)– Used as a data parallel primitive in the Connection

Machine (1990)• Feature of C* and CM-Lisp

– Guy Blelloch used scan as a primitive for various parallel algorithms

• Blelloch, 1990, “Prefix Sums and Their Applications”

• GPU Computing– O(n log n) work GPU implementation by Daniel Horn

(GPU Gems 2)• Applied to Summed Area Tables by Hensley et al. (EG05)

– O(n) work GPU scan by Sengupta et al. (EDGE06) and Greß et al. (EG06)

– O(n) work & space GPU implementation by Harris et al. (2007)

• NVIDIA CUDA SDK and GPU Gems 3• Applied to radix sort, stream compaction, and summed area

tables

Page 6: Introduction to CUDA Programming Scans Andreas Moshovos Winter 2009 Based on slides from: Wen Mei Hwu (UIUC) and David Kirk (NVIDIA) White Paper/Slides

Sequential algorithm

void scan( float* output, float* input, int length){

output[0] = 0; // since this is a prescan, not a scanfor(int j = 1; j < length; ++j){

output[j] = input[j-1] + output[j-1];}

}

• N additions• Use a guide:

– Want parallel to be work efficient– Does similar amount of work

3 1 7 0 4 1 6 3

0 3 4

Page 7: Introduction to CUDA Programming Scans Andreas Moshovos Winter 2009 Based on slides from: Wen Mei Hwu (UIUC) and David Kirk (NVIDIA) White Paper/Slides

Naïve Parallel Algorithm

for d := 1 to log2n do

forall k in parallel do

if k >= 2d then x[k] := x[k − 2d-1] + x[k]

3 1 7 0 4 1 6 3

3 1 7 0 4 1 60

3 4 8 7 4 5 70

3 4 11 11 12 12 110

3 4 11 11 15 16 220

d = 1, 2d -1 = 1

d = 2, 2d -1 = 2

d = 3, 2d -1 = 4

Page 8: Introduction to CUDA Programming Scans Andreas Moshovos Winter 2009 Based on slides from: Wen Mei Hwu (UIUC) and David Kirk (NVIDIA) White Paper/Slides

Need Double-Buffering

• First all read

• Then all write

• Solution– Use two arrays:

• Input & Output

– Alternate at each step

3 4 8 7 4 5 70

3 4 11 11 12 12 110

Page 9: Introduction to CUDA Programming Scans Andreas Moshovos Winter 2009 Based on slides from: Wen Mei Hwu (UIUC) and David Kirk (NVIDIA) White Paper/Slides

Double Buffering• Two arrays A & B• Input in global memory• Output in global memory

3 1 7 0 4 1 6 3

3 1 7 0 4 1 60

3 4 8 7 4 5 70

3 4 11 11 12 12 110

3 4 11 11 15 16 220

A

input

B

A

B

3 4 11 11 15 16 220 global

Page 10: Introduction to CUDA Programming Scans Andreas Moshovos Winter 2009 Based on slides from: Wen Mei Hwu (UIUC) and David Kirk (NVIDIA) White Paper/Slides

Naïve Kernel in CUDA__global__ void scan_naive(float *g_odata, float *g_idata,

int n){ extern __shared__ float temp[]; int thid = threadIdx.x, pout = 0, pin = 1;

temp[pout*n + thid] = (thid > 0) ? g_idata[thid-1] : 0;

for (int dd = 1; dd < n; dd *= 2) { pout = 1 - pout; pin = 1 - pout;

int basein = pin * n, baseout = pout * n; syncthreads();

temp[baseout +thid] = temp[basein +thid]; if (thid >= dd)

temp[baseout +thid] += temp[basein +thid - dd]; } syncthreads(); g_odata[thid] = temp[baseout +thid];}

Page 11: Introduction to CUDA Programming Scans Andreas Moshovos Winter 2009 Based on slides from: Wen Mei Hwu (UIUC) and David Kirk (NVIDIA) White Paper/Slides

Analysis of naïve kernel

• This scan algorithm executes log(n) parallel iterations– The steps do n-1, n-2, n-4,... n/2 adds each– Total adds: O(n*log(n))

• This scan algorithm is NOT work efficient– Sequential scan algorithm does n adds

Page 12: Introduction to CUDA Programming Scans Andreas Moshovos Winter 2009 Based on slides from: Wen Mei Hwu (UIUC) and David Kirk (NVIDIA) White Paper/Slides

Improving Efficiency

• A common parallel algorithms pattern:• Balanced Trees

– Build balanced binary tree on input data and sweep to and from the root

– Tree is conceptual, not an actual data structure

• For scan:– Traverse from leaves to root building partial sums at

internal nodes• Root holds sum of all leaves

– Traverse from root to leaves building the scan from the partial sums

• Algorithm originally described by Blelloch (1990)

Page 13: Introduction to CUDA Programming Scans Andreas Moshovos Winter 2009 Based on slides from: Wen Mei Hwu (UIUC) and David Kirk (NVIDIA) White Paper/Slides

Balanced Tree-Based Scan Algorithm / Up-Sweep

Page 14: Introduction to CUDA Programming Scans Andreas Moshovos Winter 2009 Based on slides from: Wen Mei Hwu (UIUC) and David Kirk (NVIDIA) White Paper/Slides

Balanced Tree-Based Scan Algorithm / Up-Sweep

Page 15: Introduction to CUDA Programming Scans Andreas Moshovos Winter 2009 Based on slides from: Wen Mei Hwu (UIUC) and David Kirk (NVIDIA) White Paper/Slides

Balanced Tree-Based Scan Algorithm / Up-Sweep

Page 16: Introduction to CUDA Programming Scans Andreas Moshovos Winter 2009 Based on slides from: Wen Mei Hwu (UIUC) and David Kirk (NVIDIA) White Paper/Slides

Balanced Tree-Based Scan Algorithm / Up-Sweep

Page 17: Introduction to CUDA Programming Scans Andreas Moshovos Winter 2009 Based on slides from: Wen Mei Hwu (UIUC) and David Kirk (NVIDIA) White Paper/Slides

Balanced Tree-Based Scan Algorithm / Up-Sweep

Page 18: Introduction to CUDA Programming Scans Andreas Moshovos Winter 2009 Based on slides from: Wen Mei Hwu (UIUC) and David Kirk (NVIDIA) White Paper/Slides

Balanced Tree-Based Scan Algorithm / Down-Sweep

Page 19: Introduction to CUDA Programming Scans Andreas Moshovos Winter 2009 Based on slides from: Wen Mei Hwu (UIUC) and David Kirk (NVIDIA) White Paper/Slides

Balanced Tree-Based Scan Algorithm / Down-Sweep

Page 20: Introduction to CUDA Programming Scans Andreas Moshovos Winter 2009 Based on slides from: Wen Mei Hwu (UIUC) and David Kirk (NVIDIA) White Paper/Slides

Balanced Tree-Based Scan Algorithm / Down-Sweep

Page 21: Introduction to CUDA Programming Scans Andreas Moshovos Winter 2009 Based on slides from: Wen Mei Hwu (UIUC) and David Kirk (NVIDIA) White Paper/Slides

Up-Sweep Pseudo-Code

Page 22: Introduction to CUDA Programming Scans Andreas Moshovos Winter 2009 Based on slides from: Wen Mei Hwu (UIUC) and David Kirk (NVIDIA) White Paper/Slides

Down-Sweep Pseudo-Code

Page 23: Introduction to CUDA Programming Scans Andreas Moshovos Winter 2009 Based on slides from: Wen Mei Hwu (UIUC) and David Kirk (NVIDIA) White Paper/Slides

Cuda Implementation• Declarations & Copying to shared memory

– Two elements per thread

__global__ void prescan(float *g_odata, float *g_idata, int n)

{extern __shared__ float temp[N];// allocated on invocation

int thid = threadIdx.x;int offset = 1;

temp[2*thid] = g_idata[2*thid]; // load input into shared memorytemp[2*thid+1] = g_idata[2*thid+1];

Page 24: Introduction to CUDA Programming Scans Andreas Moshovos Winter 2009 Based on slides from: Wen Mei Hwu (UIUC) and David Kirk (NVIDIA) White Paper/Slides

Cuda Implementation• Up-Sweepfor (int d = n>>1; d > 0; d >>= 1) // build sum in place up the tree{

__syncthreads();if (thid < d){

int ai = offset*(2*thid+1)-1;int bi = offset*(2*thid+2)-1;temp[bi] += temp[ai];

}offset *= 2;

}

Same computationDifferent assignment of threads

Page 25: Introduction to CUDA Programming Scans Andreas Moshovos Winter 2009 Based on slides from: Wen Mei Hwu (UIUC) and David Kirk (NVIDIA) White Paper/Slides

Up-Sweep: Who does what

t0 t1 t2 t3 t4 t5 t6 t7

d = 8

d = 4

d = 2

d = 1

Page 26: Introduction to CUDA Programming Scans Andreas Moshovos Winter 2009 Based on slides from: Wen Mei Hwu (UIUC) and David Kirk (NVIDIA) White Paper/Slides

Up-Sweep: Who does what• For N=16

– ai 0 bi 1 offset 1 d 8 n 16 thid 0– ai 1 bi 3 offset 2 d 4 n 16 thid 0– ai 3 bi 7 offset 4 d 2 n 16 thid 0– ai 7 bi 15 offset 8 d 1 n 16 thid 0– ai 2 bi 3 offset 1 d 8 n 16 thid 1– ai 5 bi 7 offset 2 d 4 n 16 thid 1– ai 11 bi 15 offset 4 d 2 n 16 thid 1– ai 4 bi 5 offset 1 d 8 n 16 thid 2– ai 9 bi 11 offset 2 d 4 n 16 thid 2– ai 6 bi 7 offset 1 d 8 n 16 thid 3– ai 13 bi 15 offset 2 d 4 n 16 thid 3– ai 8 bi 9 offset 1 d 8 n 16 thid 4– ai 10 bi 11 offset 1 d 8 n 16 thid 5– ai 12 bi 13 offset 1 d 8 n 16 thid 6– ai 14 bi 15 offset 1 d 8 n 16 thid 7

Page 27: Introduction to CUDA Programming Scans Andreas Moshovos Winter 2009 Based on slides from: Wen Mei Hwu (UIUC) and David Kirk (NVIDIA) White Paper/Slides

Down-Sweep// clear the last elementif (thid == 0) { temp[n - 1] = 0; } // traverse down tree & build scanfor (int d = 1; d < n; d *= 2) {offset >>= 1;__syncthreads();if (thid < d){

int ai = offset*(2*thid+1)-1;int bi = offset*(2*thid+2)-1;float t = temp[ai];temp[ai] = temp[bi];temp[bi] += t;

}}__syncthreads()

Page 28: Introduction to CUDA Programming Scans Andreas Moshovos Winter 2009 Based on slides from: Wen Mei Hwu (UIUC) and David Kirk (NVIDIA) White Paper/Slides

Down-Sweep: Who does what• N = 32

– ai 7 bi 15 offset 8 d 1 n 16 thid 0– ai 3 bi 7 offset 4 d 2 n 16 thid 0– ai 1 bi 3 offset 2 d 4 n 16 thid 0– ai 0 bi 1 offset 1 d 8 n 16 thid 0– ai 11 bi 15 offset 4 d 2 n 16 thid 1– ai 5 bi 7 offset 2 d 4 n 16 thid 1– ai 2 bi 3 offset 1 d 8 n 16 thid 1– ai 9 bi 11 offset 2 d 4 n 16 thid 2– ai 4 bi 5 offset 1 d 8 n 16 thid 2– ai 13 bi 15 offset 2 d 4 n 16 thid 3– ai 6 bi 7 offset 1 d 8 n 16 thid 3– ai 8 bi 9 offset 1 d 8 n 16 thid 4– ai 10 bi 11 offset 1 d 8 n 16 thid 5– ai 12 bi 13 offset 1 d 8 n 16 thid 6– ai 14 bi 15 offset 1 d 8 n 16 thid 7

Page 29: Introduction to CUDA Programming Scans Andreas Moshovos Winter 2009 Based on slides from: Wen Mei Hwu (UIUC) and David Kirk (NVIDIA) White Paper/Slides

Copy to output

• All threads do:

__syncthreads();

// write results to global memory

g_odata[2*thid] = temp[2*thid];

g_odata[2*thid+1] = temp[2*thid+1];

}

Page 30: Introduction to CUDA Programming Scans Andreas Moshovos Winter 2009 Based on slides from: Wen Mei Hwu (UIUC) and David Kirk (NVIDIA) White Paper/Slides

Bank Conflicts

• Current scan implementation has many shared memory bank conflicts– These really hurt performance on hardware

• Occur when multiple threads access the same shared memory bank with different addresses

• No penalty if all threads access different banks– Or if all threads access exact same address

• Access costs 2*M cycles if there is a conflict– Where M is max number of threads accessing

single bank

Page 31: Introduction to CUDA Programming Scans Andreas Moshovos Winter 2009 Based on slides from: Wen Mei Hwu (UIUC) and David Kirk (NVIDIA) White Paper/Slides

Loading from Global Memory to Shared

• Each thread loads two shared mem data elements• Original code interleaves loads:

temp[2*thid]   = g_idata[2*thid];

temp[2*thid+1] = g_idata[2*thid+1];

• Threads:(0,1,2,…,8,9,10,…)– banks:(0,2,4,…,0,2,4,…)

• Better to load one element from each half of the array

temp[thid]         = g_idata[thid];

temp[thid + (n/2)] = g_idata[thid + (n/2)];

Page 32: Introduction to CUDA Programming Scans Andreas Moshovos Winter 2009 Based on slides from: Wen Mei Hwu (UIUC) and David Kirk (NVIDIA) White Paper/Slides

Bank Conflicts in the Tree Algorithm / Up-Sweep

• When we build the sums, each thread reads two shared memory locations and writes one:– Threads 0 and 8 access bank 0

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 0 1 2 ...

3 1 7 0 4 1 6 3 5 8 2 0 3 3 1 9 4 5 7 …

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 0 1 2 ...

3 4 7 7 4 5 6 9 5 13 2 2 3 6 1 10 4 9 7 …

Bank:

First iteration: 2 threads access each of 8 banks.

Each corresponds to a single thread.

Like-colored arrows represent simultaneous memory accesses

t0 t1 t2 t3 t4 t5 t6 t7 t8 t9

Page 33: Introduction to CUDA Programming Scans Andreas Moshovos Winter 2009 Based on slides from: Wen Mei Hwu (UIUC) and David Kirk (NVIDIA) White Paper/Slides

Bank Conflicts in the Tree Algorithm / Up-Sweep

• When we build the sums, each thread reads two shared memory locations and writes one:– Threads 1 and 9 access bank 2, and so on

t0 t1 t2 t3 t4 t5 t6 t7 t8 t9

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 0 1 2 ...

3 1 7 0 4 1 6 3 5 8 2 0 3 3 1 9 4 5 7 …

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 0 1 2 ...

3 4 7 7 4 5 6 9 5 13 2 2 3 6 1 10 4 9 7 …

Bank:

First iteration: 2 threads access each of 8 banks.

Each corresponds to a single thread.

Like-colored arrows represent simultaneous memory accesses

Page 34: Introduction to CUDA Programming Scans Andreas Moshovos Winter 2009 Based on slides from: Wen Mei Hwu (UIUC) and David Kirk (NVIDIA) White Paper/Slides

Bank Conflicts in the Tree Algorithm / Down-Sweep

• 2nd iteration: even worse– 4-way bank conflicts; for example:

Threads 0,4,8,12, access bank 1, Threads 1,5,9,13, access Bank 5, etc.

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 0 1 2 ...

3 4 7 4 4 5 6 9 5 13 2 2 3 6 1 10 4 9 7 …

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 0 1 2 ...

3 4 7 11 4 5 6 14 5 13 2 15 3 6 1 16 4 9 7 …

Bank:

2nd iteration: 4 threads access each of 4 banks.

Each corresponds to a single thread.

Like-colored arrows represent simultaneous memory accesses

t0 t1 t2 t3 t4

Page 35: Introduction to CUDA Programming Scans Andreas Moshovos Winter 2009 Based on slides from: Wen Mei Hwu (UIUC) and David Kirk (NVIDIA) White Paper/Slides

Using Padding to Prevent Conflicts

• We can use padding to prevent bank conflicts– Just add a word of padding every 16 words:

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 0 1 2 3 …

3 1 7 0 4 1 6 3 5 8 2 0 3 3 1 9 P 4 5 7 …

0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 0 1 2 3 ...

3 4 7 7 4 5 6 9 5 13 2 2 3 6 1 10 P 4 9 7 …

Bank:

In time:

Page 36: Introduction to CUDA Programming Scans Andreas Moshovos Winter 2009 Based on slides from: Wen Mei Hwu (UIUC) and David Kirk (NVIDIA) White Paper/Slides

Using Padding to Remove Conflicts

• After you compute a shared mem address like this:

Address = 2 * stride * thid;

• Add padding like this:

Address += (address / 16);(address >> 4)

• This removes most bank conflicts– Not all, in the case of deep trees

Page 37: Introduction to CUDA Programming Scans Andreas Moshovos Winter 2009 Based on slides from: Wen Mei Hwu (UIUC) and David Kirk (NVIDIA) White Paper/Slides

Scan Bank Conflicts (1)• A full binary tree with 64 leaf nodes:

• Multiple 2-and 4-way bank conflicts• Shared memory cost for whole tree

– 1 32-thread warp = 6 cycles per thread w/o conflicts• Counting 2 shared mem reads and one write (s[a] += s[b])

– 6 * (2+4+4+4+2+1) = 102 cycles– 36 cycles if there were no bank conflicts (6 * 6)

Scale (s) Thread addresses1 0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 42 44 46 48 50 52 54 56 58 60 622 0 4 8 12 16 20 24 28 32 36 40 44 48 52 56 604 0 8 16 24 32 40 48 568 0 16 32 4816 0 3232 0

Conflicts Banks2-way 0 2 4 6 8 10 12 14 0 2 4 6 8 10 12 14 0 2 4 6 8 10 12 14 0 2 4 6 8 10 12 144-way 0 4 8 12 0 4 8 12 0 4 8 12 0 4 8 124-way 0 8 0 8 0 8 0 84-way 0 0 0 02-way 0 0None 0

Page 38: Introduction to CUDA Programming Scans Andreas Moshovos Winter 2009 Based on slides from: Wen Mei Hwu (UIUC) and David Kirk (NVIDIA) White Paper/Slides

Scan Bank Conflicts (2)

• It’s much worse with bigger trees• A full binary tree with 128 leaf nodes

– Only the last 6 iterations shown (root and 5 levels below)

• Cost for whole tree:– 12*2 + 6*(4+8+8+4+2+1) = 186 cycles– 48 cycles if there were no bank conflicts: 12*1 + (6*6)

Scale (s) Thread addresses2 0 4 8 12 16 20 24 28 32 36 40 44 48 52 56 60 64 68 72 76 80 84 88 92 96 100 104 108 112 116 120 1224 0 8 16 24 32 40 48 56 64 72 80 88 96 104 112 1208 0 16 32 48 64 80 96 11216 0 32 64 9632 0 6464 0

Conflicts Banks4-way 0 4 8 12 0 4 8 12 0 4 8 12 0 4 8 12 0 4 8 12 0 4 8 12 0 4 8 12 0 4 8 108-way 0 8 0 8 0 8 0 8 0 8 0 8 0 8 0 88-way 0 0 0 0 0 0 0 04-way 0 0 0 02-way 0 0None 0

Page 39: Introduction to CUDA Programming Scans Andreas Moshovos Winter 2009 Based on slides from: Wen Mei Hwu (UIUC) and David Kirk (NVIDIA) White Paper/Slides

Scan Bank Conflicts (3)

• A full binary tree with 512 leaf nodes– Only the last 6 iterations shown (root and 5 levels below)

• Cost for whole tree:– 48*2+24*4+12*8+6* (16+16+8+4+2+1) = 570 cycles– 120 cycles if there were no bank conflicts!

Scale (s) Thread addresses8 0 16 32 48 64 80 96 112 128 144 160 176 192 208 224 240 256 272 288 304 320 336 352 368 384 400 416 432 448 464 480 49616 0 32 64 96 128 160 192 224 256 288 320 352 384 416 448 48032 0 64 128 192 256 320 384 44864 0 128 256 384

128 0 256256 0

Conflicts Banks16-way 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 016-way 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 08-way 0 0 0 0 0 0 0 04-way 0 0 0 02-way 0 0None 0

Page 40: Introduction to CUDA Programming Scans Andreas Moshovos Winter 2009 Based on slides from: Wen Mei Hwu (UIUC) and David Kirk (NVIDIA) White Paper/Slides

Fixing Scan Bank Conflicts• Insert padding every NUM_BANKS elements

const int LOG_NUM_BANKS = 4; // 16 banks int tid = threadIdx.x;int s = 1;

// Traversal from leaves up to rootfor (d = n>>1; d > 0; d >>= 1){if (thid <= d){

int a = s*(2*tid); int b = s*(2*tid+1) a += (a >> LOG_NUM_BANKS); // insert pad word b += (b >> LOG_NUM_BANKS); // insert pad word shared[a] += shared[b];

}}

Page 41: Introduction to CUDA Programming Scans Andreas Moshovos Winter 2009 Based on slides from: Wen Mei Hwu (UIUC) and David Kirk (NVIDIA) White Paper/Slides

Fixing Scan Bank Conflicts• A full binary tree with 64 leaf nodes

• No more bank conflicts– However, there are ~8 cycles overhead for addressing

• For each s[a] += s[b] (8 cycles/iter. * 6 iter. = 48 extra cycles)

– So just barely worth the overhead on a small tree• 84 cycles vs. 102 with conflicts vs. 36 optimal

Leaf Nodes Scale (s) Thread addresses64 1 0 2 4 6 8 10 12 14 17 19 21 23 25 27 29 31 34 36 38 40 42 44 46 48 51 53 55 57 59 61 63

2 0 4 8 12 17 21 25 29 34 38 42 46 51 55 59 634 0 8 17 25 34 42 51 598 0 17 34 5116 0 34 = Padding inserted32 0

Conflicts BanksNone 0 2 4 6 8 10 12 14 1 3 5 7 9 11 13 15 2 4 6 8 10 12 14 0 3 5 7 9 11 13 15None 0 4 8 12 1 5 9 13 2 6 10 14 3 7 11 15None 0 8 1 9 2 10 3 11None 0 1 2 3None 0 2None 0

Page 42: Introduction to CUDA Programming Scans Andreas Moshovos Winter 2009 Based on slides from: Wen Mei Hwu (UIUC) and David Kirk (NVIDIA) White Paper/Slides

• A full binary tree with 128 leaf nodes– Only the last 6 iterations shown (root and 5 levels below)

• No more bank conflicts!– Significant performance win:

• 106 cycles vs. 186 with bank conflicts vs. 48 optimal

Scale (s) Thread addresses2 0 4 8 12 17 21 25 29 34 38 42 46 51 55 59 63 68 72 76 80 85 89 93 97 102 106 110 114 119 123 127 1314 0 8 17 25 34 42 51 59 68 76 85 93 102 110 119 1278 0 17 34 51 68 85 102 11916 0 34 68 10232 0 68 = Padding inserted64 0

Conflicts BanksNone 0 4 8 12 1 5 9 13 2 6 10 14 3 7 11 15 4 8 12 0 5 9 13 1 6 10 14 2 7 11 15 3None 0 8 1 9 2 10 3 11 4 12 5 13 6 14 7 15None 0 1 2 3 4 5 6 7None 0 2 4 6None 0 4None 0

Fixing Scan Bank Conflicts

Page 43: Introduction to CUDA Programming Scans Andreas Moshovos Winter 2009 Based on slides from: Wen Mei Hwu (UIUC) and David Kirk (NVIDIA) White Paper/Slides

Fixing Scan Bank Conflicts• A full binary tree with 512 leaf nodes

– Only the last 6 iterations shown (root and 5 levels below)

• Wait, we still have bank conflicts– Method is not foolproof, but still much improved– 304 cycles vs. 570 with bank conflicts vs. 120 optimal

Scale (s) Thread addresses8 0 17 34 51 68 85 102 119 136 153 170 187 204 221 238 255 272 289 306 323 340 357 374 391 408 425 442 459 476 493 510 52716 0 34 68 102 136 170 204 238 272 306 340 374 408 442 476 51032 0 68 136 204 272 340 408 47664 0 136 272 408

128 0 272 = Padding inserted256 0

Conflicts BanksNone 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 152-way 0 2 4 6 8 10 12 14 0 2 4 6 8 10 12 142-way 0 4 8 12 0 4 8 122-way 0 8 0 82-way 0 0None 0

Page 44: Introduction to CUDA Programming Scans Andreas Moshovos Winter 2009 Based on slides from: Wen Mei Hwu (UIUC) and David Kirk (NVIDIA) White Paper/Slides

Fixing Scan Bank Conflicts• It’s possible to remove all bank conflicts

– Just do multi-level padding– Example: two-level padding:

const int LOG_NUM_BANKS = 4; // 16 banks on G80int tid = threadIdx.x;int s = 1;// Traversal from leaves up to rootfor (d = n>>1; d > 0; d >>= 1){

if (thid <= d){

int a = s*(2*tid); int b = s*(2*tid+1) int offset = (a >> LOG_NUM_BANKS); // first level a += offset + (offset >>LOG_NUM_BANKS); // second level offset = (b >> LOG_NUM_BANKS); // first level b += offset + (offset >>LOG_NUM_BANKS); // second level temp[a] += temp[b]; }}

Page 45: Introduction to CUDA Programming Scans Andreas Moshovos Winter 2009 Based on slides from: Wen Mei Hwu (UIUC) and David Kirk (NVIDIA) White Paper/Slides

• A full binary tree with 512 leaf nodes– Only the last 6 iterations shown (root and 5 levels below)

– No bank conflicts• But an extra cycle overhead per address calculation• Not worth it: 440 cycles vs. 304 with 1-level padding

– With 1-level padding, bank conflicts only occur in warp 0• Very small remaining cost due to bank conflicts• Removing them hurts all other warps

Scale (s) Thread addresses8 0 17 34 51 68 85 102 119 136 153 170 187 204 221 238 255 273 290 307 324 341 358 375 392 409 426 443 460 477 494 511 52816 0 34 68 102 136 170 204 238 273 307 341 375 409 443 477 51132 0 68 136 204 273 341 409 47764 0 136 273 409

128 0 273 = 1-level padding inserted256 0 = 2-level padding inserted

Conflicts BanksNone 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 0None 0 2 4 6 8 10 12 14 1 3 5 7 9 11 13 15None 0 4 8 12 1 5 9 13None 0 8 1 9None 0 1None 0

Fixing Scan Bank Conflicts

Page 46: Introduction to CUDA Programming Scans Andreas Moshovos Winter 2009 Based on slides from: Wen Mei Hwu (UIUC) and David Kirk (NVIDIA) White Paper/Slides

Large Arrays

• So far:– Array can be processed by a block

• 1024 elements

• Larger arrays?– Divide into blocks– Scan each with a block of threads– Produce partial scans– Scan the partial scans– Add the corresponding scan result back to all

elements of each block

• See Scan Large Array in SDK

Page 47: Introduction to CUDA Programming Scans Andreas Moshovos Winter 2009 Based on slides from: Wen Mei Hwu (UIUC) and David Kirk (NVIDIA) White Paper/Slides

Large Arrays

Page 48: Introduction to CUDA Programming Scans Andreas Moshovos Winter 2009 Based on slides from: Wen Mei Hwu (UIUC) and David Kirk (NVIDIA) White Paper/Slides

Application: Stream Compaction

Page 49: Introduction to CUDA Programming Scans Andreas Moshovos Winter 2009 Based on slides from: Wen Mei Hwu (UIUC) and David Kirk (NVIDIA) White Paper/Slides

Application: Radix Sort

Page 50: Introduction to CUDA Programming Scans Andreas Moshovos Winter 2009 Based on slides from: Wen Mei Hwu (UIUC) and David Kirk (NVIDIA) White Paper/Slides

Using Streams to Overlap Kernels with Data Transfers

• Stream:– Queue of ordered CUDA requests

• By default all CUDA request go to the same stream

• Create a stream:– cudaStreamCreate (cudaStream *stream)

Page 51: Introduction to CUDA Programming Scans Andreas Moshovos Winter 2009 Based on slides from: Wen Mei Hwu (UIUC) and David Kirk (NVIDIA) White Paper/Slides

Overlapping Kernels

cudaMemcpyAsync (dA, hA, sizeB, cudaMemcpyHostToDevice, streamA);

cudaMemcpyAsync (dB, hB, sizeB, cudaMemcpyHostToDevice, streamB);

Kernel<<<100, 512, 0, streamA>>> (dAo, dA, sizeA);Kernel<<<100, 512, 0, streamB>>> (dBo, dB, sizeB);

cudaMemcpyAsync(hBo, dAo, cudaMemcpyDeviceToHost, streamA);

cudaMemcpyAsync(hBo, dAo, cudaMemcpyDeviceToHost, streamB);

cudaThreadSynchronize();