0% found this document useful (0 votes)
11 views18 pages

SIMD and MIMD Systems Explained

The document discusses various parallel computing concepts, including SIMD and MIMD systems, their characteristics, and applications. It explains interconnection networks, cache coherence, GPU programming, and MPI functions with examples. Additionally, it covers performance metrics like speedup and efficiency, and the trapezoidal rule in MPI for numerical integration.
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
11 views18 pages

SIMD and MIMD Systems Explained

The document discusses various parallel computing concepts, including SIMD and MIMD systems, their characteristics, and applications. It explains interconnection networks, cache coherence, GPU programming, and MPI functions with examples. Additionally, it covers performance metrics like speedup and efficiency, and the trapezoidal rule in MPI for numerical integration.
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

1

Explain SIMD system with its characteristics, vector processor and how graphics processing units
work.
SIMD (Single Instruction, Multiple Data) System:
A SIMD system executes a single instruction on multiple data points simultaneously, making it ideal for
tasks with inherent data parallelism. Think of it like processing multiple pixels at once for image editing
Characteristics:
- Data Parallelism: Same operation on multiple data elements.
- Synchronous Execution: One instruction operates on all data elements in parallel.
- Vector Operations: Handles large datasets as vectors or arrays.

Vector Processor:
A vector processor is a type of SIMD processor designed for scientific and engineering tasks. It operates
on vectors (arrays of data) using a single instruction. Example: Cray supercomputers.

Graphics Processing Units (GPUs):


GPUs are massively parallel SIMD processors designed for graphics rendering and compute tasks. They
have thousands of cores (like CUDA cores in NVIDIA GPUs) that execute the same instruction on multiple
data points (pixels, vertices, etc.) simultaneously.

- How GPUs Work:


- Execute shaders (small programs) on multiple pixels/vertices in parallel.
- Handle tasks like 3D rendering, matrix operations, and more.
- Used in gaming, AI, machine learning, and scientific simulations.

Explain MIMD systems with it's two principal types


MIMD (Multiple Instruction, Multiple Data) Systems:
MIMD systems execute multiple instructions on multiple data streams simultaneously, making them
highly flexible and powerful. Each processor can run a different program on different data, enabling
task-level parallelism 😊.

Two Principal Types:


1. Shared Memory MIMD:
- Multiple processors share a common memory space.
- Communication happens through shared variables.
- Processors can access and modify shared data, requiring synchronization mechanisms (like locks or
semaphores) to avoid conflicts.
- Example: Multi-core CPUs, SMP (Symmetric Multiprocessing) systems.
- Advantages: Easy to program, fast data sharing.
- Challenges: Scalability, memory consistency.
2

2. Distributed Memory MIMD:


- Each processor has its own private memory.
- Communication happens through message passing (like MPI - Message Passing Interface).
- Each node (processor + memory) operates independently, and data is explicitly exchanged between
nodes.
- Example: Clusters, distributed systems, supercomputers.
- Advantages: Scalable, fault-tolerant.
- Challenges: Programming complexity, communication overhead.

whats interconnection networks?explain shared memory interconnects and distributed memory


interconnects
Interconnection Networks:
Interconnection networks are the pathways that allow processors, memory modules, and other
components in a parallel system to communicate 😊. They play a crucial role in performance, scalability,
and cost.

Shared Memory Interconnects:


In shared memory systems, interconnects link processors to a shared memory. Examples:
- Bus: Simple, shared bus connects all processors and memory. Can be bottleneck.
- Crossbar Switch: High-speed switch connects multiple inputs to outputs, allowing simultaneous
transfers.
- Multistage Interconnects: Like omega networks, offer a balance between cost and performance.

Distributed Memory Interconnects:


In distributed memory systems, interconnects link processors (each with local memory) to enable
message passing. Examples:
- Direct Networks: Like mesh, torus, or hypercube topologies. Each node connects to neighbors.
- Indirect Networks: Routers connect nodes, like in fat tree or butterfly networks.
- High-speed interconnects: Like InfiniBand or custom interconnects in supercomputers.

What is parallel computing? Explain Flynn's classifications of parallel computers


