each other’s registers. Figure \[fig:register\_storage\_reduction\] shows the data layout for a theoretical warp of size 8 with 4 columns in registers and a warp reduction using shuffles. Once we factor the panel, we can apply the reflectors to the trailing sub-matrix in a separate kernel that is optimized for performing the core matrix-vector product in the update. In this second kernel, we load both the factored panel $P$ and a panel $M_i$ of the trailing sub-matrix $M$ to registers and apply the reflectors one at a time, updating the trailing panel in registers. Let us take an example of a $32 \times 8$ trailing panel $M_i$. For each reflector, we compute the matrix-vector product $M_i^Tv$ by flattening the $32 \times 8$ product into a reduction of a 256 vector in shared memory that has been padded to avoid bank conflicts. The reduction can then be serialized until it reaches a size of 32, where a partial reduction to a vector of size 8 can take place in 2 steps. This final vector is the product $M_i^Tv$ which can then be quickly applied to the registers storing $M_i$. This process is repeated for each trailing panel within the same kernel to 