The Modern CUDA Toolbox in Practice: A Step-by-Step Optimization Walkthrough NVIDIA published a step-by-step tutorial demonstrating how to debug, benchmark, and optimize CUDA code using modern tools such as Compute Sanitizer, Nsight Systems, NVTX, CUB, and CCCL, applied to an RGB-to-grayscale image processing pipeline with median filtering. The tutorial covers six incremental improvements, including adopting the CCCL API, using CUB's optimized algorithms, managing GPU memory with pooled containers, and parallelizing work with per-thread streams, with code available on GitHub and Google Colab. NVIDIA CUDA remains the foundation of GPU-accelerated computing, powering everything from scientific simulations to large-scale AI training. But writing correct, maintainable, and performant CUDA code can be challenging: memory bugs hide in plain sight, performance bottlenecks can be invisible without the right instrumentation, and hand-rolled GPU algorithms rarely match the efficiency of optimized libraries. Fortunately, the modern CUDA toolchain has matured significantly, and many of these challenges now have straightforward solutions. In this blog post, we will walk through the tools NVIDIA offers to debug, benchmark, and improve your code. With only small line changes each time, we are going to make the example code safer, easier to maintain, and faster. Across six incremental steps, this post will cover: - How to easily find indexing bugs by adopting the modern CCCL API and Compute Sanitizer - How to improve Nsight Systems benchmarks with NVTX - How to use CUB’s optimized algorithms at the block and device level - How to manage GPU memory through pooled containers - How to speed up host-to-device transfers with pinned containers - How to parallelize GPU work by giving each thread its own stream and asynchronous transfers As a companion to this blog post, we provide the code https://github.com/NVIDIA/accelerated-computing-hub/tree/main/resources/blogs/modern cuda cpp blogpost/code steps and the option to run on Google Colab https://colab.research.google.com/github/NVIDIA/accelerated-computing-hub/blob/main/resources/blogs/modern cuda cpp blogpost/code steps/modern cuda toolbox tutorial.ipynb . Starting point: An image processing pipeline example From an input stream of red, green, and blue images, start by transferring the data from the CPU to the GPU. Then convert those from RGB to grayscale. Then, for each 32 by 32 pixel tile in the image, compute the median by sorting the pixels and selecting the middle value. Finally, copy the median of each tile back to the CPU. The base code example Below is the full starting code. Each step in this post improves on it. define CUDA CHECK ERROR call do { \ cudaError t err = call; \ if err = cudaSuccess { \ std::cerr << "CUDA error in " << FILE << " at line " << LINE << ": " \ << cudaGetErrorString err << std::endl; \ std::exit EXIT FAILURE ; \ } \ } while 0 // Alias for an image pixel using pixel t = uint8 t; // Kernel converting the red, green and blue images into a single gray image global void computeRGBToGray const pixel t d image r, const pixel t d image g, const pixel t d image b, pixel t d image gray, int width, int height { // Compute the thread global index in the grid const int x = threadIdx.x + blockIdx.x blockDim.x; const int y = threadIdx.y + blockIdx.y blockDim.y; // Boundary check selecting only threads within the image boundary if x < width && y < height { // Compute the thread index in the image const int i = x + y width; // Convert from rgb to grayscale and store the result in global memory d image gray i = static cast