Parallel computing is a technique where multiple processes or threads execute simultaneously,
improving processing speed and efficiency. It's widely used in high-performance computing, data
analytics, and more. By dividing tasks into sub-tasks that run concurrently, parallel computing achieves
faster execution times and better resource utilization.

Flynn's Classifications:
1. SISD (Single Instruction, Single Data): Traditional sequential computers, like old single-core CPUs,
execute one instruction on one data point at a time. It's the classic von Neumann architecture.
3

2. SIMD (Single Instruction, Multiple Data): Executes a single instruction on multiple data points. Used in
GPUs for parallel processing tasks like graphics rendering, vector processing, and more.
3. MISD (Multiple Instruction, Single Data): Rarely used, involves multiple instructions operating on a
single data stream. You might see this in fault-tolerant systems.
4. MIMD (Multiple Instruction, Multiple Data): Most modern parallel systems, like multi-core CPUs,
execute multiple instructions on multiple data streams, enhancing performance. This includes
distributed systems and multi-threaded processors.

whats cache coherence?


Cache Coherence:
Cache coherence ensures that multiple caches in a shared memory system have consistent views of
shared data 😊. When one processor updates data, other caches with copies must be updated or
invalidated to avoid stale data.

Key Challenges:
- Multiple copies: Data can exist in multiple caches.
- Updates: Changes in one cache must be visible to others.

Cache Coherence Protocols:


1. Snooping Protocols: Caches "snoop" the bus to track shared data.
- Write-invalidate: Invalidate other copies on write.
- Write-update: Update other copies on write.
2. Directory-based Protocols: Central directory tracks cache line states.
- Scalable for large systems.

MESI Protocol (Example):


- M (Modified): Cache has modified data.
- E (Exclusive): Cache has only copy, unmodified.
- S (Shared): Multiple caches share data.
- I (Invalid): Cache line is invalid.

Explain GPU programming and describe heterogeneous programming with suitable explanation
GPU Programming:
GPU programming involves writing code to run on Graphics Processing Units (GPUs) for tasks like
graphics rendering, scientific simulations, and AI 😊. GPUs are massively parallel, with thousands of cores.

- Key aspects:
- SIMD architecture: Execute same instruction on multiple data points.
- Threads: Thousands of lightweight threads run in parallel.
- Memory hierarchy: Global, shared, local memory types.
4

- Programming models:
- CUDA (NVIDIA): For NVIDIA GPUs.
- OpenCL: Cross-platform, for various GPUs.
- Vulkan, DirectX: For graphics and compute.

Heterogeneous Programming:
Heterogeneous programming involves using both CPUs and GPUs (or other accelerators) in a system for
different tasks 😊.
- Why?: CPUs are great for serial tasks, GPUs for parallel tasks.
- How?:
- Offloading: Send parallel tasks to GPU, serial to CPU.
- Memory management: Handle data transfers between CPU-GPU.
- Frameworks: CUDA, OpenCL, OpenACC help manage heterogeneity.
- Example: Matrix multiplication (GPU) + data prep (CPU).

Explain MIMD System


MIMD (Multiple Instruction, Multiple Data) System:
MIMD systems execute multiple instructions on multiple data streams simultaneously 😊. Each processor
can run a different program on different data, enabling task-level parallelism.

- Key characteristics:
- Multiple processors: Run independently, often with own control unit.
- Different instructions/data: Each processor handles different tasks/data.
- Asynchronous execution: Processors operate on their own pace.
- Scalable: Can scale to many processors.
- Types:
- Shared Memory MIMD: Processors share memory, communicate via shared variables.
- Example: Multi-core CPUs, SMP systems.
- Distributed Memory MIMD: Each processor has private memory, communicate via message passing.
- Example: Clusters, supercomputers.
- Advantages:
- Flexibility: Handle diverse tasks efficiently.
- Scalability: Scale to large systems.
- Performance: High throughput for parallel tasks.
- Challenges:
- Programming complexity: Managing parallelism, communication.
- Synchronization: Coordinating between processors.
5

