Multiplicação de matrizes em cuda

Post on 26-Jun-2015

3.111 views 1 download

description

Exemplos de Multiplicação de Matrizes em CUDA.

Transcript of Multiplicação de matrizes em cuda

Multiplicação de Matrizes em CUDA

Divino César SoaresPontifícia Universidade Católica de Goiás (CMP/PUC-GO)

O Problema

• Duas matrizes de entrada: A e B. Que são quadradas e possuem os mesmos valores para suas dimensões: LARGURA x LARGURA.

• Gerar uma matriz resultado C com as mesmas dimensões das matrizes A e B.

• Cada elemento (i, j) da matriz C é o produto (interno) da linha i de A pela coluna j de B.

• Para cada elemento (i, j) de C:

for (k=1; k<=LARGURA; k++)C[i][j] += (A[i][k] * B[k][j]);

Implementação Sequencialvoid multiplica(int *A[], int *B[], int *C[]) {

for (int i=1; i<=LARGURA; i++) {for (int j=1; j<=LARGURA; j++) {

for (int k=1; k<=LARGURA; k++) {C[i][j] += (A[i][k] * B[k][j]);

}}

}}

7 4 3 0

3 9 6 1

7 8 1 5

2 3 4 6

3 4 3 9

5 6 8 7

7 8 4 2

1 6 8 4

21 0 0 0

0 0 0 0

0 0 0 0

0 0 0 0

• Variáveis:L = 4

i = 1j = 1k = 1

1 2 3 4

1

2

3

4

Implementação Sequencialvoid multiplica(int *A[], int *B[], int *C[]) {

for (int i=1; i<=LARGURA; i++) {for (int j=1; j<=LARGURA; j++) {

for (int k=1; k<=LARGURA; k++) {C[i][j] += (A[i][k] * B[k][j]);

}}

}}

7 4 3 0

3 9 6 1

7 8 1 5

2 3 4 6

3 4 3 9

5 6 8 7

7 8 4 2

1 6 8 4

33 0 0 0

0 0 0 0

0 0 0 0

0 0 0 0

• Variáveis:L = 4

i = 1j = 1k = 2

1 2 3 4

1

2

3

4

Implementação Sequencialvoid multiplica(int *A[], int *B[], int *C[]) {

for (int i=1; i<=LARGURA; i++) {for (int j=1; j<=LARGURA; j++) {

for (int k=1; k<=LARGURA; k++) {C[i][j] += (A[i][k] * B[k][j]);

}}

}}

7 4 3 0

3 9 6 1

7 8 1 5

2 3 4 6

3 4 3 9

5 6 8 7

7 8 4 2

1 6 8 4

54 0 0 0

0 0 0 0

0 0 0 0

0 0 0 0

• Variáveis:L = 4

i = 1j = 1k = 3

1 2 3 4

1

2

3

4

Implementação Sequencialvoid multiplica(int *A[], int *B[], int *C[]) {

for (int i=1; i<=LARGURA; i++) {for (int j=1; j<=LARGURA; j++) {

for (int k=1; k<=LARGURA; k++) {C[i][j] += (A[i][k] * B[k][j]);

}}

}}

7 4 3 0

3 9 6 1

7 8 1 5

2 3 4 6

3 4 3 9

5 6 8 7

7 8 4 2

1 6 8 4

72 0 0 0

0 0 0 0

0 0 0 0

0 0 0 0

• Variáveis:L = 4

i = 1j = 1k = 4

1 2 3 4

1

2

3

4

Implementação Sequencialvoid multiplica(int *A[], int *B[], int *C[]) {

for (int i=1; i<=LARGURA; i++) {for (int j=1; j<=LARGURA; j++) {

for (int k=1; k<=LARGURA; k++) {C[i][j] += (A[i][k] * B[k][j]);

}}

}}

7 4 3 0

3 9 6 1

7 8 1 5

2 3 4 6

3 4 3 9

5 6 8 7

7 8 4 2

1 6 8 4

72 12 0 0

0 0 0 0

0 0 0 0

0 0 0 0

• Variáveis:L = 4

i = 1j = 2k = 1

1 2 3 4

1

2

3

4

Implementação Sequencialvoid multiplica(int *A[], int *B[], int *C[]) {

for (int i=1; i<=LARGURA; i++) {for (int j=1; j<=LARGURA; j++) {

for (int k=1; k<=LARGURA; k++) {C[i][j] += (A[i][k] * B[k][j]);

}}

}}

7 4 3 0

3 9 6 1

7 8 1 5

2 3 4 6

3 4 3 9

5 6 8 7

