Primeira Questão
O programa omp_prob.c em si não possui um grande numero de variáveis, as mesmas da região paralela. Neuma delas apresenta uma clausula que defina o seu comportamento. Na execução do programa pode-se observar que a primeira variável tid recebe o numero identificador da thread que esta executando a região paralela. Em quanto a variável nthreads recebe o numero de threads ativas na região paralela. A variável i e uma variável de controle do laço for. E por ultimo temos a variável total que e a encarregada de conter os resultados das sumas sucessivas do programa.
Tanto a variável tid e a nthreads tem como função exteriorizar informação do sistema, é implicitamente compartilhadas por todas as threads a pesar delas somente servem para informar ao usuário do funcionamento da região paralela. No entanto a variável i, contador do laço for, de certa forma possibilita a paralelização do programa dividindo o trabalho entre as threads por iterações.
No panorama ideal o numero de loops deveria ser dividido em proporção a o numero de threads, o que não acontece devido a forma em que se encontra enunciada a sintaxe das diretivas OpenMP. De tal modo que também se consideram que o escopo implícito para a variável i e a clausula shared.
Pelo comportamento dos resultados pode-se afirmar que a variável total também seria uma variável compartilhada. Como não existe Neuma clausula que defina o comportamento destas variáveis o se assume que todas elas são compartilhadas.
O problema encontrado no uso de OpenMP e que uma thread executa todas as iterações pela clausula dynamic, mesmo setando o numero de threads com a variável de ambiente OMP_NUM_THREADS.
Modificando a diretiva:
#pragma omp for schedule(dynamic,10)
for (i=0; i<10; i++)
total = total + i*1.0;
pela diretiva:
#pragma omp for ordered
for (i=0; i<10; i++) {
#pragma omp ordered
total = total + i*1.0;
}
Consegue-se resultados corretos, mas a toda a carga da execução continua em uma thread.
Adicionando as clausulas shared e private nas variáveis da região paralela da seguinte forma, consegue-se uma melhor distribuição dos loops entre as threads.
#pragma omp parallel shared(total,nthreads) private(i,tid)
Segunda Questão
No programa omp_soma.c o problema esta no uso da clausula private para a variável soma. Cada thread tem sua copia da variável e atualiza conforme os loops. O que não outorga o resultado esperado.
Trocando a clausula private por shared, todas as threads atualizam uma única variável obtendo assim o resultado da soma.
#pragma omp parallel for schedule(static) shared(soma)
Terceira Questão
critical
Critical e uma diretiva de construção que e utilizada para a restrição na execução de determinadas tarefas. Colocando a diretiva critical antes das instruções se garante que as tarefas serão executadas em uma thread por vez. O uso desta diretiva e com a finalidade de evitar que uma variável do tipo compartilhada seja atualizada por mais uma thread ao mesmo tempo.
Exemplo:
#pragma omp parallel
{
#pragma omp for
for (i = 0; i < n; i++){
aux_dot += a[i]*b[i];
}
#pragma omp critical
dot += aux_dot;
}
Em este exemplo de paralelização do calculo de um produto escalar entre dois vetores, os loops são distribuídos entre as threads, de forma que cada uma das threads da região paralela calcula uma parte do problema. Ou seja cada thread tem sua copia da variável do aux_dot. Uma vez finalizado o calculo da parcela do produto escalar que corresponde a thread, se atualiza a variável dot com resultado obtido pela thread. Este procedimento e feito com cada uma das threads.
A diretiva critical e utilizada para que a variável dot não seja atualizada por mais de uma thread ao mesmo tempo. Quando nenhuma thread estiver atualizando a variável dot, a thread que estava esperando entra na sessão critica e faz a sua atualização mantendo a integridade do resultado.
atomic
O uso da diretiva atomic e semelhante ao uso da diretiva critical com a diferenca que o construtor atomic aplica-se a uma única instrução imediata apos a diretiva. A diretiva atomic comumente utilizada nas operações do tipo x ++ ou –x.
Exemplo:
#pragma omp parallel for shared(x, y, index, n)
for (i = 0; i < n; i ++)
{ #pragma omp atomic
x[index[i]] += work1(i);
y[i] += work2(i);
}
Neste exemplo de o uso da diretiva atomic evita a atualização simultânea da variável x por varias threads. Se for utilizada a diretiva critical no lugar da diretiva atomic a execução do programa seria seqüencialmente pois se estariam na sessão critica a variável x e a variável y.
Referencias: Exemplo Critical, Exemplo Atomic
Nenhum comentário:
Postar um comentário