Explain programming hybrid systems and discuss the advantages and challenges of using multiple
parallel programming APIs
Programming Hybrid Systems:
Hybrid systems combine different processing units like CPUs, GPUs, and accelerators 😊. Programming
them involves managing tasks across diverse architectures.

- Approach:
- Heterogeneous programming models: Like CUDA, OpenCL, OpenACC.
- Task distribution: Divide tasks based on processor strengths.
- Data management: Handle transfers between devices.
- Example: Use CPU for serial tasks, GPU for parallel tasks like matrix ops.

Advantages of Multiple Parallel Programming APIs:


- Flexibility: Choose best API for task/architecture.
- Performance: Leverage API-specific optimizations.
- Portability: Use APIs like OpenCL for cross-platform code.

Challenges:
- Complexity: Managing multiple APIs, data transfers.
- Debugging: Harder with diverse architectures.
- Maintenance: Code can get fragmented across APIs.

Define speedup and efficiency in MIMD systems and explain their significance in performance
evaluation.
Speedup and Efficiency in MIMD Systems:
In MIMD (Multiple Instruction, Multiple Data) systems, speedup and efficiency are crucial metrics for
evaluating parallel performance 😊.

- Speedup (S): Measures how much faster a parallel system completes a task compared to a sequential
system.
- Formula: S = T<sub>sequential</sub> / T<sub>parallel</sub>
- Ideal speedup is linear: S = p (with p processors).
- Efficiency (E): Measures how effectively processors are utilized.
- Formula: E = Speedup / p = T<sub>sequential</sub> / (p * T<sub>parallel</sub>)
- Ideal efficiency: E = 1 (100% utilization).
Significance in Performance Evaluation:
- Speedup: Shows performance gain from parallelism.
- Efficiency: Indicates how well resources are used.
- Scalability insights: Help decide if adding processors improves performance.
- Optimization guide: Low efficiency might mean bottlenecks or imbalance.
6

Define MPI. Explain the following MPI functions with code snippets:
a) MPI Init b) MPI_Finalize c) MPI_Comm_Size d) MPI_Comm_Rank e)MPI Reduce
f)MPI_Allreduce g) Broadcast
MPI (Message Passing Interface):
MPI is a standard for parallel programming in distributed memory systems 😊. It's widely used for high-
performance computing.
MPI Functions with Code Snippets:
a) MPI_Init:
Initializes MPI environment.
int argc;
char **argv;
MPI_Init(&argc, &argv);

b) MPI_Finalize:
Cleans up MPI environment.
MPI_Finalize();

c) MPI_Comm_size:
Gets communicator size (number of processes).
int size;
MPI_Comm_size(MPI_COMM_WORLD, &size);

d) MPI_Comm_rank:
Gets process rank (ID).
int rank;
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
e) MPI_Reduce:
Reduces data across processes (e.g., sum).
int data = 10, sum;
MPI_Reduce(&data, &sum, 1, MPI_INT, MPI_SUM, 0, MPI_COMM_WORLD);
f) MPI_Allreduce:
Allreduce: reduction result on all processes.
int data = 10, sum;
MPI_Allreduce(&data, &sum, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD);
g) MPI_Bcast:
Broadcasts data from root to all.
int data;
if (rank == 0) data = 100;
MPI_Bcast(&data, 1, MPI_INT, 0, MPI_COMM_WORLD);
// data now 100 on all processes.
7

Explain the following MPI functions with suitable MPI programs for each:
a)Scatter b) Gather c) Allgather
MPI Functions:
a) MPI_Scatter:Scatters data from root to all processes 😊.
- Syntax: MPI_Scatter(sendbuf, sendcount, sendtype, recvbuf, recvcount, recvtype, root, comm)
- Example: Root sends portions of array to processes.

#include <mpi.h>
int main(int argc, char** argv) {
int rank, size;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);
int sendbuf[4] = {1, 2, 3, 4};
int recvbuf;
MPI_Scatter(sendbuf, 1, MPI_INT, &recvbuf, 1, MPI_INT, 0, MPI_COMM_WORLD);
printf("Rank %d got %d\n", rank, recvbuf);
MPI_Finalize();
return 0;
}
// Output (4 processes):
// Rank 0 got 1
// Rank 1 got 2
// Rank 2 got 3
// Rank 3 got 4

