GPU: Use cases
Also known as: gpu examples image processing audio simulation mandelbrot particles finance compute shader cuda parallel
● Advanced6 min
Work that does the same thing to many items, with enough math in each, is GPU work: images, audio, maps, finance, science and simulation.
Images: gray from color
Each pixel's brightness, from its red, green and blue, with the weights screens use. The same shape works for brightness, contrast and gamma.
type Rgb {
r: Float32
g: Float32
b: Float32
}
gpu fn gray(r: Float32, g: Float32, b: Float32) -> Float32 {
0.2126 * r + 0.7152 * g + 0.0722 * b
}
fn main() {
const colors = [Rgb(r: 1.0, g: 0.0, b: 0.0), Rgb(r: 0.0, g: 1.0, b: 0.0), Rgb(r: 1.0, g: 1.0, b: 1.0)]
print(colors.map(c => gray(c.r, c.g, c.b)))
}The program ran and printed:
kavach run gray.kav
[0.2126, 0.7152, 1.0]Audio: louder without clipping
Every sample made louder, then clamped to -1 and 1 so it never distorts.
gpu fn limit(sample: Float32, gain: Float32) -> Float32 {
(sample * gain).clamp(-1.0, 1.0)
}
fn main() {
const audio: [Float32] = [0.1, -0.3, 0.6, -0.9]
print(audio.map(s => limit(s, 2.0)))
}The program ran and printed:
kavach run clamp.kav
[0.2, -0.6, 1.0, -1.0]Maps: distance to every shop
The distance from home to many points, with math.sqrt: the first step of "nearest" searches.
use std.math
type Point {
x: Float32
y: Float32
}
gpu fn distance(x: Float32, y: Float32, cx: Float32, cy: Float32) -> Float32 {
const dx = x - cx
const dy = y - cy
math.sqrt(dx * dx + dy * dy)
}
fn main() {
const shops = [Point(x: 3.0, y: 4.0), Point(x: 6.0, y: 8.0), Point(x: 0.0, y: 1.0)]
const home_x: Float32 = 0.0
const home_y: Float32 = 0.0
print(shops.map(s => distance(s.x, s.y, home_x, home_y)))
}The program ran and printed:
kavach run distance.kav
[5.0, 10.0, 1.0]Finance: savings in ten years
Compound growth for many accounts at once, a loop per item.
gpu fn grow(balance: Float32, rate: Float32, years: Int32) -> Float32 {
let b = balance
for _ in 0..years {
b = b * (1.0 + rate)
}
b
}
fn main() {
const balances: [Float32] = [1000.0, 2500.0, 10000.0]
const rate: Float32 = 0.07
print(balances.map(b => grow(b, rate, 10)))
}The program ran and printed:
kavach run interest.kav
[1967.1522, 4917.882, 19671.527]Science: the Mandelbrot set
Heavy work per point: up to 50 rounds each, the kind of job GPUs are built for. Each # is a point that never escaped.
type Point {
x: Float32
y: Float32
}
gpu fn escape(cx: Float32, cy: Float32) -> Int32 {
let x: Float32 = 0.0
let y: Float32 = 0.0
let n = 0
while n < 50 and x * x + y * y <= 4.0 {
const t = x * x - y * y + cx
y = 2.0 * x * y + cy
x = t
n += 1
}
n
}
fn main() {
let points: [Point] = []
for row in 0..12 {
for col in 0..40 {
points.push(Point(x: -2.2 + Float32(col) * 0.075, y: -1.1 + Float32(row) * 0.2))
}
}
const counts = points.map(p => escape(p.x, p.y))
for row in 0..12 {
let line = ""
for col in 0..40 {
line += if counts[row * 40 + col] == 50 { "#" } else { "." }
}
print(line)
}
}The program ran and printed:
kavach run mandelbrot.kav
...........................................................................................................##.................................############...........................##############..................######################..................######################..........................##############...........................############.................................##...........................................................................................
Simulation: moving particles
Each particle a record, stepped forward in time.
use std.collections
type Particle {
x: Float32
v: Float32
}
gpu fn step(x: Float32, v: Float32) -> Float32 {
x + v * 0.5
}
gpu fn add(a: Float32, b: Float32) -> Float32 {
a + b
}
fn main() {
const particles = [Particle(x: 0.0, v: 2.0), Particle(x: 10.0, v: -4.0)]
print(particles.map(p => step(p.x, p.v)))
const xs: [Float32] = [1.0, 2.0, 3.0]
const ys: [Float32] = [10.0, 20.0, 30.0]
print(collections.zip(xs, ys).map(t => add(t.0, t.1)))
}The program ran and printed:
kavach run records.kav
[1.0, 8.0][11.0, 22.0, 33.0]
When not to use the GPU
- Light work: adding 1 to each number is faster on the CPU than the trip to the GPU.
- Text, maps and files: a GPU works on numbers only.
- Sorting and float totals: today these run on all CPU cores, which is already fast; see Loops on All Cores. Whole-number
sum,minandmaxof a list already on the GPU run there.
These lists are short so the output fits on a page; with the same code, long lists run on the GPU.