loops - mundo - OPENMP F90/95 Bucles de DO anidados: problemas para mejorar la implementación en serie
openmp guia (2)
Hice algunas búsquedas pero no pude encontrar nada que pareciera estar relacionado con mi pregunta (lo siento si mi pregunta es redundante). De todos modos, como dice el título, tengo problemas para mejorar la implementación en serie de mi código. El fragmento de código que necesito para paralelizar es el siguiente (esto es Fortran90 con OpenMP):
do n=1,lm
do m=1,jm
do l=1,im
sum_u = 0
sum_v = 0
sum_t = 0
do k=1,lm
!$omp parallel do reduction (+:sum_u,sum_v,sum_t)
do j=1,jm
do i=1,im
exp_smoother=exp(-(abs(i-l)/hzscl)-(abs(j-m)/hzscl)-(abs(k-n)/vscl))
sum_u = sum_u + u_p(i,j,k) * exp_smoother
sum_v = sum_v + v_p(i,j,k) * exp_smoother
sum_t = sum_t + t_p(i,j,k) * exp_smoother
sum_u_pert(l,m,n) = sum_u
sum_v_pert(l,m,n) = sum_v
sum_t_pert(l,m,n) = sum_t
end do
end do
end do
end do
end do
end do
¿Estoy teniendo problemas de condición de carrera? ¿O simplemente estoy poniendo la directiva en el lugar equivocado? Soy bastante nuevo en esto, así que me disculpo si este es un problema excesivamente simplista.
De todos modos, sin paralelización, el código es insoportablemente lento. Para dar una idea del tamaño del problema, los índices lm, jm e im son 60, 401 y 501, respectivamente. Entonces la paralelización es crítica. ¡Cualquier ayuda o enlace a recursos útiles sería muy apreciada! Estoy usando xlf para compilar el código anterior, si eso es útil.
¡Gracias! -Jen
El lugar obvio para poner el omp pragma es en el círculo exterior.
Para cada (l, m, n), estás calculando una convolución entre tus variables perturbadas y un suavizador exponencial. Cada cálculo (l, m, n) es completamente independiente de los demás, por lo que puede colocarlo en el ciclo más externo. Entonces, por ejemplo, lo más simple
!$omp parallel do private(n,m,l,i,j,k,exp_smoother) shared(sum_u_pert,sum_v_pert,sum_t_pert,u_p,v_p,t_p), default(none)
do n=1,lm
do m=1,jm
do l=1,im
do k=1,lm
do j=1,jm
do i=1,im
exp_smoother=exp(-(abs(i-l)/hzscl)-(abs(j-m)/hzscl)-(abs(k-n)/vscl))
sum_u_pert(l,m,n) = sum_u_pert(l,m,n) + u_p(i,j,k) * exp_smoother
sum_v_pert(l,m,n) = sum_v_pert(l,m,n) + v_p(i,j,k) * exp_smoother
sum_t_pert(l,m,n) = sum_t_pert(l,m,n) + t_p(i,j,k) * exp_smoother
end do
end do
end do
end do
end do
end do
me da una aceleración de ~ 6x en 8 núcleos (usando un tamaño de problema muy reducido de 20x41x41). Dada la cantidad de trabajo que hay que hacer en los bucles, incluso en el tamaño más pequeño, supongo que la razón por la que no es una aceleración de 8x implica contensión de memoria o intercambio falso; para una mayor optimización del rendimiento, es posible que desee dividir explícitamente las matrices de suma en subbloques para cada subproceso y combinarlas al final; pero dependiendo del tamaño del problema, puede no ser deseable tener el equivalente de una matriz extra de tamaño im x jm x lm.
Parece que hay mucha estructura en este problema que puedes explotar para acelerar incluso el caso serial, pero es más fácil decir eso para encontrarlo; jugando en bolígrafo y papel no se le viene a la mente en unos minutos, pero alguien más listo puede descubrir algo.
Lo que tienes es una convolución. Esto se puede hacer con una Transformada rápida de Fourier en N log2 (N) tiempo. Tu algoritmo es N ^ 2. ¡Si usas FFT, un núcleo probablemente sea suficiente!