Windows10下Code::Blocks中GFortran SIMD子例程优化无效求助
问题背景
在Windows 10系统的Code::Blocks环境中使用GFortran时,编写了带!$omp declare simd指令的显式SIMD子例程iteration_test1,以及对应串行版本iteration_test。测试时禁用了O1、O2、O3等自动优化,尝试了-fopenmp-simd、-march=native、-msse4.2、-mavx等编译选项,但SIMD版本的计算速度并未优于串行版本。
核心原因分析
SIMD调用方式错误:当前代码在循环中逐个调用SIMD子例程,每次仅处理单个标量参数。
!$omp declare simd的作用是让子例程支持向量化调用(即一次传入一组并行数据),但单元素调用无法触发SIMD并行,本质还是串行执行每个子例程实例,完全没用到SIMD的并行能力。禁用基础优化导致SIMD生成失效:GFortran的SIMD指令生成严重依赖O1/O2/O3等级的优化。禁用这些优化后,即使添加
-fopenmp-simd,编译器也无法完成循环展开、向量化转换等关键操作,子例程内部的循环无法被向量化,SIMD指令根本不会生成。数据相关分支阻碍向量化:子例程内部的
if (val_temp .gt. 233333.)是数据依赖的条件分支,SIMD向量操作要求所有元素执行统一指令流,这种分支会打破指令一致性。无优化情况下,编译器完全无法对该循环做向量化处理。
改进方案
修改调用逻辑,传入数组参数
将SIMD子例程的输入输出改为数组类型,直接一次调用处理整个数组,让编译器能触发SIMD并行处理:! 修改后的SIMD子例程调用 call iteration_test1(1.5d5, value_0) ! 直接传入整个数组同时调整子例程定义,适配数组参数:
subroutine iteration_test1(val_in, val_out) !$omp declare simd(iteration_test1) uniform(val_in) implicit none real(8),intent(in)::val_in real(8),intent(out)::val_out(:) ! 改为数组 real(8) val_temp integer(8)::num,i,j num = 200000 do j = 1, size(val_out) val_temp = 0.d0 do i = 1, num val_temp = min(val_temp + real(i,8), 233333.d0) end do val_out(j) = val_in + val_temp end do end subroutine iteration_test1开启必要的优化等级
编译时至少开启-O2优化,配合-fopenmp-simd和-march=native,让编译器具备生成SIMD指令的能力,同时完成循环展开、冗余消除等优化:gfortran -O2 -fopenmp-simd -march=native your_code.f90 -o test.exe消除分支逻辑,提升向量化可能性
用无分支的算术运算替换原有的条件判断,比如用min()函数替代if语句:val_temp = min(val_temp + real(i,8), 233333.d0)这样可以消除数据依赖分支,让编译器更容易对循环做向量化处理。
原始测试代码
program test use module2_explicit_vector_simd implicit none integer(8) i, int_time, end_time, n parameter ( n = 40 ) real(8),dimension(n)::value_0, value_1 call system_clock(int_time) do i = 1, n call iteration_test1(1.5d5,value_0(i)) end do call system_clock(end_time) write(*,*) sum(value_0) write(*,*) "Parallel:",(end_time - int_time)/100 call system_clock(int_time) do i = 1, n call iteration_test(1.5d5,value_1(i)) end do call system_clock(end_time) write(*,*) sum(value_1) write(*,*) "Sequential program:",(end_time - int_time)/100 end program test subroutine iteration_test(val_in,val_out) implicit none real(8) val_in, val_out, val_temp integer(8)::num,i val_temp = 0.d0 num = 200000 do i = 1, num val_temp = val_temp + real(i,8) if (val_temp .gt. 233333.) then val_temp = 233333. endif end do val_out = val_in + val_temp return end subroutine iteration_test module module2_explicit_vector_simd contains subroutine iteration_test1(val_in,val_out) !$omp declare simd(iteration_test1) uniform(val_in) implicit none real(8),intent(in)::val_in real(8),intent(out)::val_out real(8) val_temp integer(8)::num,i val_temp = 0.d0 num = 200000 do i = 1, num val_temp = val_temp + real(i,8) if (val_temp .gt. 233333.) then val_temp = 233333. endif end do val_out = val_in + val_temp return end subroutine iteration_test1 end module module2_explicit_vector_simd
内容的提问来源于stack exchange,提问作者Feng Gao

