当前位置: 首页 > news >正文

在Ubuntu 20.04上搞定PGI Fortran + CUDA 11.7:一份给科学计算新手的避坑指南

在Ubuntu 20.04上搞定PGI Fortran + CUDA 11.7:一份给科学计算新手的避坑指南

如果你正在尝试将传统的Fortran科学计算代码迁移到GPU加速环境,可能会被各种版本依赖和配置问题搞得焦头烂额。Ubuntu 20.04 LTS作为当前最稳定的Linux发行版之一,搭配CUDA 11.7和PGI Fortran(现为NVIDIA HPC SDK)确实能带来显著的性能提升,但前提是你能顺利跨过安装和配置这道坎。

1. 环境准备与CUDA 11.7安装

在开始之前,确保你的系统满足以下基本要求:

  • 支持CUDA的NVIDIA显卡(计算能力3.5及以上)
  • Ubuntu 20.04 LTS 64位系统
  • 至少20GB的可用磁盘空间
  • 稳定的网络连接

1.1 系统检查与依赖安装

首先验证你的GPU是否支持CUDA:

lspci | grep -i nvidia

输出应显示你的NVIDIA显卡型号。如果不确定显卡的计算能力,可以查阅NVIDIA官方文档。

接下来更新系统并安装必要依赖:

sudo apt update && sudo apt upgrade -y sudo apt install -y build-essential linux-headers-$(uname -r)

关键步骤:禁用默认的nouveau驱动:

  1. 创建配置文件:
    sudo bash -c "echo 'blacklist nouveau' > /etc/modprobe.d/blacklist-nouveau.conf" sudo bash -c "echo 'options nouveau modeset=0' >> /etc/modprobe.d/blacklist-nouveau.conf"
  2. 更新initramfs并重启:
    sudo update-initramfs -u sudo reboot
  3. 验证nouveau是否已禁用:
    lsmod | grep nouveau
    如果没有输出,说明禁用成功。

1.2 安装NVIDIA驱动和CUDA 11.7

Ubuntu 20.04提供了更简便的驱动安装方式:

sudo ubuntu-drivers autoinstall

安装完成后验证驱动:

nvidia-smi

你应该看到类似如下的输出,包含驱动版本和CUDA版本信息:

+-----------------------------------------------------------------------------+ | NVIDIA-SMI 450.80.02 Driver Version: 450.80.02 CUDA Version: 11.7 | |-------------------------------+----------------------+----------------------+

现在安装CUDA 11.7工具包:

wget https://developer.download.nvidia.com/compute/cuda/11.7.0/local_installers/cuda_11.7.0_515.43.04_linux.run sudo sh cuda_11.7.0_515.43.04_linux.run

在安装界面中:

  1. 取消勾选"NVIDIA Driver"(因为我们已经单独安装了驱动)
  2. 确保"CUDA Toolkit 11.7"被选中
  3. 接受许可协议并完成安装

1.3 环境配置与验证

将CUDA添加到环境变量:

echo 'export PATH=/usr/local/cuda-11.7/bin:$PATH' >> ~/.bashrc echo 'export LD_LIBRARY_PATH=/usr/local/cuda-11.7/lib64:$LD_LIBRARY_PATH' >> ~/.bashrc source ~/.bashrc

验证CUDA安装:

nvcc --version

应该显示CUDA 11.7的版本信息。

运行设备查询示例:

cd /usr/local/cuda/samples/1_Utilities/deviceQuery make ./deviceQuery

如果看到"Result = PASS",说明CUDA安装成功。

2. NVIDIA HPC SDK(原PGI编译器)安装

PGI编译器现已整合到NVIDIA HPC SDK中,提供了更好的CUDA Fortran支持。

2.1 下载与安装

从NVIDIA官网下载HPC SDK:

wget https://developer.download.nvidia.com/hpc-sdk/22.7/nvhpc_2022_227_Linux_x86_64_cuda_11.7.tar.gz tar xzvf nvhpc_2022_227_Linux_x86_64_cuda_11.7.tar.gz cd nvhpc_2022_227_Linux_x86_64_cuda_11.7 sudo ./install

