Efficient AI

Tail Bounds(White Board) & GPU Programming Model

Aditya Desai · August 12th 2026

Many thanks to all the resources listed in References on the main website. Almost all figures are taken from these resources; some were generated with Gemini. Some images are taken from Google Images and still need proper citation (in progress).

Example Workload: $C = A + B$

Problem. Given two matrices $A, B \in \mathbb{R}^{m \times n}$, compute $C \in \mathbb{R}^{m \times n}$ by element-wise addition and store the result:

$$C_{ij} = A_{ij} + B_{ij} \quad \text{for all } i \in [m],\; j \in [n]$$

Element-wise matrix addition: C equals A plus B

Example Workload: $C = A + B$

Traditional C (row-major, nested loops):

void mat_add(const float *A, const float *B, float *C,
             int m, int n) {
  for (int i = 0; i < m; i++) {
    for (int j = 0; j < n; j++) {
      int idx = i * n + j;
      C[idx] = A[idx] + B[idx];
    }
  }
}
  • One thread of control; $mn$ additions in sequence
  • Entry $(i,j)$ lives at offset $i\cdot n + j$

Example Workload: $C = A + B$

  • Massive parallelization opportunity: $mn$ additions can be done in parallel.
Each matrix entry added independently by a parallel thread

OpenMP Code for $C = A + B$

Shared-memory parallelism via OpenMP pragmas:

#include <omp.h>

void mat_add(const float *A, const float *B, float *C,
             int m, int n) {
  #pragma omp parallel for collapse(2)
  for (int i = 0; i < m; i++) {
    for (int j = 0; j < n; j++) {
      int idx = i * n + j;
      C[idx] = A[idx] + B[idx];
    }
  }
}
  • #pragma omp parallel for — split loop iterations across threads
  • collapse(2) — flatten the nested loops into one $mn$-iteration space

CPU vs. GPU architecture choice

CPU vs GPU architecture

GPU Code for $C = A + B$

CUDA C: one thread per entry

__global__ void mat_add(const float *A, const float *B,
                        float *C, int m, int n) {
  int i = blockIdx.y * blockDim.y + threadIdx.y;
  int j = blockIdx.x * blockDim.x + threadIdx.x;
  if (i < m && j < n) {
    int idx = i * n + j;
    C[idx] = A[idx] + B[idx];
  }
}

// launch in main:
main() {
  ....
  dim3 block(16, 16);
  dim3 grid((n + 15) / 16, (m + 15) / 16);
  mat_add<<<grid, block>>>(A_d, B_d, C_d, m, n);
  ....
}
  • Kernel: each thread computes one $C_{ij}$
  • Launch: 2D grid of $16\times 16$ thread blocks covering $m\times n$
  • Grid of blocks and block of threads — a way of organizing threads.
  • Defines how threads are mapped to hardware.
  • And thus how they are allowed to interact.

GPU systems

Heterogeneous CPU–GPU setup

Streaming Multiprocessors

Streaming Multiprocessor architecture

Block / Thread mapping to hardware

Block and thread mapping to GPU hardware
  1. All threads in a block are executed on a single SM.
  2. These threads can sync among each other and use shared memory for information sharing.
  3. Threads across blocks cannot sync.

Warp: unit of computing and scheduling

Warp: unit of computing and scheduling

Warp: Wrap divergence

Warp: unit of computing and scheduling

Memory: A note

Memory: A note

Tensor Cores

Deep learning needs matrix multiplication

Most deep-learning compute reduces to GEMMs (\(C = AB\)). One output entry needs \(K\) multiply-adds: \(C_{ij} = \sum_k A_{ik} B_{kj}\).

Neural net
Linear / attention
GEMM
$C=AB$
CUDA kernel
tiles + warps
Tensor Core
MMA
Key idea: GEMMs are heavy — large models contain millions or billions of independent dot products. Tensor Cores add specialized hardware to run many multiply-accumulates in parallel (they do not replace CUDA cores).

CUDA cores vs. Tensor Cores

CUDA core

  • General-purpose scalar/vector arithmetic
  • Flexible instructions
  • One thread controls its own arithmetic
  • Useful for arbitrary kernels

Tensor Core

  • Specialized matrix-multiply-accumulate datapath
  • Operates on small matrix tiles
  • Designed for very high throughput
  • Ideal for GEMM / convolution / attention

Think: scalar ALU vs. a tiny matrix-multiplication engine.

What does a Tensor Core actually compute?

Conceptually, an MMA computes \( D = A B + C \)

Inputs

Low/medium precision, e.g. FP16, BF16, TF32, FP8

Multiply

Many products computed in parallel

Accumulate

Into a wider accumulator, e.g. FP32

Tile shapes and supported datatypes depend on GPU architecture and instruction.

Tensor Core evolution

Generation Representative NVIDIA GPUs Important capability
Volta V100 Tensor Cores introduced; FP16 → FP32 accumulation
Turing T4 / RTX 20 More formats + broader workloads
Ampere A100 / RTX 30 TF32, BF16, structured sparsity
Hopper H100 FP8, Transformer Engine, improved MMA
Blackwell B100/B200/RTX 50 family Further FP4/FP8-oriented AI throughput

Why lower precision?

Lower precision means:

  • More values moved per byte
  • Smaller storage footprint
  • More arithmetic units per area
  • Higher matrix throughput

Typical ML pattern

FP16 / BF16 / FP8 inputs

multiply at low precision

accumulate in wider precision

Important: Tensor Core throughput is only useful if the numerical format is acceptable for the workload.

From GEMM to Tensor Cores: tiling

A tiles × B tiles → C

Large GEMMs are decomposed into tiles; warps cooperate on tiles that map naturally to MMA instructions.

Warp-level matrix operations

A Tensor Core instruction is coordinated across threads in a warp.

Thread registers

Each lane owns fragments of matrices.

Warp

Threads collectively issue an MMA operation.

Tensor Core

Hardware consumes fragments and produces accumulator fragments.

  • Fragment layout is architecture/instruction dependent.
  • Higher-level APIs hide much of this complexity.

Tensor Cores and sparsity

Modern NVIDIA GPUs can accelerate certain structured sparse matrix operations.

Example: 2:4 sparsity

Within each group of four values, two are nonzero.

Dense:

$$[a,b,c,d]$$

Sparse 2:4:

$$[a,0,c,0]$$

Hardware/software encode the nonzero positions so the matrix engine can skip work.

Contrast with unstructured sparsity: arbitrary zeros are much harder to map efficiently onto fixed-width matrix hardware.

Tensor Cores in Transformers

Linear layers

\( Y = XW \)

Usually excellent Tensor Core workloads.

Attention

\( QK^\top \) and \( PV \)

Matrix-heavy; shape and memory behavior matter.

MLP

Two large GEMMs dominate many transformer blocks.

This is why Tensor Core throughput is central to modern LLM training and inference.

References