Nesse post não vou explicar as várias outras funcionalidades que a API oferece em relação aos loops. Aqui vou fazer uma pausa para testar o que foi explicado até agora. De que adianta a teoria sem um teste prático do que tá rolando?
Para testar um loop distribuído, resolvi criar uma função que calcula a multiplicação entre duas matrizes, colocando o resultado em uma terceira matriz (MC = MA * MB):
const int MATRIX_SIZE = 1000;
int MA[MATRIX_SIZE][MATRIX_SIZE];
int MB[MATRIX_SIZE][MATRIX_SIZE];
int MC[MATRIX_SIZE][MATRIX_SIZE];
void multi()
{
int soma;
//percorre cada linha de MA
for(int j = 0; j < MATRIX_SIZE; j++)
{
//percorre cada coluna de MB
for(int i = 0; i < MATRIX_SIZE; i++, soma = 0)
{
//calcula
for(int k = 0; k < MATRIX_SIZE; k++)
{
soma += MA[j][k] * MB[k][i];
}
//coloca resoltado na matriz MC
MC[j][i] = soma;
}
}
}
int main(int ac, char **av)
{
multi();
return 0;
}
Como o teste consiste em melhorar o desempenho do cálculo, não me dei ao trabalho de iniciar os elementos das matrizes e nem de verificar se o resultado está correto.
Executando o código anterior, o Code::Blocks retornou a mensagem:
Process returned 0 (0×0) execution time : 23.509 s
Press any key to continue.
Lembrando que executei a compilação debug sem nenhum tipo de otimização. Com certeza a release seria muito mais rápida.
Quem lembra das aulas de matemática do colegial, sabe que o cálculo de cada elemento resultante (MC) independe do cálculo de elementos anteriores. O laço for está de acordo com as restrições impostas pela API, então, pq não dividir a tarefa em várias threads?
const int MATRIX_SIZE = 1000;
int MA[MATRIX_SIZE][MATRIX_SIZE];
int MB[MATRIX_SIZE][MATRIX_SIZE];
int MC[MATRIX_SIZE][MATRIX_SIZE];
void multi()
{
//percorre cada linha de MA
#pragma omp parallel for
for(int j = 0; j < MATRIX_SIZE; j++)
{
//percorre cada coluna de MB
for(int i = 0; i < MATRIX_SIZE; i++)
{
int soma = 0;
//calcula
for(int k = 0; k < MATRIX_SIZE; k++)
{
soma += MA[j][k] * MB[k][i];
}
//coloca resoltado na matriz MC
MC[j][i] = soma;
}
}
}
int main(int ac, char **av)
{
multi();
return 0;
}
Repare que, além do #pragma omp parallel for adicionado antes do primeiro loop, outras mudanças foram feitas para adaptar o código:
- A variável soma foi removida do começo da função e declarada dentro do segundo for, para evitar que seja compartilhada entre as threads;
- Não é mais necessário zerar a variável soma no corpo do segundo for;
Executando o código modificado, obtive o seguinte resultado:
Process returned 0 (0×0) execution time : 7.129 s
Press any key to continue.
Isso corresponde a, praticamente, um terço do tempo necessário para calcular pelo método tradicional. Surpreso? Requisitando 20 threads para executar o loop, obtive um resultado melhor ainda:
Process returned 0 (0×0) execution time : 6.692 s
Press any key to continue.
Okay, a diferença não é gritante, mas num sistema onde o desempenho é um fator crítico, esses ms podem fazer toda a diferença.
Leave a Reply