b) MPI_Gather:Gathers data from all to root 😊.


- Syntax: MPI_Gather(sendbuf, sendcount, sendtype, recvbuf, recvcount, recvtype, root, comm)
- Example: Each process sends data; root gathers into array.
#include <mpi.h>
int main(int argc, char** argv) {
int rank, size;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);

int sendbuf = rank + 1;


int recvbuf[4];
MPI_Gather(&sendbuf, 1, MPI_INT, recvbuf, 1, MPI_INT, 0, MPI_COMM_WORLD);
8

if (rank == 0) {
printf("Root gathered: ");
for (int i = 0; i < size; i++)
printf("%d ", recvbuf[i]);
}
MPI_Finalize();
return 0;
}
// Output (4 processes):
// Root gathered: 1 2 3 4

c) MPI_Allgather:Gathers data from all to all 😊.


- Syntax: MPI_Allgather(sendbuf, sendcount, sendtype, recvbuf, recvcount, recvtype, comm)
- Example: Each process gets data from all.

#include <mpi.h>
int main(int argc, char** argv) {
int rank, size;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);

int sendbuf = rank + 1;


int recvbuf[4];
MPI_Allgather(&sendbuf, 1, MPI_INT, recvbuf, 1, MPI_INT, MPI_COMM_WORLD);

printf("Rank %d got: ", rank);


for (int i = 0; i < size; i++)
printf("%d ", recvbuf[i]);
printf("\n");
MPI_Finalize();
return 0;
}
// Output (4 processes):
// Rank 0 got: 1 2 3 4
// Rank 1 got: 1 2 3 4
// Rank 2 got: 1 2 3 4
// Rank 3 got: 1 2 3 4
9

Explain the trapezoidal rule in MPI. Write a program to demonstrate the trapezoidal rule in MPI and
illustrate the importance of Trap function
Trapezoidal rule in MPI:
The trapezoidal rule is a numerical integration technique to approximate the value of a definite integral
😊. In MPI, we can parallelize it by dividing the interval among processes.
- Formula: ∫f(x)dx ≈ (h/2)[f(x0) + 2f(x1) + ... + 2f(xn-1) + f(xn)]
- Parallel approach: Divide [a,b] among p processes, each computes local sum, then reduce.
MPI Program for Trapezoidal Rule:
#include <stdio.h>
#include <mpi.h>
double f(double x) { return x*x; }
double Trap(double a, double b, int n, double (*f)(double)) {
double h = (b-a)/n;
double sum = (f(a) + f(b))/2.0;
for (int i = 1; i < n; i++)
sum += f(a + i*h);
return sum * h;
}
int main(int argc, char** argv) {
int rank, size;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
MPI_Comm_size(MPI_COMM_WORLD, &size);
double a = 0.0, b = 1.0;
int n = 1024;
double local_a = a + rank*(b-a)/size;
double local_b = local_a + (b-a)/size;
int local_n = n/size;
double local_sum = Trap(local_a, local_b, local_n, f);
double total_sum;
MPI_Reduce(&local_sum, &total_sum, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD);
if (rank == 0)
printf("Integral ≈ %.6f\n", total_sum);
MPI_Finalize();
return 0;
}

Importance of `Trap` Function:- Modularity: Trap encapsulates integration logic.


- Reusability: Can use for any f(x) by passing function pointer.
- Parallelism: Each process computes local integral independently
10

Explain Point to point communication and Explain MPI_Send and MPI_Recv functions with MPI
programs
Point-to-Point Communication:
Point-to-point communication involves direct data transfer between two processes 😊. In MPI, this is
achieved using MPI_Send and MPI_Recv.

MPI_Send:Sends a message to another process.


- Syntax: MPI_Send(buf, count, datatype, dest, tag, comm)
- buf: Data to send
- dest: Rank of destination process
- tag: Message identifier

MPI_Recv:Receives a message from another process.


- Syntax: MPI_Recv(buf, count, datatype, source, tag, comm, status)
- buf: Where to store received data
- source: Rank of sender (or MPI_ANY_SOURCE)
- tag: Message tag (or MPI_ANY_TAG)

