10-605 / 10-805
Machine Learning from Large Datasets
Recap: the course so far
2
Many of these are different with GPUs
3
Many of these are different with GPUs
4
WHAT ARE GPUS?
5
What is a GPU?
6
A graphics processing unit (GPU) is a specialized electronic circuit designed to rapidly manipulate and alter memory to accelerate the creation of images in a frame buffer intended for output to a display device. [wikipedia]
The term GPU was popularized by Nvidia in 1999, who marketed the GeForce 256 as "the world's first …Graphics Processing Unit.” It was presented as a "single-chip processor with integrated transform, lighting, triangle setup/clipping, and rendering engines".[3]
Rendering and lighting require fast parallel rotations, translations, and scaling
GPUs do linear algebra in parallel
Started to be used in mid-2000’s for ML – Theano (2007) was first DL framework based on GPUs
7
Summary of GPUs vs CPUs
9
GPUs are faster than CPUs:
maximum FLOPS/clock cycle
old slide
10
GPUs have more cores than CPUs
hi-end CPU: 32
hi-end GPU: 2000
GPUs have less memory than CPUs
hi-end CPU: 256 Gb
hi-end GPU: 8 Gb
old slide
11
GPUs compute faster because they are parallel
old slide
12
GPUs also read memory faster
(memory bandwidth: bits/sec)
because they are parallel
old slide
13
newer slide
Newer Accelerators
newer slide
15
newer slide
NVIDIA GB200 Superchip Incl. Two Blackwell GPUs and One Grace CPU
GraceHopper H100
Roughly speaking performance doubles every 2.5-3 years
GPUs vs CPUs
17
newer slide
GPUs vs TPUs/NPUs
History of Google’s TPU’s
Bigger models need more memory
Faster clocks mean faster training
Will discuss HBM later
More bandwidth 🡪 Faster Communication
HOW DO YOU USE A GPU?
22
Using GPUs for ML
23
Aside: Vectorizing logistic regression
24
Note : x・w can be partially computed in parallel
Aside: Vectorizing logistic regression
25
Note: Xbatch ・w can be computed in parallel…
Aside: Vectorizing logistic regression
26
Similarly for the rest of the algorithm…
28
Streaming SGD:
x1
x2
x3
w
x4
x5
x6
Single examples x, y
29
Streaming SGD:
X1
X2
X3
w
X4
X5
X6
Minibatch examples X, y
Limitations are
Parameters being learned are stored on the GPU to reduce communication costs
Copy them out occasionally to checkpoint and test the model
Using GPUs for ML
30
#include <iostream>
#include <algorithm>
using namespace std;
#define N 1024
#define RADIUS 3
#define BLOCK_SIZE 16
__global__ void stencil_1d(int *in, int *out) {
__shared__ int temp[BLOCK_SIZE + 2 * RADIUS];
int gindex = threadIdx.x + blockIdx.x * blockDim.x;
int lindex = threadIdx.x + RADIUS;
// Read input elements into shared memory
temp[lindex] = in[gindex];
if (threadIdx.x < RADIUS) {
temp[lindex - RADIUS] = in[gindex - RADIUS];
temp[lindex + BLOCK_SIZE] = in[gindex + BLOCK_SIZE];
}
// Synchronize (ensure all the data is available)
__syncthreads();
// Apply the stencil
int result = 0;
for (int offset = -RADIUS ; offset <= RADIUS ; offset++)
result += temp[lindex + offset];
// Store the result
out[gindex] = result;
}
void fill_ints(int *x, int n) {
fill_n(x, n, 1);
}
int main(void) {
int *in, *out; // host copies of a, b, c
int *d_in, *d_out; // device copies of a, b, c
int size = (N + 2*RADIUS) * sizeof(int);
// Alloc space for host copies and setup values
in = (int *)malloc(size); fill_ints(in, N + 2*RADIUS);
out = (int *)malloc(size); fill_ints(out, N + 2*RADIUS);
// Alloc space for device copies
cudaMalloc((void **)&d_in, size);
cudaMalloc((void **)&d_out, size);
// Copy to device
cudaMemcpy(d_in, in, size, cudaMemcpyHostToDevice);
cudaMemcpy(d_out, out, size, cudaMemcpyHostToDevice);
// Launch stencil_1d() kernel on GPU
stencil_1d<<<N/BLOCK_SIZE,BLOCK_SIZE>>>(d_in + RADIUS, d_out + RADIUS);
// Copy result back to host
cudaMemcpy(out, d_out, size, cudaMemcpyDeviceToHost);
// Cleanup
free(in); free(out);
cudaFree(d_in); cudaFree(d_out);
return 0;
}
serial code
parallel code
serial code
parallel fn
Simple Processing Flow
31
PCI Bus
Simple Processing Flow
32
PCI Bus
Simple Processing Flow
33
PCI Bus
SOME EXAMPLE CODE
34
Hello World!
35
int main(void) {
printf("Hello World!\n");
return 0;
}
Output:
$ nvcc hello_world.cu
$ a.out
Hello World!
$
Hello World! with Device Code
36
__global__ void mykernel(void) {
}
int main(void) {
mykernel<<<1,1>>>();
printf("Hello World!\n");
return 0;
}
Hello World! with Device Code
__global__ void mykernel(void) {
}
37
Hello World! with Device COde
mykernel<<<1,1>>>();
38
Hello World! with Device Code
39
__global__ void mykernel(void){
}
int main(void) {
mykernel<<<1,1>>>();
printf("Hello World!\n");
return 0;
}
Output:
$ nvcc hello.cu
$ a.out
Hello World!
$
Addition on the Device
__global__ void add(int *a, int *b, int *c) {
*c = *a + *b;
}
40
Addition on the Device
__global__ void add(int *a, int *b, int *c) {
*c = *a + *b;
}
41
Memory Management
May be passed to/from host code
May not be dereferenced in host code
May be passed to/from device code
May not be dereferenced in device code
42
Addition on the Device: add()
__global__ void add(int *a, int *b, int *c) {
*c = *a + *b;
}
43
Addition on the Device: main()
int main(void) {
int a, b, c; // host copies of a, b, c
int *d_a, *d_b, *d_c; // device copies of a, b, c
int size = sizeof(int);
// Allocate space for device copies of a, b, c
cudaMalloc((void **)&d_a, size);
cudaMalloc((void **)&d_b, size);
cudaMalloc((void **)&d_c, size);
// Setup input values
a = 2;
b = 7;
44
We’re getting ready to do this…
45
PCI Bus
Addition on the Device: main()
// Copy inputs to device
cudaMemcpy(d_a, &a, size, cudaMemcpyHostToDevice);
cudaMemcpy(d_b, &b, size, cudaMemcpyHostToDevice);
// Launch add() kernel on GPU
add<<<1,1>>>(d_a, d_b, d_c);
// Copy result back to host
cudaMemcpy(&c, d_c, size, cudaMemcpyDeviceToHost);
// Cleanup
cudaFree(d_a); cudaFree(d_b); cudaFree(d_c);
return 0;
}
46
Next: Vector Addition on the Device
__global__ void add(int *a, int *b, int *c) {
c[blockIdx.x] = a[blockIdx.x] + b[blockIdx.x];
}
47
Spoiler: blockIdx.y is also a thing (and probably blockIdx.z)
Vector Addition on the Device
__global__ void add(int *a, int *b, int *c) {
c[blockIdx.x] = a[blockIdx.x] + b[blockIdx.x];
}
48
c[0] = a[0] + b[0];
c[1] = a[1] + b[1];
c[2] = a[2] + b[2];
c[3] = a[3] + b[3];
Block 0
Block 1
Block 2
Block 3
Vector Addition on the Device: add()
__global__ void add(int *a, int *b, int *c) {
c[blockIdx.x] = a[blockIdx.x] + b[blockIdx.x];
}
49
Vector Addition on the Device: main()
50
#define N 512
int main(void) {
int *a, *b, *c; // host copies of a, b, c
int *d_a, *d_b, *d_c; // device copies of a, b, c
int size = N * sizeof(int);
// Alloc space for device copies of a, b, c
cudaMalloc((void **)&d_a, size);
cudaMalloc((void **)&d_b, size);
cudaMalloc((void **)&d_c, size);
// Alloc space for host copies of a, b, c and setup input values
a = (int *)malloc(size); random_ints(a, N);
b = (int *)malloc(size); random_ints(b, N);
c = (int *)malloc(size);
Vector Addition on the Device: main()
51
// Copy inputs to device
cudaMemcpy(d_a, a, size, cudaMemcpyHostToDevice);
cudaMemcpy(d_b, b, size, cudaMemcpyHostToDevice);
// Launch add() kernel on GPU with N blocks
add<<<N,1>>>(d_a, d_b, d_c);
// Copy result back to host
cudaMemcpy(c, d_c, size, cudaMemcpyDeviceToHost);
// Cleanup
free(a); free(b); free(c);
cudaFree(d_a); cudaFree(d_b); cudaFree(d_c);
return 0;
}
a little more magic…
Coordinating Host & Device
52
cudaMemcpy() | Blocks the CPU until the copy is complete Copy begins when all preceding CUDA calls have completed |
cudaMemcpyAsync() | Asynchronous, does not block the CPU |
cudaDeviceSynchronize() | Blocks the CPU until all preceding CUDA calls have completed |
ML PLATFORMS AND GPUS
53
Some sample code
54
Demo
55
MORE DETAILS ON GPU PROGRAMMING
56
Threads and Cores in GPUs
57
58
Simplified summary of GPUs vs CPUs
Blocks, Grids, Threads, Warps
59
Comparison
60
SIMT: Single Instruction Multiple Threads
61
A thread can access its own block id and also thread id. Blocks and threads are in a grid, which is 2D or 3D (there’s a .x and a .y part)
What’s in a GPU?
62
Threads (SIMT, synchronous threads) are grouped into cores (which are decoupled, like a MIMD machine)
IDs and Dimensions
63
Device
Grid 1
Block�(0,0,0)
Block�(1,0,0)
Block�(2,0,0)
Block�(1,1,0)
Block�(2,1,0)
Block�(0,1,0)
Block (1,1,0)
Thread�(0,0,0) | Thread�(1,0,0) | Thread�(2,0,0) | Thread�(3,0,0) | Thread�(4,0,0) |
Thread�(0,1,0) | Thread�(1,1,0) | Thread�(2,1,0) | Thread�(3,1,0) | Thread�(4,1,0) |
Thread�(0,2,0) | Thread�(1,2,0) | Thread�(2,2,0) | Thread�(3,2,0) | Thread�(4,2,0) |
Other kinds of accelerators
64
GPUs vs NPUs and TPUs
65
Memory types
66
Memory Hierarchy in GPUs
67
Memory Hierarchy Inside A GPU
History of Google’s TPU’s
Bigger models need more memory
Will discuss HBM vs on-chip memory later