How to Optimize a CUDA Matmul Kernel for CuBLAS-Like Performance: A Worklog
siboehm.com
siboehm.com
It is interesting to see that the author got this far without interchanging the innermost loop over k to the outermost loop, as is done in CUTLASS (https://github.com/NVIDIA/cutlass).
As you can see in this blog post the code ends up with a lot of compile-time constants (e.g. BLOCKSIZE, BM, BN, BK, TM, TN) one way to optimize this code further is to use an auto-tuner to find the optimal value for all of these parameters for your GPU and problem size, for example Kernel Tuner (https://github.com/KernelTuner/kernel_tuner)
You don’t actually need 3 tiles in groupshared memory.
With some care, it’s possible to go with 2 tiles there. One tile is loaded from the first source matrices. Another tile is accumulator for the output matrix tile. And then in the main function of the compute shader load values for the second source matrix from the global memory, and update all affected elements of the output tile with these mad() instructions.
Shader model 5.0 limits amount of group shared memory to 32kb, and that streaming trick allows to push to the limit, with 64x64 tiles. I think I got about 15% performance win from that trick on some of the GPUs I’m testing my software.
__shared__ float As[(CHUNKSIZE+1) * CHUNKSIZE];
If CHUNKSIZE is a power of 2 this will eliminate bank conflicts.In general I'm still confused about whether vectorized load instructions (LDS.128) necessarily lead to bank conflicts or not. My impression was that consecutive 32b floats get mapped to different banks, so to avoid conflicts I'd want the warp to load 32*32b consecutive elements at each step.
__shared__ float As[(CHUNKSIZE+4) \* CHUNKSIZE]
Ultimately it's down to trial and error, like always with GPGPU.I'm also curious whether warp shuffle operations might be useful to reduce some of the shared memory traffic.
[1]: https://github.com/webonnx/wonnx/blob/master/wonnx/templates...
Bram wrote some interesting exploration here: https://jott.live/markdown/webgpu_safari
Albeit, much of the boilerplate was in place. Students were tasked with maximizing bandwidth via the optimization techniques mentioned in the article and other literature.
>> Profiling our naive kernel
I would highly recommend trying out NVIDIA NSight Systems to help profiling: it gives a lot of information while optimizing for CUDA (together with info on CPU, NVTX markers, Vulkan,OpenGL etc).