sinatras/pmpp-eval huggingface dataset:
Question: If we want to use each thread in a grid to calculate one output element of a vector addition, what is the expression for mapping the thread/block indices to the data index i?
A. i = threadIdx.x + threadIdx.y;
B. i = blockIdx.x + threadIdx.x;
C. i = blockIdx.x * blockDim.x + threadIdx.x;
D. i = blockIdx.x * threadIdx.x;
Answer: C
Explanation: You need both the block offset (blockIdx.x * blockDim.x) and the thread offset within the block (threadIdx.x).
Question: Each thread calculates two adjacent elements of a vector addition. What is the expression for the data index i of the first element processed by a thread?
A. i = blockIdx.x * blockDim.x + threadIdx.x * 2;
B. i = blockIdx.x * threadIdx.x * 2;
C. i = (blockIdx.x * blockDim.x + threadIdx.x) * 2;
D. i = blockIdx.x * blockDim.x * 2 + threadIdx.x;
Answer: C
Explanation: This doubles the logical thread index so each thread starts at an even index (0,2,4,...) while remaining contiguous across blocks.
Question: Each thread calculates two elements. A block processes 2*blockDim.x consecutive elements in two sections: first section (each thread does one element), then second section (each thread does one element). What is the expression for the first element index i for a thread?
A. i = blockIdx.x * blockDim.x + threadIdx.x + 2;
B. i = blockIdx.x * threadIdx.x * 2;
C. i = (blockIdx.x * blockDim.x + threadIdx.x) * 2;
D. i = blockIdx.x * blockDim.x * 2 + threadIdx.x;
Answer: D
Explanation: The first section starts at the block's base offset of 2*blockDim.x. Each thread handles i and then i + blockDim.x in the second section.
Question: Vector addition with length 8000, 1 output element per thread, block size 1024. Using the minimum number of blocks to cover all elements, how many threads are in the grid?
A. 8000
B. 8196
C. 8192
D. 8200
Answer: C
Explanation: ceil(8000/1024) = 8 blocks, each with 1024 threads -> 8*1024 = 8192 threads.
Question: Allocate an array of v integers in device global memory with cudaMalloc. What is the correct expression for the second argument (size in bytes)?
A. n
B. v
C. n * sizeof(int)
D. v * sizeof(int)
Answer: D
Explanation: cudaMalloc takes the size in bytes; for v integers that is v * sizeof(int).
Question: Allocate an array of n floats and have pointer A_d point to it. What is the appropriate first argument to cudaMalloc?
A. n
B. (void*) A_d
C. *A_d
D. (void**) &A_d
Answer: D
Explanation: cudaMalloc's first parameter is a void** to receive the device pointer (i.e., the address of the pointer).
Question: Copy 3000 bytes from host array A_h to device array A_d. Which API call is correct?
A. cudaMemcpy(3000, A_h, A_d, cudaMemcpyHostToDevice);
B. cudaMemcpy(A_h, A_d, 3000, cudaMemcpyDeviceToHost);
C. cudaMemcpy(A_d, A_h, 3000, cudaMemcpyHostToDevice);
D. cudaMemcpy(3000, A_d, A_h, cudaMemcpyHostToDevice);
Answer: C
Explanation: Syntax is cudaMemcpy(dst, src, sizeBytes, kind). Here we copy from host to device.
Question: How to declare variable err to receive return values of CUDA API calls?
A. int err;
B. cudaError err;
C. cudaError_t err;
D. cudaSuccess_t err;
Answer: C
Explanation: CUDA API error return type is cudaError_t.
Question: Given the CUDA code:

```c
__global__ void foo_kernel(float* a, float* b, unsigned int N) {
    unsigned int i = blockIdx.x * blockDim.x + threadIdx.x;
    if (i < N) {
        b[i] = 2.7f * a[i] - 4.3f;
    }
}

void foo(float* a_d, float* b_d) {
    unsigned int N = 200000;
    foo_kernel<<<(N + 128 - 1) / 128, 128>>>(a_d, b_d, N);
}
```

(a) What is the number of threads **per block**?
Answer: 128
Explanation: Given by the kernel launch <<<..., 128>>>.
Question: Given the CUDA code:

```c
__global__ void foo_kernel(float* a, float* b, unsigned int N) {
    unsigned int i = blockIdx.x * blockDim.x + threadIdx.x;
    if (i < N) {
        b[i] = 2.7f * a[i] - 4.3f;
    }
}

void foo(float* a_d, float* b_d) {
    unsigned int N = 200000;
    foo_kernel<<<(N + 128 - 1) / 128, 128>>>(a_d, b_d, N);
}
```

(b) What is the **number of threads in the grid**?
Answer: 200064
Explanation: Blocks = ceil(200000/128) = (200000 + 127) // 128 = 1563; threads = 1563 * 128 = 200064.
Question: Given the CUDA code:

```c
__global__ void foo_kernel(float* a, float* b, unsigned int N) {
    unsigned int i = blockIdx.x * blockDim.x + threadIdx.x;
    if (i < N) {
        b[i] = 2.7f * a[i] - 4.3f;
    }
}

void foo(float* a_d, float* b_d) {
    unsigned int N = 200000;
    foo_kernel<<<(N + 128 - 1) / 128, 128>>>(a_d, b_d, N);
}
```

(c) What is the **number of blocks in the grid**?
Answer: 1563
Explanation: Computed as (N + 128 - 1) / 128 with N = 200000.
Question: Given the CUDA code:

```c
__global__ void foo_kernel(float* a, float* b, unsigned int N) {
    unsigned int i = blockIdx.x * blockDim.x + threadIdx.x;
    if (i < N) {
        b[i] = 2.7f * a[i] - 4.3f;
    }
}

void foo(float* a_d, float* b_d) {
    unsigned int N = 200000;
    foo_kernel<<<(N + 128 - 1) / 128, 128>>>(a_d, b_d, N);
}
```

(d) How many threads **execute the index computation line** `unsigned int i = blockIdx.x * blockDim.x + threadIdx.x;`?
Answer: 200064
Explanation: All launched threads execute the index computation line.
Question: Given the CUDA code:

```c
__global__ void foo_kernel(float* a, float* b, unsigned int N) {
    unsigned int i = blockIdx.x * blockDim.x + threadIdx.x;
    if (i < N) {
        b[i] = 2.7f * a[i] - 4.3f;
    }
}

void foo(float* a_d, float* b_d) {
    unsigned int N = 200000;
    foo_kernel<<<(N + 128 - 1) / 128, 128>>>(a_d, b_d, N);
}
```

(e) How many threads **execute the assignment inside the `if (i < N)`** - i.e., `b[i] = 2.7f * a[i] - 4.3f;`?
Answer: 200000
Explanation: Only threads with i < N execute the body; extra 64 threads fail the predicate.
Question: Given the following CUDA code:

```c
__global__ void foo_kernel(float* a, float* b, unsigned int M, unsigned int N) {
    unsigned int row = blockIdx.y * blockDim.y + threadIdx.y;
    unsigned int col = blockIdx.x * blockDim.x + threadIdx.x;
    if (row < M && col < N) {
        b[row*N + col] = a[row*N + col]/2.1f + 4.8f;
    }
}

void foo(float* a_d, float* b_d) {
    unsigned int M = 150;
    unsigned int N = 300;
    dim3 bd(16, 32);
    dim3 gd((N - 1) / 16 + 1, (M - 1) / 32 + 1);
    foo_kernel<<<gd, bd>>>(a_d, b_d, M, N);
}
```

(a) What is the number of threads per block?
Answer: 512
Explanation: bd = (16,32) ⇒ threadsPerBlock = 16x32 = 512.
Question: Given the following CUDA code:

```c
__global__ void foo_kernel(float* a, float* b, unsigned int M, unsigned int N) {
    unsigned int row = blockIdx.y * blockDim.y + threadIdx.y;
    unsigned int col = blockIdx.x * blockDim.x + threadIdx.x;
    if (row < M && col < N) {
        b[row*N + col] = a[row*N + col]/2.1f + 4.8f;
    }
}

void foo(float* a_d, float* b_d) {
    unsigned int M = 150;
    unsigned int N = 300;
    dim3 bd(16, 32);
    dim3 gd((N - 1) / 16 + 1, (M - 1) / 32 + 1);
    foo_kernel<<<gd, bd>>>(a_d, b_d, M, N);
}
```