按照提示完成安装,建议选择默认安装路径(/opt/nvidia/hpc_sdk)。

2.2 环境配置

设置HPC SDK环境变量:

echo 'export PATH=/opt/nvidia/hpc_sdk/Linux_x86_64/22.7/compilers/bin:$PATH' >> ~/.bashrc echo 'export MANPATH=/opt/nvidia/hpc_sdk/Linux_x86_64/22.7/compilers/man:$MANPATH' >> ~/.bashrc source ~/.bashrc

验证安装:

pgfortran --version

应显示类似如下的信息:

pgfortran 22.7-0 64-bit target on x86-64 Linux -tp zen2-64

2.3 解决常见兼容性问题

Ubuntu 20.04默认的gcc版本(9.4.0)可能与HPC SDK不完全兼容。解决方法:

  1. 安装兼容的gcc版本:
sudo apt install -y gcc-8 g++-8
  1. 设置编译器替代:
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-8 80 sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-8 80 sudo update-alternatives --config gcc # 选择gcc-8
  1. 配置HPC SDK使用指定gcc版本:
export CC=gcc-8 export CXX=g++-8

3. CUDA Fortran开发环境配置

3.1 创建简单的CUDA Fortran项目

创建一个简单的向量加法示例:

vecadd.cuf:

module mathOps contains attributes(global) subroutine vectorAdd(a, b, c, n) implicit none real, device :: a(n), b(n), c(n) integer, value :: n integer :: i i = blockDim%x * (blockIdx%x - 1) + threadIdx%x if (i <= n) then c(i) = a(i) + b(i) end if end subroutine vectorAdd end module mathOps program main use mathOps use cudafor implicit none integer, parameter :: N = 1024 real :: a(N), b(N), c(N) real, device :: a_d(N), b_d(N), c_d(N) type(dim3) :: grid, block ! 初始化主机数据 a = 1.0 b = 2.0 ! 设置CUDA核执行配置 block = dim3(256, 1, 1) grid = dim3(ceiling(real(N)/block%x), 1, 1) ! 传输数据到设备 a_d = a b_d = b ! 调用CUDA核 call vectorAdd<<<grid, block>>>(a_d, b_d, c_d, N) ! 将结果传回主机 c = c_d ! 验证结果 if (all(abs(c - 3.0) < 1.0e-5)) then print *, "Test passed!" else print *, "Test failed!" end if end program main

3.2 编译与运行

使用以下命令编译CUDA Fortran程序:

pgfortran -Mcuda -o vecadd vecadd.cuf

运行程序:

./vecadd

如果看到"Test passed!"输出,说明CUDA Fortran环境配置成功。

3.3 Makefile示例

对于更复杂的项目,建议使用Makefile管理编译过程:

Makefile:

FC = pgfortran FCFLAGS = -Mcuda -fast -Minfo=all SRCS = vecadd.cuf OBJS = $(SRCS:.cuf=.o) EXEC = vecadd all: $(EXEC) $(EXEC): $(OBJS) $(FC) $(FCFLAGS) -o $@ $^ %.o: %.cuf $(FC) $(FCFLAGS) -c $< clean: rm -f $(OBJS) $(EXEC)

4. 性能优化与调试技巧

4.1 编译器优化选项

PGI编译器提供了多种优化选项:

  • -fast:启用常用优化组合
  • -O3:高级优化级别
  • -Mcuda=fastmath:启用CUDA快速数学运算
  • -Minfo=accel:显示并行化信息

推荐组合:

pgfortran -Mcuda=fastmath -fast -Minfo=all -o program program.cuf

4.2 CUDA Fortran特有功能

  1. Managed Memory:简化内存管理
real, managed :: array(N)
  1. CUDA事件计时
