Fitting Into cuBLASDx Idioms#
Note
This chapter is part of the agent-targeted Agent BLAS Performance Guide and is also published as Markdown for
agent consumption, in the markdown/ folder of the HTML documentation tree.
Find every row below that is true of your kernel — expect several; the axes are independent, and a solution is
a choice on each one. Each idiom has one directory of measurable variants under
example/cublasdx/performance_guide/; each variant’s header comment states what it isolates.
Idiom Catalogue#
# |
If your kernel… |
Example directory |
|---|---|---|
01 |
holds |
|
02 |
writes results in a different type than it accumulates in |
|
03 |
applies a pure function of each input element’s own value before multiplying |
|
04 |
preprocesses in a way that needs other elements — row normalization, transpose, FFT — so threads must cooperate |
|
05 |
combines an input element with a second tensor before the multiply |
|
06 |
as 05, but the transform over the external data itself needs cross-thread cooperation |
|
07 |
post-processes each output element using its own global position — mask, bias, positional scaling |
|
08 |
post-processes needing the whole row, column or tile — max, sum, normalization |
|
09 |
as 08, but the epilogue also reads a separate tensor (e.g. a statistic carried between tiles) |
|
10 |
loads |
|
11 |
multiplies a matrix by its own transpose ( |
|
12 |
multiplies a matrix by itself ( |
|
13 |
feeds one GEMM’s result into another without a round trip through global memory |
|
14 |
has an operand computed rather than stored — generated from its indices, alone or scaled by a small side vector |
|
15 |
accumulates several GEMMs into one output before a single epilogue |
|
16 |
calls the GEMM for every pair of a slice-pair loop nest — products of sliced operands with per-slice scale factors accumulated into one result, as in Ozaki-scheme emulation; the operand of the outer loop can stay staged across the inner loop, and the factorized scales are applied while staging |
|
17 |
runs many small problems, each with its own leading dimension read from an array |
|
18 |
runs problems of differing shapes or precisions in one kernel |
|
19 |
needs accumulation more accurate than plain fp32 — a separate fp32 register accumulator, periodic fp64 promotion, per-K-tile fp64 promotion, scored against an fp64 ground truth |
|
Idioms Feed Back Into The Descriptor#
Several rows are choices about operators, so expect one pass back through Finding Problem Shape:
Idiom |
What it changes in the descriptor |
|---|---|
01, 02 |
|
10 |
On Hopper the
any gate missing silently leaves the |
11, 12 |
|
17 |
A runtime leading dimension passed to |
18 |
Several descriptors, selected at run time. |
Expressing It The Library’s Way#
Tensors, not pointers. The tensor
execute(...)overloads give you custom layouts, suggested layouts and the register accumulator. Build inputs withcublasdx::make_tensorand a layout from the descriptor.Layouts and accumulators pair up.
suggest_layout_smem_*()= swizzled from the tuning database,get_layout_smem_*()= plain; the accumulator must match (mispairing: compile error one way, silent slowdown the other). Theexecute(a, b)overload that returns an accumulator picks the matching one — use it.Copy with the library, then wait.
cublasdx::copy/cublasdx::copy_fragmentpick vectorization and the async path;cublasdx::copy_wait()afterwards is required (__syncthreads()is not sufficient onSM >= 800). Copy width follows alignment.cublasdx::copymoves bytes only — a transform goes in a load op into the MMA, a shared-to-shared pass, or a hand-written load-transform-store loop; choosing among the three is idiom 03.Custom-layout operands keep their smem tag —
cute::make_tensor(cute::make_smem_ptr(ptr), layout). With a rawcute::Layoutthe failure is a template error atexecute()that names neither tensor nor layout.
Epilogues#
A second kernel re-reads the output from global memory — for a memory-bound epilogue that costs about as much
as the GEMM. Act in registers instead: execute(a, b) returns the accumulator, which carries:
Method |
Use it when |
|---|---|
|
Each output element is a pure function of itself. |
|
The result depends on position or a second tensor; the lambda gets the accumulator element, the output
element and the |
|
|
|
No transform — write the fragment out. |
Register output pays whenever the epilogue is elementwise or a reduction that can start per-fragment; it stops paying when the epilogue needs values a thread does not own (transpose, ordered scan, anything non-commutative) — those force the tile through shared memory anyway, so store first and work there. That split is idiom 08’s two main variants.