7 8 4 2

1 6 8 4

72 48 0 0

0 0 0 0

0 0 0 0

0 0 0 0

• Variáveis:L = 4

i = 1j = 2k = 2

1 2 3 4

1

2

3

4

Implementação Sequencialvoid multiplica(int *A[], int *B[], int *C[]) {

for (int i=1; i<=LARGURA; i++) {for (int j=1; j<=LARGURA; j++) {

for (int k=1; k<=LARGURA; k++) {C[i][j] += (A[i][k] * B[k][j]);

}}

}}

7 4 3 0

3 9 6 1

7 8 1 5

2 3 4 6

3 4 3 9

5 6 8 7

7 8 4 2

1 6 8 4

72 72 0 0

0 0 0 0

0 0 0 0

0 0 0 0

• Variáveis:L = 4

i = 1j = 2k = 3

1 2 3 4

1

2

3

4

Implementação Sequencialvoid multiplica(int *A[], int *B[], int *C[]) {

for (int i=1; i<=LARGURA; i++) {for (int j=1; j<=LARGURA; j++) {

for (int k=1; k<=LARGURA; k++) {C[i][j] += (A[i][k] * B[k][j]);

}}

}}

7 4 3 0

3 9 6 1

7 8 1 5

2 3 4 6

3 4 3 9

5 6 8 7

7 8 4 2

1 6 8 4

72 99 0 0

0 0 0 0

0 0 0 0

0 0 0 0

• Variáveis:L = 4

i = 1j = 2k = 4

1 2 3 4

1

2

3

4

Estrutura da Solução1. Alocar memória na GPU.

2. Copia dados de entrada. Da CPU para a GPU.

3. Configura execução. Número de threads e blocos.

4. Copia resultados.

cudaMalloc((void **)&A_d, size_A);cudaMalloc((void **)&B_d, size_B);cudaMalloc((void **)&C_d, size_C);

cudaMemcpy(A_d, A, size_A, cudaMemcpyHostToDevice);cudaMemcpy(B_d, B, size_B , cudaMemcpyHostToDevice);cudaMemcpy(C_d, C, size_C , cudaMemcpyHostToDevice);

dim3 gride(X, Y)dim3 bloco(Z, W, K)meu_kernel<<<gride, bloco>>>(A, B, C);

cudaMemcpy(C, C_d, size_C , cudaMemcpyDeviceToHost);

Primeira Abordagem

Kernel 1dim3 gride(1, 1)dim3 bloco(4, 4, 1)

dim3 gride(2, 1)dim3 bloco(4, 4, 1)

dim3 gride(1, 1)dim3 bloco(30, 30, 1)

Gride

0,0 0,1 0,2 0,3

1,0 1,1 1,2 1,3

2,0 2,1 2,2 2,3

3,0 3,1 3,2 3,3

Gride Gride

<< Launch error >>>

Bloco com 600 threadsBloco 0

Bloco 0 Bloco 1

0,0 0,1 0,2 0,3

1,0 1,1 1,2 1,3

2,0 2,1 2,2 2,3

3,0 3,1 3,2 3,3

0,0 0,1 0,2 0,3

1,0 1,1 1,2 1,3

2,0 2,1 2,2 2,3

3,0 3,1 3,2 3,3

Kernel 1dim3 gride(1, 1)dim3 bloco(LARGURA, LARGURA, 1)

Gride

0,0 0,1 0,2 0,3

1,0 1,1 1,2 1,3

2,0 2,1 2,2 2,3

3,0 3,1 3,2 3,3

Bloco 0

LARGURA

LARGU

RA

Kernel 1dim3 gride(1, 1)dim3 bloco(LARGURA, LARGURA, 1)

Gride

0,0 0,1 0,2 0,3

1,0 1,1 1,2 1,3

2,0 2,1 2,2 2,3

3,0 3,1 3,2 3,3

Bloco 0

LARGURA

LARGU

RA

__global__ void mulGpu(int *A[], int *B[], int *C[]) {int i = threadIdx.x;int j = threadIdx.y;

for (int k=1; k<=LARGURA; k++) {C[i][j] += (A[i][k] * B[k][j]);

}}

Kernel 1: Multiplicação na GPU

Kernel 1

__global__ void mulGpu(int *A[], int *B[], int *C[]) {int i = threadIdx.x;int j = threadIdx.y;

for (int k=1; k<=LARGURA; k++) {C[i][j] += (A[i][k] * B[k][j]);

}}

Kernel 1: Multiplicação na GPU

7 4 3 0

3 9 6 1

7 8 1 5

2 3 4 6

