1.9. IVF 并行计算与 MKL
IVF 2011 提供了多种并行计算方案,从自动并行到显式 OpenMP 和 Intel MKL。
1.9.1. Intel MKL 数学库
Intel Math Kernel Library (MKL) 提供高度优化的数学函数。
1.9.1.1. 配置 MKL
项目属性 → Intel Performance Libraries → Use MKL → Parallel
或链接器设置:
附加依赖项: mkl_intel_lp64.lib mkl_core.lib mkl_intel_thread.lib
libiomp5md.lib
1.9.1.2. BLAS 矩阵运算
program mkl_demo
use blas95 ! Fortran 95 BLAS 接口
implicit none
integer, parameter :: n = 3
real(8) :: a(n, n), b(n, n), c(n, n)
! 初始化矩阵
a = reshape([1.0_dp, 2.0_dp, 3.0_dp, &
4.0_dp, 5.0_dp, 6.0_dp, &
7.0_dp, 8.0_dp, 9.0_dp], [n, n])
b = reshape([9.0_dp, 8.0_dp, 7.0_dp, &
6.0_dp, 5.0_dp, 4.0_dp, &
3.0_dp, 2.0_dp, 1.0_dp], [n, n])
! 矩阵乘积 C = A * B
call gemm(a, b, c)
write(*, *) "矩阵乘法结果:"
write(*, '(3F8.2)') c
! 线性方程组 Ax = b
real(8) :: b_vec(n), x(n)
b_vec = [1.0_dp, 2.0_dp, 3.0_dp]
call gesv(a, b_vec, x) ! 求解 Ax = b
write(*, *) "解:", x
end program
1.9.1.3. LAPACK 线性代数
program lapack_demo
implicit none
integer, parameter :: n = 3
real(8) :: a(n, n), w(n) ! w = 特征值
integer :: info
a = reshape([2.0_dp, -1.0_dp, 0.0_dp, &
-1.0_dp, 2.0_dp, -1.0_dp, &
0.0_dp, -1.0_dp, 2.0_dp], [n, n])
! 计算特征值 (dsyev = double symmetric eigenvalue)
call dsyev("N", "U", n, a, n, w, work, lwork, info)
if (info == 0) then
write(*, *) "特征值:", w
end if
end program
小技巧
MKL 的向量数学库(VML)可以直接应用于数组:
include 'mkl_vml.f90'
real(8) :: x(1000), y(1000)
x = [ (i * 0.01_dp, i = 1, 1000) ]
call vdsin(1000, x, y) ! 批量 sin 计算
1.9.2. OpenMP 并行
IVF 2011 完全支持 OpenMP 3.0。
1.9.2.1. 启用 OpenMP
项目属性 → C/C++ → 语言 → OpenMP 支持 → 是 (/Qopenmp)
或命令行: ifort /Qopenmp program.f90
1.9.2.2. 并行 do 循环
program omp_demo
use omp_lib ! OpenMP 库模块
implicit none
integer, parameter :: n = 10000000
real(8), allocatable :: a(:), b(:), c(:)
real(8) :: start, end_time
integer :: i
allocate(a(n), b(n), c(n))
a = 1.0_dp; b = 2.0_dp
! 计时
start = omp_get_wtime()
! 并行 for 循环
!$omp parallel do
do i = 1, n
c(i) = a(i) * b(i) + sin(a(i)) * cos(b(i))
end do
!$omp end parallel do
end_time = omp_get_wtime()
write(*, '(A, F8.4, A)') "并行耗时: ", end_time - start, " 秒"
end program
重要
OpenMP 指令格式:
- !$omp:通常指令
- !$omp parallel do:并行 do 循环
- !$omp end parallel do:结束(可选,由缩进推断)
1.9.2.3. 规约操作
real(8) :: sum, a(n)
a = [ (i * 1.0_dp, i = 1, n) ]
sum = 0.0_dp
!$omp parallel do reduction(+:sum)
do i = 1, n
sum = sum + a(i)
end do
!$omp end parallel do
write(*, *) "总和:", sum
1.9.2.4. 并行区域
integer :: thread_id, num_threads
!$omp parallel private(thread_id) shared(num_threads)
thread_id = omp_get_thread_num()
!$omp master
num_threads = omp_get_num_threads()
write(*, *) "线程数:", num_threads
!$omp end master
write(*, '(A, I3, A, I3)') "线程 ", thread_id, " / ", num_threads - 1
!$omp end parallel
1.9.3. do concurrent(Fortran 2008+)
! 编译器自动并行化提示
integer :: i, j
real :: mat(1000, 1000)
do concurrent (i = 1:1000)
do concurrent (j = 1:1000)
mat(i, j) = sin(i * 0.01_dp) * cos(j * 0.01_dp)
end do
end do
! do concurrent 要求:
! 1. 循环间无依赖
! 2. 不能有 I/O 语句
! 3. 不能有 return/exit
! 4. 不能修改循环外变量(除赋值外)
备注
do concurrent 是**语义提示**,编译器可能自动并行,
但不保证一定并行。与 OpenMP 结合使用效果更佳。
1.9.4. 自动并行化(IVF 编译器)
IVF 可以自动分析循环并生成并行代码:
ifort /Qparallel program.f90 ! 自动并行
ifort /Qpar-report:2 program.f90 ! 显示并行报告
小心
自动并行依赖编译器的分析结果,复杂循环可能无法自动并行。 对于关键循环,建议使用显式的 OpenMP 指令。
1.9.5. 并行性能建议
建议 |
说明 |
|---|---|
大粒度并行 |
每个线程分配足够大的工作量,减少调度开销 |
避免假共享 |
不同线程的变量不要在同一缓存行上 |
减少同步 |
用 |
数据本地化 |
使用 |
绑定线程 |
设置 KMP_AFFINITY 环境变量绑定物理核心 |
均衡负载 |
用 |
嵌套并行谨慎 |
外层已并行时,内层不再开新线程 |
预热 |
在大循环前执行小计算让线程池激活 |
1.9.6. 环境变量
set OMP_NUM_THREADS=4 ! 设置线程数
set KMP_AFFINITY=granularity=fine,compact,1,0 ! 线程亲核性
set MKL_NUM_THREADS=4 ! MKL 线程数
set KMP_BLOCKTIME=200 ! 线程等待时间(毫秒)