(b) What is the number of threads in the grid?
Answer: 48640
Explanation: gd = (19,5) ⇒ blocks = 19x5 = 95. Threads = 95x512 = 48,640.
Question: Given the following CUDA code:

```c
__global__ void foo_kernel(float* a, float* b, unsigned int M, unsigned int N) {
    unsigned int row = blockIdx.y * blockDim.y + threadIdx.y;
    unsigned int col = blockIdx.x * blockDim.x + threadIdx.x;
    if (row < M && col < N) {
        b[row*N + col] = a[row*N + col]/2.1f + 4.8f;
    }
}

void foo(float* a_d, float* b_d) {
    unsigned int M = 150;
    unsigned int N = 300;
    dim3 bd(16, 32);
    dim3 gd((N - 1) / 16 + 1, (M - 1) / 32 + 1);
    foo_kernel<<<gd, bd>>>(a_d, b_d, M, N);
}
```

(c) What is the number of blocks in the grid?
Answer: 95
Explanation: Blocks = gd.x x gd.y = 19 x 5 = 95.
Question: Given the following CUDA code:

```c
__global__ void foo_kernel(float* a, float* b, unsigned int M, unsigned int N) {
    unsigned int row = blockIdx.y * blockDim.y + threadIdx.y;
    unsigned int col = blockIdx.x * blockDim.x + threadIdx.x;
    if (row < M && col < N) {
        b[row*N + col] = a[row*N + col]/2.1f + 4.8f;
    }
}

void foo(float* a_d, float* b_d) {
    unsigned int M = 150;
    unsigned int N = 300;
    dim3 bd(16, 32);
    dim3 gd((N - 1) / 16 + 1, (M - 1) / 32 + 1);
    foo_kernel<<<gd, bd>>>(a_d, b_d, M, N);
}
```

(d) How many threads execute the assignment `b[row*N + col] = a[row*N + col]/2.1f + 4.8f;`?
Answer: 45000
Explanation: Only threads with (row < M && col < N) execute it. Count = MxN = 150x300 = 45,000.
Question: A 2D matrix has width=400 and height=500 and is stored as a 1D array in row-major order. What is the linear index of the element at row=20, col=10?
Answer: 8010
Explanation: Row-major index = row*width + col = 20*400 + 10 = 8,010.
Question: A 2D matrix has width=400 and height=500 and is stored as a 1D array in column-major order. What is the linear index of the element at row=20, col=10?
Answer: 5020
Explanation: Column-major index = col*height + row = 10*500 + 20 = 5,020.
Question: A 3D tensor has width=400 (x), height=500 (y), and depth=300 (z). It is stored as a 1D array in row-major order with index mapping idx = z*height*width + y*width + x. What is the linear index of the element at x=10, y=20, z=5?
Answer: 1008010
Explanation: idx = 5*500*400 + 20*400 + 10 = 1,000,000 + 8,000 + 10 = 1,008,010.
Question: Consider the following CUDA kernel and host code:

```c
__global__ void foo_kernel(int* a, int* b) {
    unsigned int i = blockIdx.x*blockDim.x + threadIdx.x;
    if (threadIdx.x < 40 || threadIdx.x >= 104) {
        b[i] = a[i] + 1;
    }
    if (i % 2 == 0) {
        a[i] = b[i] * 2;
    }
    for (unsigned int j = 0; j < 5 - (i % 3); ++j) {
        b[i] += j;
    }
}

void foo(int* a_d, int* b_d) {
    unsigned int N = 1024;
    foo_kernel<<<(N + 128 - 1)/128, 128>>>(a_d, b_d);
}
```

(a) How many warps are there per block?
Answer: 4
Explanation: Each block has 128 threads and a warp has 32 threads -> 128/32 = 4 warps per block.
Question: Consider the following CUDA kernel and host code:

