面试题答案
一键面试并行计算模型选择
- OpenMP:适用于共享内存架构的多核CPU。它通过在Fortran代码中添加编译指导语句来实现并行化,相对容易上手,适合对已有单线程Fortran程序进行并行改造。
- MPI:用于分布式内存系统,节点间通过消息传递进行通信。如果程序需要运行在多台机器组成的集群上,MPI是较好选择,但编程复杂度较高。
- CUDA Fortran:专门用于NVIDIA GPU的并行计算。适用于矩阵运算这类高度数据并行的任务,可显著提升计算效率,但需要对GPU架构和CUDA编程模型有深入了解。
以OpenMP为例重构过程
- 分析代码结构:确定矩阵运算部分的代码,通常是循环部分,这些循环可以并行执行。
- 添加OpenMP指导语句:
- 在最外层循环前添加
!$omp parallel do
,例如:
- 在最外层循环前添加
!$omp parallel do
do i = 1, n
do j = 1, m
result(i, j) = a(i, j) + b(i, j)
end do
end do
!$omp end parallel do
这里n
和m
是矩阵的维度,a
、b
是输入矩阵,result
是结果矩阵。
可能遇到的挑战及解决方案
- 数据竞争:多个线程同时访问和修改同一内存位置。
- 解决方案:使用临界区(
!$omp critical
)或原子操作(!$omp atomic
)来保护共享数据。例如,如果要对一个共享变量sum
进行累加:
- 解决方案:使用临界区(
!$omp parallel do reduction(+:sum)
do i = 1, n
sum = sum + a(i)
end do
!$omp end parallel do
这里使用reduction
子句,OpenMP会为每个线程创建一个sum
的私有副本,最后将这些副本的值累加到全局sum
中,避免数据竞争。
2. 负载不均衡:不同线程执行任务时间差异大。
- 解决方案:采用动态调度(!$omp parallel do schedule(dynamic)
),将任务动态分配给空闲线程,使负载更均衡。
以CUDA Fortran为例重构过程
- 数据传输:将主机(CPU)上的矩阵数据传输到设备(GPU)上。
real, dimension(:,:), allocatable :: a_host, b_host, result_host
real, dimension(:,:), device, allocatable :: a_device, b_device, result_device
allocate(a_host(n, m), b_host(n, m), result_host(n, m))
allocate(a_device(n, m), b_device(n, m), result_device(n, m))
a_device = a_host
b_device = b_host
- 内核函数编写:定义在GPU上执行矩阵运算的内核函数。
attributes(global) subroutine matrix_add(a, b, result, n, m)
real, dimension(:,:), device :: a, b, result
integer :: n, m
integer :: i, j
i = blockIdx%x * blockDim%x + threadIdx%x
j = blockIdx%y * blockDim%y + threadIdx%y
if (i <= n.and.j <= m) then
result(i, j) = a(i, j) + b(i, j)
end if
end subroutine matrix_add
- 调用内核函数:在主机代码中调用内核函数。
integer :: num_blocks_x, num_blocks_y, num_threads_x, num_threads_y
num_threads_x = 16
num_threads_y = 16
num_blocks_x = ceiling(real(n) / real(num_threads_x))
num_blocks_y = ceiling(real(m) / real(num_threads_y))
call matrix_add<<<dim3(num_blocks_x, num_blocks_y), dim3(num_threads_x, num_threads_y)>>>(a_device, b_device, result_device, n, m)
- 数据回传:将结果从GPU传输回CPU。
result_host = result_device
以CUDA Fortran为例可能遇到的挑战及解决方案
- GPU内存管理:GPU内存有限,可能出现内存不足。
- 解决方案:合理分配和释放GPU内存,例如只在需要时将数据传输到GPU,计算完成后及时释放设备内存。
- 内核函数性能调优:内核函数执行效率不高。
- 解决方案:优化内核函数代码,如合理使用共享内存(
shared
关键字),优化线程块和线程的分配等。
- 解决方案:优化内核函数代码,如合理使用共享内存(