Example Program:
#include <mpi.h>
#include <stdio.h>
int main(int argc, char** argv) {
int rank;
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &rank);
if (rank == 0) {
int data = 100;
MPI_Send(&data, 1, MPI_INT, 1, 0, MPI_COMM_WORLD);
printf("Process 0 sent: %d\n", data);
} else if (rank == 1) {
int data;
MPI_Recv(&data, 1, MPI_INT, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE);
printf("Process 1 received: %d\n", data);
}
MPI_Finalize();
return 0;
}
// Output:
// Process 0 sent: 100
// Process 1 received: 100
11

Define OpenMP. Explain the following OpenMP pragmas with code snippets:
a) atomic b) for c) parallel d) sections
OpenMP is an API for multi-platform shared-memory parallel programming in C/C++ and Fortran 😊. It
uses compiler directives to parallelize loops and code blocks.
OpenMP Pragmas with Code Snippets:
a) #pragma omp atomic:
Ensures a memory update is atomic
int x = 0;
#pragma omp parallel for
for (int i = 0; i < 100; i++) {
#pragma omp atomic
x++;
}
printf("%d\n", x); // Should print 100

b) #pragma omp for:


Divides loop iterations among threads 😊♂️.
int sum = 0;
#pragma omp parallel for reduction(+:sum)
for (int i = 1; i <= 100; i++)
sum += i;
printf("%d\n", sum); // 1+2+...+100

c) #pragma omp parallel:


Creates a parallel region 😊.
#pragma omp parallel
{
printf("Hello from thread %d\n", omp_get_thread_num());
}
// Each thread prints its ID

d) #pragma omp sections:


Divides work into separate sections 😊.
#pragma omp parallel sections
{
#pragma omp section
printf("Section 1 (thread %d)\n", omp_get_thread_num());
#pragma omp section
printf("Section 2 (thread %d)\n", omp_get_thread_num());
}// Two sections run in parallel
12

Explain reduction clause with syntax and example. How does the reduction clause simplify parallel
computation compared to using a critical section?
Reduction Clause:
The reduction clause in OpenMP combines results from threads 😊.
- Syntax: reduction(op : var)
- op: Operation (e.g., +, *, min, max)
- var: Variable to reduce

Example:
int sum = 0;
#pragma omp parallel for reduction(+:sum)
for (int i = 1; i <= 100; i++)
sum += i;
printf("%d\n", sum); // Prints 5050

vs Critical Section:- Reduction: Efficient, uses optimized tree-like combination.


- Critical: Uses locks, can be slower for many threads 😊.
// Using critical
int sum = 0;
#pragma omp parallel for
for (int i = 1; i <= 100; i++) {
#pragma omp critical
sum += i;
}

// Using reduction (faster, clearer)


int sum = 0;
#pragma omp parallel for reduction(+:sum)
for (int i = 1; i <= 100; i++)
sum += i;

Benefits of reduction:
- Faster: Optimized implementation.
- Clearer code: Expresses intent directly.
13

Define OpenMP and explain its programming model for shared-memory MIMD systems
OpenMP (Open Multi-Processing):
OpenMP is an API for multi-platform shared-memory parallel programming in C/C++ and Fortran 😊. It's
designed for shared-memory MIMD (Multiple Instruction, Multiple Data) systems.

OpenMP Programming Model:- Fork-Join Model:


- Master thread: Starts execution.
- Fork: Create parallel threads on encountering #pragma omp parallel.
- Join: Threads synchronize and terminate when work completes.
- Shared Memory:
- Threads share global variables.
- Use private/shared clauses to control visibility.
- Work Sharing:
- Directives like #pragma omp for divide work among threads.
- Synchronization:
- critical, barrier, atomic for coordinating threads.

Example:
#pragma omp parallel
{
int id = omp_get_thread_num();
printf("Hello from thread %d\n", id);
}
// Fork: threads run in parallel, Join: all done