```c
__global__ void foo_kernel(int* a, int* b) {
    unsigned int i = blockIdx.x*blockDim.x + threadIdx.x;
    if (threadIdx.x < 40 || threadIdx.x >= 104) {
        b[i] = a[i] + 1;
    }
    if (i % 2 == 0) {
        a[i] = b[i] * 2;
    }
    for (unsigned int j = 0; j < 5 - (i % 3); ++j) {
        b[i] += j;
    }
}

void foo(int* a_d, int* b_d) {
    unsigned int N = 1024;
    foo_kernel<<<(N + 128 - 1)/128, 128>>>(a_d, b_d);
}
```

(b) How many warps are there in the entire grid?
Answer: 32
Explanation: Blocks = (1024 + 128 - 1)/128 = 8. Warps per block = 4. Total warps = 8 x 4 = 32.
Question: Consider the following CUDA kernel and host code:

```c
__global__ void foo_kernel(int* a, int* b) {
    unsigned int i = blockIdx.x*blockDim.x + threadIdx.x;
    if (threadIdx.x < 40 || threadIdx.x >= 104) {
        b[i] = a[i] + 1;
    }
    if (i % 2 == 0) {
        a[i] = b[i] * 2;
    }
    for (unsigned int j = 0; j < 5 - (i % 3); ++j) {
        b[i] += j;
    }
}

void foo(int* a_d, int* b_d) {
    unsigned int N = 1024;
    foo_kernel<<<(N + 128 - 1)/128, 128>>>(a_d, b_d);
}
```

For the statement `if (threadIdx.x < 40 || threadIdx.x >= 104) { ... }`:
(i) How many warps in the grid are active on this statement?
Answer: 24
Explanation: Per block: warp 0 (0-31) active; warp 1 (32-63) partially active -> warp active; warp 2 (64-95) inactive; warp 3 (96-127) partially active -> warp active. So 3 active warps/block x 8 blocks = 24.
Question: Consider the following CUDA kernel and host code:

```c
__global__ void foo_kernel(int* a, int* b) {
    unsigned int i = blockIdx.x*blockDim.x + threadIdx.x;
    if (threadIdx.x < 40 || threadIdx.x >= 104) {
        b[i] = a[i] + 1;
    }
    if (i % 2 == 0) {
        a[i] = b[i] * 2;
    }
    for (unsigned int j = 0; j < 5 - (i % 3); ++j) {
        b[i] += j;
    }
}

void foo(int* a_d, int* b_d) {
    unsigned int N = 1024;
    foo_kernel<<<(N + 128 - 1)/128, 128>>>(a_d, b_d);
}
```

For the statement `if (threadIdx.x < 40 || threadIdx.x >= 104) { ... }`:
(ii) How many warps in the grid are divergent on this statement?
Answer: 16
Explanation: Per block, warp 1 (32-63) and warp 3 (96-127) have mixed predicates (some threads true, some false) -> 2 divergent warps/block x 8 blocks = 16.
Question: Consider the following CUDA kernel and host code:

```c
__global__ void foo_kernel(int* a, int* b) {
    unsigned int i = blockIdx.x*blockDim.x + threadIdx.x;
    if (threadIdx.x < 40 || threadIdx.x >= 104) {
        b[i] = a[i] + 1;
    }
    if (i % 2 == 0) {
        a[i] = b[i] * 2;
    }
    for (unsigned int j = 0; j < 5 - (i % 3); ++j) {
        b[i] += j;
    }
}

void foo(int* a_d, int* b_d) {
    unsigned int N = 1024;
    foo_kernel<<<(N + 128 - 1)/128, 128>>>(a_d, b_d);
}
```

For the same statement, what is the SIMD efficiency of warp 0 of block 0? Give a decimal in [0,1] with two decimals; do not include %.
Answer: 1.00
Explanation: Warp 0 covers threads 0-31; all satisfy `threadIdx.x < 40`. Active lanes = 32/32 = 100%.
Question: Consider the following CUDA kernel and host code:

```c
__global__ void foo_kernel(int* a, int* b) {
    unsigned int i = blockIdx.x*blockDim.x + threadIdx.x;
    if (threadIdx.x < 40 || threadIdx.x >= 104) {
        b[i] = a[i] + 1;
    }
    if (i % 2 == 0) {
        a[i] = b[i] * 2;
    }
    for (unsigned int j = 0; j < 5 - (i % 3); ++j) {
        b[i] += j;
    }
}

void foo(int* a_d, int* b_d) {
    unsigned int N = 1024;
    foo_kernel<<<(N + 128 - 1)/128, 128>>>(a_d, b_d);
}
```

