Each kernel is one point per memory level. Placement needs its operation count and its byte count at the named boundary.
The three kernels below share the assumed platform: 100 GFLOP/s peak, 20 GB/s sustained DRAM bandwidth. Each cap is min(100, AI x 20).
Kernel | AI at DRAM (FLOP/B) | Cap (GFLOP/s) | Binding roof |
|---|---|---|---|
Single-precision dot product | 0.25 | 5 | Memory |
7-point stencil, FP64 with reuse | 0.81 | 16 | Memory |
Tiled dense GEMM, FP32 | 12 | 100 | Compute |
Dot product (FP32, N elements; counts N-1 adds as N, exact for large N):
ops = 2N FLOP, bytes = 8N B -> AI = 2N / 8N = 0.25 FLOP/B
cap = min(100, 0.25 x 20 = 5) = 5 GFLOP/s
7-point stencil (FP64, 7 mul + 6 add = 13 FLOP per point):
bytes per point ~ 16 B (one 8 B read, one 8 B write after reuse)
AI = 13 / 16 = 0.8125 ~= 0.81 FLOP/B
cap = min(100, 0.8125 x 20 = 16.25) ~= 16 GFLOP/s
Tiled GEMM (FP32, N = 512; assumed counted traffic ~22.4 MB):
ops = 2 x 512^3 = 268,435,456 FLOP
AI = 268,435,456 / 22,400,000 ~= 11.98 ~= 12 FLOP/B
cap = min(100, 12 x 20 = 240) = 100 GFLOP/sEach hierarchy level has its own roof. One kernel lands differently against each, and the lowest cap binds end to end.
Level | Sustained BW | Kernel AI here (FLOP/B) | Cap (GFLOP/s) |
|---|---|---|---|
L1 | 1000 GB/s | 2 | 100 |
LLC | 200 GB/s | 1 | 100 |
DRAM | 20 GB/s | 0.5 | 10 |
The L1-local view says compute-bound at 2 x 1000 = 2000, capped to 100. The DRAM view says 0.5 x 20 = 10, so the kernel is memory-bound in production.
The model below computes the bound for any stated peak, bandwidth and intensity. Its testbench replays every cap used on this page.
module tb_roofline;
function real attainable(input real peak, input real bw, input real ai);
real mem;
mem = ai * bw;
if (mem < peak) attainable = mem;
else attainable = peak;
endfunction
real got;
real want;
real diff;
int errors;
task check(input real peak, input real bw, input real ai, input real want_in);
want = want_in;
got = attainable(peak, bw, ai);
diff = got - want;
if (diff < 0.0) diff = -diff;
if (diff > 0.000001) errors = errors + 1;
endtask
initial begin
errors = 0;
check(100.0, 20.0, 0.5, 10.0);
check(100.0, 20.0, 5.0, 100.0);
check(100.0, 20.0, 20.0, 100.0);
check(100.0, 20.0, 0.25, 5.0);
check(100.0, 20.0, 0.8125, 16.25);
check(100.0, 20.0, 12.0, 100.0);
if (errors == 0) $display("PASS: roofline caps 10 100 100 5 16.25 100 GFLOP/s all correct");
else $display("FAIL: %0d roofline checks wrong", errors);
$finish;
end
endmoduleThe classic error is one flat bandwidth number for all kernels. Intensities differ by level, so a single slope misplaces every kernel with reuse.
The DRAM placement. Its cap is 10 GFLOP/s against 100 at L1 and LLC, and the lowest cap binds.
Intensity halves to 13 / 32 = 0.40625. The cap falls to 0.40625 x 20 = 8.125 GFLOP/s, still memory-bound.