Explain the trapezoidal rule and describe its parallel implementation using OpenMP
Trapezoidal Rule:
The trapezoidal rule approximates a definite integral 😊.
- Formula: ∫f(x)dx ≈ (h/2)[f(x0) + 2f(x1) + ... + 2f(xn-1) + f(xn)]
- h = (b-a)/n: Width of each subinterval.

Parallel Implementation with OpenMP:Divide interval [a,b] among threads, compute local sums,
combine 😊.
double trap(double a, double b, int n) {
double h = (b-a)/n, sum = 0.0;
#pragma omp parallel for reduction(+:sum)
for (int i = 0; i < n; i++) {
double x = a + i*h;
sum += (i==0 || i==n) ? f(x) : 2*f(x);
}
14

return (h/2) * sum;


}
- Parallelism: Each thread computes part of sum.
- Reduction: Combines local sums efficiently.

Define GPU and GPGPU. Explain the architecture of a GPU with a neat block diagram and compare it
with a CPU architecture
GPU (Graphics Processing Unit):
A GPU is a specialized processor for graphics and parallel computations 😊.
GPGPU (General-Purpose GPU):
GPGPU refers to using GPUs for general-purpose computing (not just graphics).

GPU Architecture:- Massively parallel: Thousands of cores (CUDA cores on NVIDIA).


- SIMD (Single Instruction, Multiple Data): Cores execute same instruction on different data.
- High memory bandwidth: Optimized for data-intensive tasks.
- Hierarchy: Blocks/threads, shared memory, global memory.

CPU Architecture:- Few powerful cores: Optimized for serial tasks.


- MIMD (Multiple Instruction, Multiple Data): Cores can execute different instructions.
- General-purpose: Handles diverse tasks.

Comparison:- Parallelism: GPU >> CPU (thousands vs few cores).


- Use case: GPU for parallel/data-intensive (e.g., ML, simulations); CPU for serial/general tasks.
- Latency vs Throughput: CPU low latency, GPU high throughput.

.Define threads, blocks and grids. Write a CUDA program that prints greetings from threads in
multiple blocks, and mention the variables that are initialized in each thread's memory when a kernel
begins
CUDA Concepts:
- Thread: Basic execution unit on GPU 😊.
- Block: Group of threads, runs on one SM (Streaming Multiprocessor).
- Grid: Collection of blocks.

CUDA Program:
#include <stdio.h>
__global__ void hello() {
int tid = threadIdx.x; // Thread ID in block
int bid = blockIdx.x; // Block ID in grid
printf("Hello from thread %d in block %d\n", tid, bid);
}
15

int main() {
int blocks = 2, threadsPerBlock = 3;
hello<<<blocks, threadsPerBlock>>>();
cudaDeviceSynchronize();
return 0;
}
// Output:
// Hello from thread 0 in block 0
// Hello from thread 1 in block 0
// Hello from thread 2 in block 0
// Hello from thread 0 in block 1
// Hello from thread 1 in block 1
// Hello from thread 2 in block 1

Thread Memory:Each thread has:


- Registers: Local variables (e.g., tid, bid).
- Local memory: Spills if registers overflow.

Explain heterogeneous computing and describe the interaction between host (CPU) and device (GPU)
Heterogeneous Computing:
Heterogeneous computing uses different processor types (e.g., CPU + GPU) for specific tasks 😊. CPUs
handle general tasks, GPUs handle parallel-intensive work.

Host (CPU) and Device (GPU) Interaction:- Host: CPU manages main program, initiates GPU work.
- Device: GPU executes parallel kernels.
- Interaction:
- Memory transfer: CPU<->GPU via PCIe.
- Kernel launch: CPU tells GPU what to run.
- Synchronization: CPU waits for GPU via cudaDeviceSynchronize().

Example Flow:1. CPU prepares data.


2. CPU copies data to GPU memory (cudaMemcpy).
3. CPU launches GPU kernel (<<<>>>).
4. GPU runs kernel in parallel.
5. CPU syncs, copies results back (cudaMemcpy).
16

Explain the CUDA programming model and describe the organization of threads, blocks, and grids
CUDA Programming Model:
CUDA is NVIDIA's platform for GPU computing 😊.
- Host (CPU): Manages data, launches kernels.
- Device (GPU): Executes kernels in parallel.
- Kernels: Functions run on GPU in parallel (__global__).