For the same statement, what is the SIMD efficiency of warp 1 of block 0? Give a decimal in [0,1] with two decimals; do not include %.
Answer: 0.25
Explanation: Warp 1 covers 32-63; only 32-39 (8 lanes) satisfy the predicate. Efficiency = 8/32 = 25%.
Question: Consider the following CUDA kernel and host code:

```c
__global__ void foo_kernel(int* a, int* b) {
    unsigned int i = blockIdx.x*blockDim.x + threadIdx.x;
    if (threadIdx.x < 40 || threadIdx.x >= 104) {
        b[i] = a[i] + 1;
    }
    if (i % 2 == 0) {
        a[i] = b[i] * 2;
    }
    for (unsigned int j = 0; j < 5 - (i % 3); ++j) {
        b[i] += j;
    }
}

void foo(int* a_d, int* b_d) {
    unsigned int N = 1024;
    foo_kernel<<<(N + 128 - 1)/128, 128>>>(a_d, b_d);
}
```

For the same statement, what is the SIMD efficiency of warp 3 of block 0? Give a decimal in [0,1] with two decimals; do not include %.
Answer: 0.75
Explanation: Warp 3 covers 96-127; only 104-127 (24 lanes) satisfy the predicate. Efficiency = 24/32 = 75%.
Question: Consider the following CUDA kernel and host code:

```c
__global__ void foo_kernel(int* a, int* b) {
    unsigned int i = blockIdx.x*blockDim.x + threadIdx.x;
    if (threadIdx.x < 40 || threadIdx.x >= 104) {
        b[i] = a[i] + 1;
    }
    if (i % 2 == 0) {
        a[i] = b[i] * 2;
    }
    for (unsigned int j = 0; j < 5 - (i % 3); ++j) {
        b[i] += j;
    }
}

void foo(int* a_d, int* b_d) {
    unsigned int N = 1024;
    foo_kernel<<<(N + 128 - 1)/128, 128>>>(a_d, b_d);
}
```

For the statement `if (i % 2 == 0) { ... }`:
(i) How many warps in the grid are active on this statement?
Answer: 32
Explanation: All warps reach the statement; within each warp, half the threads satisfy `i % 2 == 0`, but the warp itself is active. Total warps = 32.
Question: Consider the following CUDA kernel and host code:

```c
__global__ void foo_kernel(int* a, int* b) {
    unsigned int i = blockIdx.x*blockDim.x + threadIdx.x;
    if (threadIdx.x < 40 || threadIdx.x >= 104) {
        b[i] = a[i] + 1;
    }
    if (i % 2 == 0) {
        a[i] = b[i] * 2;
    }
    for (unsigned int j = 0; j < 5 - (i % 3); ++j) {
        b[i] += j;
    }
}

void foo(int* a_d, int* b_d) {
    unsigned int N = 1024;
    foo_kernel<<<(N + 128 - 1)/128, 128>>>(a_d, b_d);
}
```

For the statement `if (i % 2 == 0) { ... }`:
(ii) How many warps in the grid are divergent on this statement?
Answer: 32
Explanation: Within every warp, half the lanes are even and half odd, so every warp diverges on this predicate.
Question: Consider the following CUDA kernel and host code:

```c
__global__ void foo_kernel(int* a, int* b) {
    unsigned int i = blockIdx.x*blockDim.x + threadIdx.x;
    if (threadIdx.x < 40 || threadIdx.x >= 104) {
        b[i] = a[i] + 1;
    }
    if (i % 2 == 0) {
        a[i] = b[i] * 2;
    }
    for (unsigned int j = 0; j < 5 - (i % 3); ++j) {
        b[i] += j;
    }
}

void foo(int* a_d, int* b_d) {
    unsigned int N = 1024;
    foo_kernel<<<(N + 128 - 1)/128, 128>>>(a_d, b_d);
}
```

