warp reductions can be used. This follows our observation in Section \[subsec:reg\_perf\] to assign as few threads as possible to process column pairs, frees up valuable resources and increases the overall performance of the reduction. Row padding is used to keep the rows at multiples of the warp size, and column padding is used to keep the number of columns even. Kernels can then be launched using $32\times n/2$ threads to process each matrix. Figures \[fig:shared\_alloc\] and \[fig:shared\_warp\_reduction\] show examples of the thread allocation and reductions for a $8 \times 8$ matrix using a theoretical warp size of 4.

![Distribution of column pairs to warps at each step of a sweep.[]{data-label="fig:round_robin"}](pair_generation.pdf){width="0.6\linewidth"}

[0.3]{} ![Shared memory kernel implementation details.](shared_alloc.pdf "fig:"){width="\linewidth"}

[0.4]{} ![Shared memory kernel implementation details.](shared_warp_reduction.pdf "fig:"){width="\linewidth"}

Performance {#performance-1}
-----------

Figures \[fig:shared\_svd\_perf\] and \[fig:shared\_svd\_occupancy\] show the performance of the parallel shared SVD kernel compared to the serial register SVD kernel on a P100 GPU. We can see the improved growth in performance in the shared memory kernel due to the greater occupancy as well as the absence of any local memory transactions. Looking at the double precision occupancy, we notice two dips in occupancy at matrix sizes 22 and 32 as the 