C.W.K.
Stream
Lesson 05 of 05 · published

Matrix Ops CLI — Seven Personalities

~12 min · matrices, cli, benchmark, tooling

Level 0Beginner
0 XP0/38 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete

One executable, every matrix flavor — your lab bench

You now have enough kernels to build a single command-line tool that lets you benchmark any matrix flavor with any size. Call it matrix_ops. Subcommands:

SubcommandArgsWhat it runs
add / sub / mul / div<ROWS> <COLS>Element-wise op (Track 5 with 2-D indexing)
gemv<ROWS> <COLS>BLAS-2: y = A·x
gemm-naive<M> <N> <K>One thread per output element (Lesson 4)
gemm<M> <N> <K>Tiled, with shared memory + register blocking

Why build this? Because comparing yourself against vendor BLAS (Track 7) requires a stable test rig you trust. Letting the CLI accept dimensions means you can sweep sizes without recompiling.

Real numbers on RTX 4090 (FP32):

OpSizeBW or Perf
add4096×4096167 GB/s
gemv8192×8192216 GB/s
gemm-naive512³0.41 TF/s
gemm-tiled2048³3.8 TF/s

The 9× jump from naive to tiled is exactly what we predicted: each 16 KB shared-memory tile feeds 128× more FMAs before re-hitting DRAM.

Code

matrix_ops main() — argv dispatch·cuda
// matrix_ops.cu — single executable that runs any of our kernels
#include <cstdio>
#include <cstdlib>
#include <string>
// (kernel implementations omitted; assume gemm_naive, gemm_tiled, etc.)

int main(int argc, char** argv) {
    if (argc < 2) {
        fprintf(stderr,
            "Usage: matrix_ops <op> [args...]\n"
            "  add|sub|mul|div  <rows> <cols>\n"
            "  gemv             <rows> <cols>\n"
            "  gemm-naive       <M> <N> <K>\n"
            "  gemm             <M> <N> <K>\n");
        return 1;
    }
    std::string op = argv[1];
    if      (op == "add")        return run_add(argc, argv);
    else if (op == "gemm")       return run_gemm_tiled(argc, argv);
    else if (op == "gemm-naive") return run_gemm_naive(argc, argv);
    else if (op == "gemv")       return run_gemv(argc, argv);
    // ...
    fprintf(stderr, "Unknown op: %s\n", op.c_str());
    return 1;
}
Sweep sizes from a shell — generate your own roofline·bash
# Sweep gemm sizes and capture timing → CSV for plotting
echo 'M,N,K,tflops' > sweep.csv
for n in 256 512 1024 2048 4096; do
    out=$(./matrix_ops gemm $n $n $n)
    tflops=$(echo "$out" | awk '/TFLOP/ {print $NF}')
    echo "$n,$n,$n,$tflops" >> sweep.csv
done
cat sweep.csv
# Plot in Python: pd.read_csv('sweep.csv').plot(x='M', y='tflops', ...)

External links

Exercise

Build a minimal matrix_ops with at least three subcommands (add, gemm-naive, gemm) on whichever GPU you have. Run a sweep over sizes 256, 512, 1024, 2048, 4096 for the gemm subcommand. Plot TFLOP/s vs size. The naive version's curve will plateau early (bandwidth-bound regardless of size); the tiled version's curve will keep climbing until you hit Tensor Core territory (which we cover in Track 8).

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.