Organization:- Thread: Basic execution unit 😊. Executes kernel code.


- Has threadIdx (1D/2D/3D).
- Block: Group of threads, runs on one SM (Streaming Multiprocessor).
- Threads in block can:
- Sync (__syncthreads()).
- Share memory (__shared__).
- Has blockIdx, blockDim.
- Grid: Collection of blocks.
- Blocks run independently.
- Has gridDim.

Example:
cuda
__global__ void add(int* a, int* b, int* c) {
int idx = threadIdx.x + blockIdx.x * blockDim.x;
c[idx] = a[idx] + b[idx];
}
int main() {
int blocks = 2, threadsPerBlock = 256;
add<<<blocks, threadsPerBlock>>>(d_a, d_b, d_c);
}
- Threads: Do computation in parallel.
- Blocks: Grouped for SM execution.
- Grid: Manages blocks.

Explain NVIDIA compute capability and device architecture and discuss its significance in determining
GPU
NVIDIA Compute Capability:
Compute Capability defines a GPU's architectural features and supported instructions 😊.
- Version (e.g., 7.5, 8.6): Indicates architecture (e.g., Volta, Ampere).
- [Link]: Major = architecture, Minor = incremental improvements.
17

Device Architecture:- SM (Streaming Multiprocessor): Core execution unit.


- Contains CUDA cores, shared memory, registers.
- CUDA Cores: Execute threads.
- Memory Hierarchy:
- Global: Device memory.
- Shared: Block-level fast memory.
- Registers: Thread-local.

Significance:- Feature Support: Determines CUDA features (e.g., Tensor Cores, FP16).
- Performance: Impacts execution efficiency.
- Compatibility: Code must match or be <= target GPU's capability.

bash
$ nvidia-smi --query-gpu=compute_cap --format=csv
# Check GPU compute capability

Explain vector addition using CUDA and describe how thread indices are mapped to data elements
Vector Addition using CUDA:
Adds two vectors element-wise on GPU 😊.
cuda
__global__ void add(int* a, int* b, int* c, int n) {
int idx = threadIdx.x + blockIdx.x * blockDim.x;
if (idx < n) // Boundary check
c[idx] = a[idx] + b[idx];
}
int main() {
int n = 1024;
int blocks = (n + 255) / 256; // Ceiling divide
add<<<blocks, 256>>>(d_a, d_b, d_c, n);
}

Thread Indices Mapping:- 1D Mapping (common):


- idx = threadIdx.x + blockIdx.x * blockDim.x
- Each thread handles one element.
- N-D Mapping: For multidimensional data (e.g., images).

Example:- n=1024, blocks=4, threads/block=256:


- Thread (0,0) -> a[0], (0,255) -> a[255], (1,0) -> a[256], ...
18

Explain the methods used for returning results from CUDA kernels and discuss the role of unified
memory and explicit memory transfers.
Returning Results from CUDA Kernels:
1. Write to global memory:
- Kernel writes to device array, host copies back.

cuda
__global__ void add(int* a, int* b, int* c) {
c[tid] = a[tid] + b[tid];
}
cudaMemcpy(h_c, d_c, size, cudaMemcpyDeviceToHost);

2. Use pinned memory: Faster host<->device transfers.


3. Unified Memory (UM):
- Shared memory space, managed by CUDA.
- Simplifies programming.

Unified Memory (UM):- Single pointer: Accessible from host/GPU.


- Automatic migration: Pages move as needed.

cuda
cudaMallocManaged(&ptr, size);
kernel<<<...>>>(ptr);
cudaDeviceSynchronize(); // Ensure data is synced

- Pros: Easy to use, less explicit transfers.


- Cons: Potential overhead if not optimized.

Explicit Memory Transfers:- cudaMemcpy: Control transfers.


- Async transfers: Overlap with kernel execution.

cuda
cudaMemcpyAsync(d_a, h_a, size, H2D, stream);
kernel<<<...>>>(d_a);
cudaMemcpyAsync(h_c, d_c, size, D2H, stream);

You might also like