3 4 3 9

5 6 8 7

7 8 4 2

1 6 8 4

0 0 0 0

0 0 0 0

0 0 0 0

0 0 0 0

1 2 3 4

1

2

3

4

Instante de tempo t=0

Kernel 1

__global__ void mulGpu(int *A[], int *B[], int *C[]) {int i = threadIdx.x;int j = threadIdx.y;

for (int k=1; k<=LARGURA; k++) {C[i][j] += (A[i][k] * B[k][j]);

}}

Kernel 1: Multiplicação na GPU

7 4 3 0

3 9 6 1

7 8 1 5

2 3 4 6

3 4 3 9

5 6 8 7

7 8 4 2

1 6 8 4

21 12 9 0

35 20 15 0

49 28 21 0

7 4 3 0

1 2 3 4

1

2

3

4

Instante de tempo t=1

Kernel 1

__global__ void mulGpu(int *A[], int *B[], int *C[]) {int i = threadIdx.x;int j = threadIdx.y;

for (int k=1; k<=LARGURA; k++) {C[i][j] += (A[i][k] * B[k][j]);

}}

Kernel 1: Multiplicação na GPU

7 4 3 0

3 9 6 1

7 8 1 5

2 3 4 6

3 4 3 9

5 6 8 7

7 8 4 2

1 6 8 4

12 36 24 4

18 54 36 6

24 72 48 8

18 54 36 42

1 2 3 4

1

2

3

4

Instante de tempo t=2

Kernel 1

__global__ void mulGpu(int *A[], int *B[], int *C[]) {int i = threadIdx.x;int j = threadIdx.y;

for (int k=1; k<=LARGURA; k++) {C[i][j] += (A[i][k] * B[k][j]);

}}

Kernel 1: Multiplicação na GPU

7 4 3 0

3 9 6 1

7 8 1 5

2 3 4 6

3 4 3 9

5 6 8 7

7 8 4 2

1 6 8 4

72 99 72 73

123 159 87 88

105 138 81 40

89 134 63 70

1 2 3 4

1

2

3

4

Instante de tempo t=L

Vantagens/Desvantagens

• Vantagem em relação a sequencial: 1. cada elemento de C é calculado em paralelo.

• Desvantagens desta abordagem:1. Restrição do formato das matrizes. Elas devem ser quadradas.2. Restrição da quantidade de elementos em cada matriz. Menor que 512.3. Usa apenas a memória global da GPU. A memória global apresenta grande latência.4. Apenas um bloco de threads, com poucas threads. Tamanho do maior bloco 22 x 22.5. Os mesmos dados são buscados várias vezes da memória.

Resultado: Subutilização dos recursos da GPU.

Segunda Abordagem

Kernel 2dim3 gride(2, 2)dim3 bloco(15, 15, 1)

dim3 gride(1, 1)dim3 bloco(30, 30, 1)

GrideGride

<< Launch error >>>

Bloco com 600 threads

Bloco 0, 0 Bloco 0, 1

0,0 0,1 0,2 0,3

1,0 1,1 1,2 1,3

2,0 2,1 2,2 2,3

3,0 3,1 3,2 3,3

0,0 0,1 0,2 0,3

1,0 1,1 1,2 1,3

2,0 2,1 2,2 2,3

3,0 3,1 3,2 3,3

Bloco 1, 0 Bloco 1, 1

0,0 0,1 0,2 0,3

1,0 1,1 1,2 1,3

2,0 2,1 2,2 2,3

3,0 3,1 3,2 3,3

0,0 0,1 0,2 0,3

1,0 1,1 1,2 1,3

2,0 2,1 2,2 2,3

3,0 3,1 3,2 3,3

Kernel 2

225 threads por bloco.Total de 900 threads.

dim3 gride(2, 2)dim3 bloco(15, 15, 1)

Gride

Bloco 0, 0 Bloco 0, 1

0,0 0,1 0,2 0,3

1,0 1,1 1,2 1,3

2,0 2,1 2,2 2,3

3,0 3,1 3,2 3,3

0,0 0,1 0,2 0,3

1,0 1,1 1,2 1,3

2,0 2,1 2,2 2,3

3,0 3,1 3,2 3,3

Bloco 1, 0 Bloco 1, 1

0,0 0,1 0,2 0,3

1,0 1,1 1,2 1,3

2,0 2,1 2,2 2,3

3,0 3,1 3,2 3,3

0,0 0,1 0,2 0,3

1,0 1,1 1,2 1,3

2,0 2,1 2,2 2,3

3,0 3,1 3,2 3,3

