Deep Implementation

Custom elementwise kernel via Triton

My first real Triton kernel. The moment the roofline became something I could touch, and why elementwise ops are the perfect first kernel.

The roofline has been my compass for a while. Today I finally wrote a kernel that sits on it, and the chart stopped being abstract.

I wrote my first Triton kernel. Not a fancy one, an elementwise add, but it's mine, and it taught me more about the memory hierarchy than a month of reading.

Why Triton, why elementwise

Writing raw CUDA is a slog: grid, block, thread indexing, shared memory, synchronization, and an hour of debugging for a kernel that does one addition. Triton abstracts the thread-level bookkeeping and lets you write in a Python-like dialect. The compiler figures out the grid, the block, the vectorization, the memory coalescing. You focus on the algorithm.

And elementwise is the perfect first kernel because it's purely memory-bound. It does one FLOP per byte moved. It's the roofline's purest case: no tiling, no shared memory, just "read, compute, write".

The kernel

import triton
import triton.language as tl

@triton.jit
def add_kernel(x_ptr, y_ptr, out_ptr, n_elements, BLOCK: tl.constexpr):
    pid = tl.program_id(axis=0)
    offsets = pid * BLOCK + tl.arange(0, BLOCK)
    mask = offsets < n_elements
    x = tl.load(x_ptr + offsets, mask=mask)
    y = tl.load(y_ptr + offsets, mask=mask)
    tl.store(out_ptr + offsets, x + y, mask=mask)

# launch
grid = (triton.cdiv(n, BLOCK),)
add_kernel[grid](x, y, out, n, BLOCK=1024)

That's it. Four lines of actual logic. The compiler turns it into a CUDA kernel with coalesced memory access, vectorized loads, and no wasted threads.

What I learned

Mental model

Writing a Triton kernel is like driving a stick shift for the first time after years of automatics. You feel the gears, you stall a few times, and then you realize you've been driving with the handbrake on the whole time.

Why this matters for inference

Real inference workloads are full of elementwise-adjacent ops: activation functions, scaling, quantization, RoPE. When you need a custom op that PyTorch doesn't have, or the fused version isn't fast enough, Triton is how you write it without a CUDA compiler and a week of your life.

And once you can write a kernel, the serving engines stop being magic. When vLLM or SGLang is slow, you can actually look at what it's doing and ask the right question.

The takeaway

The roofline isn't a theory. It's a measurement, and now I've taken it with my own hands.

Tomorrow: a PyTorch custom op with a real CUDA backend, one step deeper.