Aula 26: Multiplicação de Matrizes — o Kernel Ingénuo na GPU
A multiplicação de matrizes C = A * B calcula cada elemento C[row][col] como uma soma de produtos ao longo da dimensão partilhada K: C[row][col] = soma sobre k de A[row][k] * B[k][col]. O jeito direto (ingénuo) de paralelizar isso numa GPU é atribuir uma thread a cada elemento de C. A thread calcula
Imagine uma grade gigante de multiplicação para preencher. Na versão ingénua, cada trabalhador é responsável por apenas uma célula, mas para preenchê-la ele caminha até um depósito distante e busca uma linha inteira e uma coluna inteira — e o trabalhador ao lado dele caminha até o mesmo depósito e busca exatamente a mesma linha de novo. Muitas viagens desperdiçadas.
- mapeamento de thread para elemento
- Cada thread é dona de um elemento de C. Ela calcula row e col a partir dos índices 2D e produz C[row][col].
- o loop de k (produto escalar)
- O loop sobre k que acumula sum += A[row*K + k] * B[k*N + col]. É um produto escalar entre uma linha de A e uma coluna de B.
- índice plano row-major
- Uma matriz rows x cols é guardada linha após linha, então o elemento [r][c] fica no endereço r*cols + c.
- leituras globais redundantes
- Na versão ingénua, cada elemento de A e de B é lido da global memory repetidas vezes por threads diferentes, queimando largura de banda.