回 帖 发 新 帖 刷新版面

主题:用fortran与OpenMP并行成功了

始终并行不了是一个很困惑的事,终于让我找到了一个例子,这是一个计算Pi的值,调用了并行,成功了,希望对初学者有帮助!!
我的配置是IVF9.1,v studio2005,Openmp2.5,进入代码界面后要设置属性---fortran--language--process--OpenMp Dirctives为Generate parallel code
这个并行的问题,我研究了很长时间,首先你要明确以下几点才能并行:
1 你的计算机是双核以上的
2 计算机的系统是64位的如XP64位(原因是现在的CPU多是采用64位架构,因此系统也要是64位的0
3 你所用的IVF有64位组件,也异是在安装时会有64MT。。。(在安装的过程中可以看到这个组件的安装)
4 在IVF中要配置参数,project--(×) properties-fortran——language——process openMP Directives ——generate parallel code(Qopenmp)
5 你的程序可以并行,即程序中有可以并行的地方,前后没有逻辑关系
   基本上把这几点弄懂了,差不多可以进行简单的并行计算了

program main

!*****************************************************************************80
!
!! MAIN is the main program for TEST_OMP.
!
!  Discussion:
!
!    TEST_OMP estimates the value of PI.
!
!    This program uses Open MP parallelization directives.  
!
!    It should run properly whether parallelization is used or not.
!
!    However, the parallel version computes the sum in a different
!    order than the serial version; some of the quantities added are
!    quite small, and so this will affect the accuracy of the results.
!
!  Modified:
!
!    29 January 2003
!
!  Author:
!
!    John Burkardt
!
!  A FORTRAN 90 module may be available:
!
! use omp_lib
!
!  A FORTRAN 77 include file may be available:
!
! include 'omp_lib.h'
!
  implicit none

  integer, parameter :: r4_logn_max = 9
  integer id
  integer nthreads
  integer omp_get_num_procs
  integer omp_get_num_threads
  integer omp_get_thread_num

  call timestamp ( )

  write ( *, '(a)' ) ' '
  write ( *, '(a)' ) 'TEST_OMP'
  write ( *, '(a)' ) '  FORTRAN90 version'
  write ( *, '(a)' ) ' '
  write ( *, '(a)' ) '  Estimate the value of PI by summing a series.'
  write ( *, '(a)' ) ' '
  write ( *, '(a)' ) '  This program includes Open MP directives, which'
  write ( *, '(a)' ) '  may be used to run the program in parallel.'
  write ( *, '(a)' ) ' '
  write ( *, '(a)' ) '  The number of processors available:'
  write ( *, '(a,i8)' ) '  OMP_GET_NUM_PROCS () = ', omp_get_num_procs ( )

  nthreads = 4

  write ( *, '(a)' ) ' '
  write ( *, '(a,i8,a)' ) '  Call OMP_SET_NUM_THREADS, and request ', &
    nthreads, ' threads.'

  call omp_set_num_threads ( nthreads )
!
!  Note that the call to OMP_GET_NUM_THREADS will always return 1
!  if called outside a parallel region!
!
!$OMP parallel private ( id )

  id = omp_get_thread_num ( )

  write ( *, '(a,i3)' ) '  This is process ', id

  if ( id == 0 ) then

    write ( *, '(a)' ) ' '
    write ( *, '(a)' ) '  Calling OMP_GET_NUM_THREADS inside a '
    write ( *, '(a)' ) '  parallel region, we get the number of'
    write ( *, '(a,i3)' ) '  threads is ', omp_get_num_threads ( )
    write ( *, '(a)' ) ' '

  end if

!$OMP end parallel

  call r4_test ( r4_logn_max )

  write ( *, '(a)' ) ' '
  write ( *, '(a)' ) 'TEST_OMP'
  write ( *, '(a)' ) '  Normal end of execution.'

  write ( *, '(a)' ) ' '
  call timestamp ( )

  stop
end
subroutine r4_test ( logn_max )

!*****************************************************************************80
!
!! R4_TEST estimates the value of PI using single precision.
!
!  Discussion:
!
!    PI is estimated using N terms.  N is increased from 10^2 to 10^LOGN_MAX.
!    The calculation is repeated using both sequential and Open MP enabled code.
!    Wall clock time is measured by calling SYSTEM_CLOCK.
!
!  Modified:
!
!    06 January 2003
!
!  Author:
!
!    John Burkardt
!
  implicit none

  integer clock_max
  integer clock_rate
  integer clock_start
  integer clock_stop
  real error
  real estimate
  integer logn
  integer logn_max
  character ( len = 3 ) mode
  integer n
  real r4_pi
  real time

  write ( *, '(a)' ) ' '
  write ( *, '(a)' ) 'R4_TEST:'
  write ( *, '(a)' ) '  Estimate the value of PI,'
  write ( *, '(a)' ) '  using single precision arithmetic.'
  write ( *, '(a)' ) ' '
  write ( *, '(a)' ) '  N = number of terms computed and added;'
  write ( *, '(a)' ) ' '
  write ( *, '(a)' ) '  ESTIMATE = the computed estimate of PI;'
  write ( *, '(a)' ) ' '
  write ( *, '(a)' ) '  ERROR = ( the computed estimate - PI );'
  write ( *, '(a)' ) ' '
  write ( *, '(a)' ) '  TIME = elapsed wall clock time;'
  write ( *, '(a)' ) ' '
  write ( *, '(a)' ) '  Note that you can''t increase N forever, because:'
  write ( *, '(a)' ) '  A) ROUNDOFF starts to be a problem, and'
  write ( *, '(a)' ) '  B) maximum integer size is a problem.'
  write ( *, '(a)' ) ' '
  write ( *, '(a,i12)' ) '  The maximum integer:' , huge ( n )
  write ( *, '(a)' ) ' '
  write ( *, '(a)' ) ' '
  write ( *, '(a)' ) '           N Mode    Estimate        Error           Time'
  write ( *, '(a)' ) ' '

  n = 1

  do logn = 2, logn_max

    mode = 'OMP'

    call system_clock ( clock_start, clock_rate, clock_max )

    call r4_pi_est_omp ( n, estimate )

    call system_clock ( clock_stop, clock_rate, clock_max )

    time = real ( clock_stop - clock_start ) / real ( clock_rate )

    error = abs ( estimate - r4_pi ( ) )

    write ( *, '( i12, 2x, a3, 2x, f14.10, 2x, g14.6, 2x, g14.6 )' ) &
      n, mode, estimate, error, time

    n = n * 10

  end do

  return
end
subroutine r4_pi_est_omp ( n, estimate )

!*****************************************************************************80
!
!! R4_PI_EST_OMP estimates the value of PI, using Open MP.
!
!  Discussion:
!
!    The calculation is based on the formula for the indefinite integral:
!
!      Integral 1 / ( 1 + X**2 ) dx = Arctan ( X ) 
!
!    Hence, the definite integral
!
!      Integral ( 0 <= X <= 1 ) 1 / ( 1 + X**2 ) dx 
!      = Arctan ( 1 ) - Arctan ( 0 )
!      = PI / 4.
!
!    A standard way to approximate an integral uses the midpoint rule.
!    If we create N equally spaced intervals of width 1/N, then the
!    midpoint of the I-th interval is 
!
!      X(I) = (2*I-1)/(2*N).  
!
!    The approximation for the integral is then:
!
!      Sum ( 1 <= I <= N ) (1/N) * 1 / ( 1 + X(I)**2 )
!
!    In order to compute PI, we multiply this by 4; we also can pull out
!    the factor of 1/N, so that the formula you see in the program looks like:
!
!      ( 4 / N ) * Sum ( 1 <= I <= N ) 1 / ( 1 + X(I)**2 )
!
!    Until roundoff becomes an issue, greater accuracy can be achieved by 
!    increasing the value of N.
!
!  Modified:
!
!    06 January 2003
!
!  Author:
!
!    John Burkardt
!
!  Parameters:
!
!    Input, integer N, the number of terms to add up.
!
!    Output, real ESTIMATE, the estimated value of pi.
!
  implicit none

  real h
  real estimate
  integer i
  integer n
  real sum2
  real x

  h = 1.0E+00 / real ( 2 * n )

  sum2 = 0.0E+00
!
!$OMP parallel do  private(x)  shared(h)  reduction(+: sum2)
!
  do i = 1, n
    x = h * real ( 2 * i - 1 )
    sum2 = sum2 + 1.0E+00 / ( 1.0E+00 + x**2 )
  end do

  estimate = 4.0E+00 * sum2 / real ( n )

  return
end
function r4_pi ( )

!*****************************************************************************80
!
!! R4_PI returns the value of pi.
!
!  Modified:
!
!    02 February 2000
!
!  Author:
!
!    John Burkardt
!
!  Parameters:
!
!    Output, real R4_PI, the value of pi.
!
  implicit none

  real r4_pi

  r4_pi = 3.14159265358979323846264338327950288419716939937510E+00

  return
end
subroutine timestamp ( )

!*****************************************************************************80
!
!! TIMESTAMP prints the current YMDHMS date as a time stamp.
!
!  Example:
!
!    May 31 2001   9:45:54.872 AM
!
!  Modified:
!
!    31 May 2001
!
!  Author:
!
!    John Burkardt
!
!  Parameters:
!
!    None
!
  implicit none

  character ( len = 8 ) ampm
  integer d
  character ( len = 8 ) date
  integer h
  integer m
  integer mm
  character ( len = 9 ), parameter, dimension(12) :: month = (/ &
    'January  ', 'February ', 'March    ', 'April    ', &
    'May      ', 'June     ', 'July     ', 'August   ', &
    'September', 'October  ', 'November ', 'December ' /)
  integer n
  integer s
  character ( len = 10 )  time
  integer values(8)
  integer y
  character ( len = 5 ) zone

  call date_and_time ( date, time, zone, values )

  y = values(1)
  m = values(2)
  d = values(3)
  h = values(5)
  n = values(6)
  s = values(7)
  mm = values(8)

  if ( h < 12 ) then
    ampm = 'AM'
  else if ( h == 12 ) then
    if ( n == 0 .and. s == 0 ) then
      ampm = 'Noon'
    else
      ampm = 'PM'
    end if
  else
    h = h - 12
    if ( h < 12 ) then
      ampm = 'PM'
    else if ( h == 12 ) then
      if ( n == 0 .and. s == 0 ) then
        ampm = 'Midnight'
      else
        ampm = 'AM'
      end if
    end if
  end if

  write ( *, '(a,1x,i2,1x,i4,2x,i2,a1,i2.2,a1,i2.2,a1,i3.3,1x,a)' ) &
    trim ( month(m) ), d, y, h, ':', n, ':', s, '.', mm, trim ( ampm )

  return
end

回复列表 (共10个回复)

沙发

学习!!!

板凳

我运行了一下可以并行,Openmp5.0是编译器默认安装的吗?还有没高版本的?

3 楼


我用的是OpenMP2.5,最新的是OpenMp3.0,不过还没有出来,这个OpenMp是IVF9.1自带的

4 楼

请问是不是只要把编译器设置好以后,在CVF下的程序直接运行,编译器就会自动的并行计算?

5 楼

问题远远没有那么简单,并不是说在程序里加上!$omp parallel//!$omp end parallel就可能并行,有时候并行运行的时间比串行的还并行的还要长,关键是要对自己的程序何时并行和在何处并行,并行得不好,那么为并行而进行的线程创建,初始化,线程管理以及中止等操作,这些开销的时间比串行还长。具体的问题要具体分析,举个例子:
  x(0)=0
  y(0)=1
  do i=1,100
    x(i)=y(i-1)+1
    y(i)=x(i-1)+2
  end do
这个程序不能直接在头尾加!$omp parallel do //!$omp end parallel do ,因为这个并行里存在循环迭代,如果你的程序里有这类循环,那么不能直接进行并行,需要重写并行程序为:
  x(0)=0
  y(0)=1
  x(49)=30
  y(49) =30
 !$omp parallel do  private(m,i)
  do m=0,1
  do i=m*49+1,m*50+50
    x(i)=y(i-1)+1
    y(i)=x(i-1)+2
  end do
  end do 
!$omp end parallel do 
  这还只是一个简单的更改,如果想深入的学习OpenMP,还要找一些专业的书看看并行的原理是怎么样的!

6 楼

如果我的程序结构是这样的

program main

 call A()

 call B()

end program main

A()  和 B() 是完全不相干的,请问如果要实现两个子程序的并行,应该怎么处理?

谢谢

7 楼

你可以这样处理:
 program main
 !$omp parallel
   call A()
   call B()
 !$omp end parallel
  end  program
  

  如果你的程序是这样的话:
program main
  integer i,j,n,m
  n=10
  m=10
 do i=1,n
   call A()
  end do 

  do j=1,m
    call B()
  end do
 
  end program
而且A,B没有数据联系,那么下面可以但是不好:
  program main
  integer i,j,n,m
  n=10
  m=10
!$omp parallel do
  do i=1,n
   call A()
  end do 
 !$omp end parallel do
 
 !$omp parallel do
  do j=1,m
    call B()
  end do
!$omp end parallel do

 下面的处理比较好:
  program main
  implicit none
  integer i,j,n,m
  n=10
  m=10

!$omp parallel
  !$omp do
    do i=1,n
      call A()
    end do 

  !$omp do
    do j=1,m
      call B()
    end do

 !$omp end parallel
  end  program
这样只进入并行区域一次,而且在并行区域内将工作分割成两部分,这样就可以消除开销,其中间的!$omp do可以不加!$omp end do

8 楼

可以这样实现最简单的并行:

program main
!$OMP PARALLEL SECTIONS 
!$OMP SECTION 
 call A()
!$OMP SECTION
 call B()
!$OMP END PARALLEL SECTIONS 
end program main

9 楼

受教了!

10 楼

这贴好,得学习学习。
准备买本书系统学习下并行,有推荐的没有?

我来回复

您尚未登录,请登录后再回复。点此登录或注册