Kernel 2dim3 gride(2, 2)dim3 bloco(15, 15, 1)

Gride

Bloco 0, 0 Bloco 0, 1

0,0 0,1 0,2 0,3

1,0 1,1 1,2 1,3

2,0 2,1 2,2 2,3

3,0 3,1 3,2 3,3

0,0 0,1 0,2 0,3

1,0 1,1 1,2 1,3

2,0 2,1 2,2 2,3

3,0 3,1 3,2 3,3

Bloco 1, 0 Bloco 1, 1

0,0 0,1 0,2 0,3

1,0 1,1 1,2 1,3

2,0 2,1 2,2 2,3

3,0 3,1 3,2 3,3

0,0 0,1 0,2 0,3

1,0 1,1 1,2 1,3

2,0 2,1 2,2 2,3

3,0 3,1 3,2 3,3

__global__ void mulGpu2(int *A[], int *B[], int *C[]) {int i = blockIdx.x * SUB_LARGURA + threadIdx.x;int j = blockIdx.y * SUB_LARGURA + threadIdx.y;

for (int k=1; k<=LARGURA; k++) {C[i][j] += (A[i][k] * B[k][j]);

}}

Kernel 2: Multiplicação na GPU

Kernel 2

7 4 3 0

3 9 6 1

7 8 1 5

2 3 4 6

3 4 3 9

5 6 8 7

7 8 4 2

1 6 8 4

0 0 0 0

0 0 0 0

0 0 0 0

0 0 0 0

1 2 3 4

1

2

3

4

__global__ void mulGpu2(int *A[], int *B[], int *C[]) {int i = blockIdx.x * SUB_LARGURA + threadIdx.x;int j = blockIdx.y * SUB_LARGURA + threadIdx.y;

for (int k=1; k<=LARGURA; k++) {C[i][j] += (A[i][k] * B[k][j]);

}}

Kernel 2: Multiplicação na GPU

Kernel 2

7 4 3 0

3 9 6 1

7 8 1 5

2 3 4 6

3 4 3 9

5 6 8 7

7 8 4 2

1 6 8 4

21 12 0 0

35 20 0 0

0 0 0 0

0 0 0 0

1 2 3 4

1

2

3

4

__global__ void mulGpu2(int *A[], int *B[], int *C[]) {int i = blockIdx.x * SUB_LARGURA + threadIdx.x;int j = blockIdx.y * SUB_LARGURA + threadIdx.y;

for (int k=1; k<=LARGURA; k++) {C[i][j] += (A[i][k] * B[k][j]);

}}

Kernel 2: Multiplicação na GPU

Kernel 2

7 4 3 0

3 9 6 1

7 8 1 5

2 3 4 6

3 4 3 9

5 6 8 7

7 8 4 2

1 6 8 4

12 36 0 0

18 54 0 0

0 0 21 0

0 0 3 0

1 2 3 4

1

2

3

4

__global__ void mulGpu2(int *A[], int *B[], int *C[]) {int i = blockIdx.x * SUB_LARGURA + threadIdx.x;int j = blockIdx.y * SUB_LARGURA + threadIdx.y;

for (int k=1; k<=LARGURA; k++) {C[i][j] += (A[i][k] * B[k][j]);

}}

Kernel 2: Multiplicação na GPU

Kernel 2

7 4 3 0

3 9 6 1

7 8 1 5

2 3 4 6

3 4 3 9

5 6 8 7

7 8 4 2

1 6 8 4

72 99 72 73

123 159 87 88

105 138 81 40

89 134 63 70

1 2 3 4

1

2

3

4

Instante de tempo t=L __global__ void mulGpu2(int *A[], int *B[], int *C[]) {

int i = blockIdx.x * SUB_LARGURA + threadIdx.x;int j = blockIdx.y * SUB_LARGURA + threadIdx.y;

for (int k=1; k<=LARGURA; k++) {C[i][j] += (A[i][k] * B[k][j]);

}}

Kernel 2: Multiplicação na GPU

Vantagens/Desvantagens

• Vantagem em relação a sequencial: 1. cada elemento de C é calculado em paralelo.

• Vantagens em relação a primeira abordagem: 1. Matrizes de tamanho arbitrário.2. Quantidade maior de blocos, permite melhor utilização dos recursos da GPU.

• Desvantagens desta abordagem:1. Restrição do formato das matrizes. Elas devem ser quadradas.2. Usa apenas a memória global da GPU. A memória global apresenta grande latência.3. Os mesmos dados são buscados várias vezes da memória.

Resultado: Muito tempo gasto esperando transferência de dados.

Dúvidas?