For the statement `if (i % 2 == 0) { ... }`:
(iii) What is the SIMD efficiency of warp 0 of block 0? Give a decimal between 0 and 1 with two decimals; do not include a % sign.
Answer: 0.50
Explanation: Exactly half the lanes (even indices) are active: 16/32 = 50%.
Question: Consider the following CUDA kernel and host code:

```c
__global__ void foo_kernel(int* a, int* b) {
    unsigned int i = blockIdx.x*blockDim.x + threadIdx.x;
    if (threadIdx.x < 40 || threadIdx.x >= 104) {
        b[i] = a[i] + 1;
    }
    if (i % 2 == 0) {
        a[i] = b[i] * 2;
    }
    for (unsigned int j = 0; j < 5 - (i % 3); ++j) {
        b[i] += j;
    }
}

void foo(int* a_d, int* b_d) {
    unsigned int N = 1024;
    foo_kernel<<<(N + 128 - 1)/128, 128>>>(a_d, b_d);
}
```

Consider the loop `for (unsigned int j = 0; j < 5 - (i % 3); ++j) { ... }`.
(i) How many loop iterations (values of j) execute with no divergence across the entire grid?
Answer: 3
Explanation: Threads with i%3 in {0,1,2} have bounds 5,4,3 respectively. For j = 0,1,2 all threads execute; for j = 3 and 4 some threads do not, causing divergence. Hence 3 non-divergent iterations.
Question: Consider the following CUDA kernel and host code:

```c
__global__ void foo_kernel(int* a, int* b) {
    unsigned int i = blockIdx.x*blockDim.x + threadIdx.x;
    if (threadIdx.x < 40 || threadIdx.x >= 104) {
        b[i] = a[i] + 1;
    }
    if (i % 2 == 0) {
        a[i] = b[i] * 2;
    }
    for (unsigned int j = 0; j < 5 - (i % 3); ++j) {
        b[i] += j;
    }
}

void foo(int* a_d, int* b_d) {
    unsigned int N = 1024;
    foo_kernel<<<(N + 128 - 1)/128, 128>>>(a_d, b_d);
}
```