type(cudaEvent) :: startEvent, stopEvent real :: time ierr = cudaEventCreate(startEvent) ierr = cudaEventCreate(stopEvent) ierr = cudaEventRecord(startEvent, 0) ! ... 执行核函数 ... ierr = cudaEventRecord(stopEvent, 0) ierr = cudaEventSynchronize(stopEvent) ierr = cudaEventElapsedTime(time, startEvent, stopEvent) print *, "Kernel time:", time, "ms"

4.3 调试工具

  1. CUDA-MEMCHECK
cuda-memcheck ./vecadd
  1. PGI编译器调试信息
pgfortran -g -Mcuda -o vecadd vecadd.cuf
  1. Nsight Systems:性能分析工具
nsys profile --stats=true ./vecadd

4.4 多GPU编程

对于多GPU系统,可以使用以下模式:

use cudafor integer :: numDevices, ierr ierr = cudaGetDeviceCount(numDevices) do i = 0, numDevices-1 ierr = cudaSetDevice(i) ! 在每个设备上执行操作 end do

5. 实际应用案例:矩阵乘法优化

让我们看一个更实际的例子:矩阵乘法优化。

matmul.cuf:

module matmul_kernels use cudafor contains attributes(global) subroutine simpleMatMul(A, B, C, N) real, device :: A(N,N), B(N,N), C(N,N) integer, value :: N integer :: i, j, k real :: sum i = (blockIdx%x-1)*blockDim%x + threadIdx%x j = (blockIdx%y-1)*blockDim%y + threadIdx%y if (i <= N .and. j <= N) then sum = 0.0 do k = 1, N sum = sum + A(i,k) * B(k,j) end do C(i,j) = sum end if end subroutine simpleMatMul attributes(global) subroutine sharedMatMul(A, B, C, N) real, device :: A(N,N), B(N,N), C(N,N) integer, value :: N integer :: i, j, k, tx, ty, bx, by real, shared :: As(16,16), Bs(16,16) real :: sum tx = threadIdx%x ty = threadIdx%y bx = blockIdx%x by = blockIdx%y i = (bx-1)*16 + tx j = (by-1)*16 + ty sum = 0.0 do k = 1, N, 16 if (i <= N .and. (k+ty-1) <= N) then As(tx,ty) = A(i, k+ty-1) else As(tx,ty) = 0.0 end if if ((k+tx-1) <= N .and. j <= N) then Bs(tx,ty) = B(k+tx-1, j) else Bs(tx,ty) = 0.0 end if call syncthreads() do kk = 1, 16 sum = sum + As(tx,kk) * Bs(kk,ty) end do call syncthreads() end do if (i <= N .and. j <= N) then C(i,j) = sum end if end subroutine sharedMatMul end module matmul_kernels program main use matmul_kernels use cudafor implicit none integer, parameter :: N = 512 real :: A(N,N), B(N,N), C1(N,N), C2(N,N) real, device :: A_d(N,N), B_d(N,N), C1_d(N,N), C2_d(N,N) type(dim3) :: grid, block integer :: i, j, ierr real :: time type(cudaEvent) :: startEvent, stopEvent ! 初始化矩阵 do j = 1, N do i = 1, N A(i,j) = real(i+j) B(i,j) = real(i-j) end do end do ! 创建CUDA事件 ierr = cudaEventCreate(startEvent) ierr = cudaEventCreate(stopEvent) ! 传输数据到设备 A_d = A B_d = B ! 简单矩阵乘法 block = dim3(16, 16, 1) grid = dim3(ceiling(real(N)/16), ceiling(real(N)/16), 1) ierr = cudaEventRecord(startEvent, 0) call simpleMatMul<<<grid, block>>>(A_d, B_d, C1_d, N) ierr = cudaEventRecord(stopEvent, 0) ierr = cudaEventSynchronize(stopEvent) ierr = cudaEventElapsedTime(time, startEvent, stopEvent) print *, "Simple matmul time:", time, "ms" ! 共享内存矩阵乘法 ierr = cudaEventRecord(startEvent, 0) call sharedMatMul<<<grid, block>>>(A_d, B_d, C2_d, N) ierr = cudaEventRecord(stopEvent, 0) ierr = cudaEventSynchronize(stopEvent) ierr = cudaEventElapsedTime(time, startEvent, stopEvent) print *, "Shared matmul time:", time, "ms" ! 验证结果 C1 = C1_d C2 = C2_d do j = 1, N do i = 1, N if (abs(C1(i,j) - C2(i,j)) > 1.0e-4) then print *, "Error at (", i, ",", j, ")" stop end if end do end do print *, "Results match!" ! 清理 ierr = cudaEventDestroy(startEvent) ierr = cudaEventDestroy(stopEvent) end program main

