使用 openMP 和 openACC 的多线程多 GPU 计算
Multi-threaded multi GPU computation using openMP and openACC
我正在尝试编写将 openmp 线程移植到单个 gpu 的代码。我在 this.Since 上发现的案例研究/代码非常少我不是计算机科学背景。
我的编程技能比较差
This is how the basic idea look's like
这是目前开发的代码。
CALL OMP_SET_NUM_THREADS(2)
!$omp parallel num_threads(acc_get_num_devices(acc_device_nvidia))
do while ( num.gt.iteration)
id = omp_get_thread_num()
call acc_set_device_num(id+1, acc_device_nvidia)
!!$acc kernels
!error=0.0_rk
!!$omp do
!$acc kernels
!!$omp do
do j=2,nj-1
!!$acc kernels
do i=2,ni-1
T(i,j)=0.25*(T_o(i+1,j)+T_o(i-1,j)+ T_o(i,j+1)+T_o(i,j-1) )
enddo
!!$acc end kernels
enddo
!!$omp end do
!$acc end kernels
!!$acc update host(T,T_o)
error=0.0_rk
do j=2,nj-1
do i=2,ni-1
error = max( abs(T(i,j) - T_o(i,j)), error)
T_o(i,j) = T(i,j)
enddo
enddo
!!$acc end kernels
!!$acc update host(T,T_o,error)
iteration = iteration+1
print*,iteration , error
!print*,id
enddo
!$omp end parallel
这里有很多问题。
首先,您不能将 OpenMP(或 OpenACC)并行循环放在一个 do while 上。 do while have indeterminant number to iterations for therefore create a dependency in that exiting the loop depends on the previous iteration of the loop.您需要使用 DO 循环,其中迭代次数在进入循环时已知。
其次,即使您将其转换为 DO 循环,如果 运行 并行,您也会遇到竞争条件。每个 OpenMP 线程都会为 T 和 T_o 数组的相同元素赋值。另外,T_o 的结果用作下一次迭代的输入,从而创建依赖关系。换句话说,如果您尝试并行化外部迭代循环,您会得到错误的答案。
对于 OpenACC 代码,我建议在迭代循环周围添加一个数据区域,即“!$acc data copy(T,T_o)”在迭代循环之前和循环之后“ !$acc end data",这样数据只在设备上创建一次。正如您现在所拥有的那样,每次通过迭代循环都会隐式创建和复制数据,从而导致不必要的数据移动。还在最大错误减少循环周围添加一个内核区域,这样它也被卸载了。
总的来说,我更喜欢使用 MPI+OpenCC 进行多 GPU 编程而不是 OpenMP。使用 MPI,域分解是固有的,然后您可以将 MPI 等级一对一映射到设备。并不是说 OpenMP 不能工作,而是您经常需要手动分解域。还试图管理多个设备内存并使它们保持同步可能很棘手。加上 MPI,您的代码还可以跨节点,而不是仅限于单个节点。
我正在尝试编写将 openmp 线程移植到单个 gpu 的代码。我在 this.Since 上发现的案例研究/代码非常少我不是计算机科学背景。 我的编程技能比较差
This is how the basic idea look's like
这是目前开发的代码。
CALL OMP_SET_NUM_THREADS(2)
!$omp parallel num_threads(acc_get_num_devices(acc_device_nvidia))
do while ( num.gt.iteration)
id = omp_get_thread_num()
call acc_set_device_num(id+1, acc_device_nvidia)
!!$acc kernels
!error=0.0_rk
!!$omp do
!$acc kernels
!!$omp do
do j=2,nj-1
!!$acc kernels
do i=2,ni-1
T(i,j)=0.25*(T_o(i+1,j)+T_o(i-1,j)+ T_o(i,j+1)+T_o(i,j-1) )
enddo
!!$acc end kernels
enddo
!!$omp end do
!$acc end kernels
!!$acc update host(T,T_o)
error=0.0_rk
do j=2,nj-1
do i=2,ni-1
error = max( abs(T(i,j) - T_o(i,j)), error)
T_o(i,j) = T(i,j)
enddo
enddo
!!$acc end kernels
!!$acc update host(T,T_o,error)
iteration = iteration+1
print*,iteration , error
!print*,id
enddo
!$omp end parallel
这里有很多问题。
首先,您不能将 OpenMP(或 OpenACC)并行循环放在一个 do while 上。 do while have indeterminant number to iterations for therefore create a dependency in that exiting the loop depends on the previous iteration of the loop.您需要使用 DO 循环,其中迭代次数在进入循环时已知。
其次,即使您将其转换为 DO 循环,如果 运行 并行,您也会遇到竞争条件。每个 OpenMP 线程都会为 T 和 T_o 数组的相同元素赋值。另外,T_o 的结果用作下一次迭代的输入,从而创建依赖关系。换句话说,如果您尝试并行化外部迭代循环,您会得到错误的答案。
对于 OpenACC 代码,我建议在迭代循环周围添加一个数据区域,即“!$acc data copy(T,T_o)”在迭代循环之前和循环之后“ !$acc end data",这样数据只在设备上创建一次。正如您现在所拥有的那样,每次通过迭代循环都会隐式创建和复制数据,从而导致不必要的数据移动。还在最大错误减少循环周围添加一个内核区域,这样它也被卸载了。
总的来说,我更喜欢使用 MPI+OpenCC 进行多 GPU 编程而不是 OpenMP。使用 MPI,域分解是固有的,然后您可以将 MPI 等级一对一映射到设备。并不是说 OpenMP 不能工作,而是您经常需要手动分解域。还试图管理多个设备内存并使它们保持同步可能很棘手。加上 MPI,您的代码还可以跨节点,而不是仅限于单个节点。