For the same loop:
(ii) How many loop iterations (values of j) have divergence somewhere in the grid?
Answer: 2
Explanation: Iterations j = 3 and j = 4 are executed by only some threads (depending on i%3), so both are divergent.
Question: Vector addition of length 2000; one output element per thread; thread block size = 512 threads. Using the minimum number of blocks to cover all elements, how many threads are in the grid?
Answer: 2048
Explanation: Blocks = ceil(2000/512) = 4; threads = 4 x 512 = 2048.
Question:  with 2000 elements and 512 threads per block (minimal blocks), how many warps do you expect to have divergence due to the boundary check (threads skipping work past N)?
Answer: 1
Explanation: Total warps = 2048/32 = 64. Only the warp covering thread indices 1984-2015 has some active (<=1999) and some inactive (>=2000) threads. The final warp (2016-2047) has all threads inactive (no divergence).
Question: A block with 8 threads executes a section before a barrier. Times (us) to reach the barrier are: 2.0, 2.3, 3.0, 2.8, 2.4, 1.9, 2.6, 2.9. Threads then wait at the barrier until the slowest arrives. What percentage of the aggregate thread time is spent waiting? Give a percentage to one decimal place; do not include %.
Answer: 17.1
Explanation: Max time = 3.0. Waiting per thread = (3.0 - t). Sum waits = 4.1 us. Aggregate time = 8 x 3.0 = 24 us. Percentage ~ 4.1/24 ~ 17.1%.
Question: An SM supports up to 1536 threads and up to 4 blocks concurrently. For a single SM, which block size among {128, 256, 512, 1024} yields the maximum number of resident threads, and how many threads does it schedule? Return the result as a tuple (BLOCK_SIZE, THREADS).
Answer: (512, 1536)
Explanation: For 128: min(4, 1536/128)=4 blocks -> 4x128=512 threads. For 256: 4 blocks -> 1024. For 512: 3 blocks (limited by total threads) -> 3x512=1536. For 1024: 1 block -> 1024. Max is 1536 with 512/block.
Question: A device allows up to 64 blocks per SM and 2048 threads per SM. For each per-SM assignment below, state if it's possible and the occupancy (% of 2048 threads):
(a) 8 blocks x 128 threads
(b) 16 blocks x 64 threads
(c) 32 blocks x 32 threads
(d) 64 blocks x 32 threads
(e) 32 blocks x 64 threads Provide five semicolon-separated tuples (ans,occ) for (a)-(e), where ans is Yes/No and occ is an integer percent with no %.
Answer: (Yes,50);(Yes,50);(Yes,50);(Yes,100);(Yes,100)
Explanation: Check blocks ≤ 64 and total threads ≤ 2048 for each case; occupancy = total_threads / 2048.
Question: A GPU has 2048 threads/SM, 32 blocks/SM, and 65,536 registers/SM. For each kernel, can it achieve full occupancy (2048 threads/SM)? If not, what limits it?
(a) 128 threads/block, 30 registers/thread
(b) 32 threads/block, 29 registers/thread
(c) 256 threads/block, 34 registers/thread Provide three semicolon-separated tuples (ans,limit,threads) for (a)-(c), where ans is Yes/No, limit in {none,blocks,registers}, threads is an integer.
Answer: (Yes,none,2048);(No,blocks,1024);(No,registers,1792)
Explanation: For each case, first bound by blocks from threads/SM, then check blocks limited by registers per block. Compare resulting resident threads to 2048.
Question: We multiply two 8x8 matrices C=AxB on a GPU. One thread computes one C[i,j]. Using shared-memory tiling with tile size T=2 (2x2 tiles), and assuming no caching except shared memory, how many total global-memory element loads (A and B only; ignore the 64 stores of C) are performed for the full multiplication? Give an integer; do not include units or x.
Answer: 2
Explanation: Baseline (no tiling, T=1): 64 outputs x 8 MACs/output x 2 loads/MAC = 1024 loads. With tiling of size T, loads = (8/T)^2 x (8/T) x 2T^2 = 1024/T. For T=2 -> 1024/2 = 512.
Question: Consider an 8x8 matrix multiplication with tile size T=2, what is the reduction factor in global-memory traffic versus the naive untiled case (T=1)? Give an integer; do not include units or x.
Answer: 2
Explanation: Untiled: 1024 loads. T=2: 512 loads. Reduction factor = 1024 / 512 = 2x.
Question: Now use tile size T=4 (4x4 tiles) for an 8x8 matrix multiplication. How many total global-memory element loads (A and B only; ignore C stores) are performed?
Answer: 256
Explanation: Using loads = 1024/T with N=8, for T=4 we have 1024/4 = 256.
Question: Consider an 8x8 matrix multiplication with tile size T=4. What is the reduction factor in global-memory traffic relative to the naive untiled case (T=1)? Give an integer; do not include units or x.
Answer: 4
Explanation: Untiled: 1024 loads. T=4: 256 loads. Reduction factor = 1024 / 256 = 4x, confirming linear scaling with tile dimension.
Question: Consider matrix addition C = A + B. Can shared memory be used to reduce global memory bandwidth consumption for this kernel? Briefly justify your answer.
Answer: No.
Explanation: In element-wise addition, each output element C[i,j] depends only on A[i,j] and B[i,j] once. Threads do not reuse neighbors' A or B values, so there is no inter-thread temporal locality to exploit. Caching A/B into shared memory would just add extra copies without reducing global loads.
Question: Assume register and shared memory capacities are not limiting. Give one important reason why using shared memory (instead of registers) to hold values fetched from global memory can be valuable.
Answer: Shared memory enables inter-thread data sharing within a block.
Explanation: Registers are private to a thread. Shared memory is visible to all threads in a block, so a value fetched once from global memory can be reused by multiple threads, reducing global traffic.
Question: For a tiled matrix-matrix multiplication kernel using 32x32 tiles, what is the reduction in global memory bandwidth usage for the input matrices M and N (compared to the untiled naive access), assuming ideal reuse within a tile? Provide the two reduction factors as M,N (integers); do not include x or text.
Answer: 32,32
Explanation: Within a 32x32 tile, each loaded element of M (resp. N) is reused across 32 multiply-accumulates along the tile dimension, replacing 32 separate global loads in the naive scheme. Thus, global loads per useful use drop by ~32x for both inputs.