编译并运行:

pgfortran -Mcuda=fastmath -fast -Minfo=all -o matmul matmul.cuf ./matmul

这个示例展示了两种矩阵乘法实现:简单版本和使用了共享内存的优化版本。在实际应用中,共享内存版本通常能带来显著的性能提升。

http://www.cnnetsun.cn/news/1992153.html

相关文章:

  • Docker Rootless模式踩坑全记录:从CentOS 7低内核到成功运行Nginx
  • 别再争论‘男程序员 vs 女程序员’了:从一篇经典课文聊聊技术团队中的无意识偏见与如何构建真正包容的环境
  • 告别刺眼白底!Notepad++ 8.5.8与Adobe Acrobat DC 2024最新版护眼主题配置全流程
  • Qwen3-0.6B-FP8效果展示:Chainlit界面中完成复杂Python函数生成与单元测试编写
  • 齿轮箱零部件及其装配质检中的TVA技术突破(3)
  • 如何快速部署d2s-editor:暗黑2存档修改的终极免费指南
  • Agentic AI时代程序员必备算法思想详解(附实战案例)
  • 文脉定序实战教程:构建带缓存机制的重排序服务,降低GPU重复计算开销
  • Bebas Neue 开源字体:如何用免费方案解决专业设计难题
  • LabVIEW波形图数据类型应用
  • 魔兽争霸III优化完全指南:WarcraftHelper终极配置方案
  • Win10/Win11系统下,Labelme AI标注模型导入避坑全记录:从C盘路径查找到环境配置
  • Python Web服务器网关接口:WSGI、ASGI、RSGI、uWSGI、uwsgi、Gunicorn、Uvicorn
  • 抖音批量下载神器:如何用Python实现高效去水印视频采集?
  • Vue项目里语音播报没声音?手把手教你解决Chrome 89+的本地合成服务问题
  • 别再只用JConsole本地监控了!手把手教你搞定远程JMX连接(附四种安全模式对比)
  • 150元预算也能玩SDR?手把手教你用ZYNQ7010+AD9363搭建开源无线电硬件(附BOM清单)
  • 071、芯片级优化:扩散模型专用加速器设计手记
  • 十万个why:为什么大模型不设计成带有记忆的?
  • Scikit-learn ROC曲线超直观
  • 告别‘秃头’地球:给你的Cesium项目加上本地离线地形(Nginx/Tomcat静态服务发布详解)
  • 数字信号处理学习笔记--Chapter 1.4.1 时域采样定理基本概念
  • AGI探索策略失效全解析,深度解读稀疏奖励下的3种自驱机制崩溃场景
  • 山东大学创新实训项目个人博客——第二篇
  • 042、功耗管理与动态频率电压调整(DVFS)集成:从一次电池续航翻车说起
  • Neeshck-Z-lmage_LYX_v2应用案例:快速生成电商产品精修图与风格海报
  • 学术科研必备!最全EndNote期刊名缩写!直接导入就可使用
  • 终极指南:如何用RPFM工具将全面战争MOD开发效率提升500%
  • 如何永久保存微信聊天记录?WeChatMsg开源工具深度解析
  • JiYuTrainer:极域电子教室破解工具的完整使用指南与教程