回 帖 发 新 帖 刷新版面

主题:OpenMP中某段程序的并行时间与串行时不一样

各位老师:
    我在学习OpenMP时遇到了一个问题,我想将某个矩阵乘相量的do循环利用OpenMp进行并行化处理,但发现用OpenMP处理后的程序消耗的时间仅比比串行的时候快不到一倍。我将每一块循环分开来分别计算时间,发现每块单裤的循环在OpenMP并行下所消耗的时间比串行时慢了一倍。请问各位老师,这是怎么回事?
    我可能说的不是太清楚,程序片段附在了下面,就是说时间差diff1、diff2、diff3、diff4在并行状态下的结果是串行状态时的将近一倍。请问这具体是什么原因造成的?


!cccccccccccccccc------------Subroutine MVP    Y=A*X--------------cccccccccccccccc
subroutine MVP(A,X,Y,N)
    implicit none
  integer i,j,N,k
  integer time(2),t1(2),t2(2),t3(2),t4(2),diff,diff1,diff2,diff3,diff4
  real :: X(N),Y(N),A(N,N)
  Y=0
  k=N/4
  call system_clock(time(1))
!$OMP parallel
!$OMP sections
 !$OMP section

  call system_clock(t1(1))

    do i=1,k
      do j=1,N
        Y(i)=Y(i)+A(j,i)*X(j)
      enddo
    enddo

  call system_clock(t1(2))
  diff1=t1(2)-t1(1)


  !$OMP section
   call system_clock(t2(1))
  do i=k+1,2*k
      do j=1,N
        Y(i)=Y(i)+A(j,i)*X(j)
      enddo
    enddo
   call system_clock(t2(2))
   diff2=t2(2)-t2(1)
   
  !$OMP section
   call system_clock(t3(1))
  do i=2*k+1,3*k
      do j=1,N
        Y(i)=Y(i)+A(j,i)*X(j)
      enddo
    enddo
   call system_clock(t3(2))
   diff3=t3(2)-t3(1)

  !$OMP section
   call system_clock(t4(1))
  do i=3*k+1,4*k
      do j=1,N
        Y(i)=Y(i)+A(j,i)*X(j)
      enddo
    enddo
   call system_clock(t4(2))
   diff4=t4(2)-t4(1)

 !$OMP end sections
 !$OMP end parallel
call system_clock(time(2))
  diff=time(2)-time(1)
  write(*,*) diff,diff1,diff2,diff3,diff4
    return
end

回复列表 (共20个回复)

11 楼

A数组很大,10000*10000量级的,我就是因为在parallel do的情况下感觉消耗时间比预想中的长才改成section模式的,目的就是想测一测每个核所消耗的时间……

12 楼

变量的公有私有声明加了没有?

13 楼

加了,没用。不过我做了一个测试,如果把Y声明成非allocatable值,那么消耗的时间要比原来的程序时间长很多,不过做如此修改后的并行时间却差不多比串行提高了三倍多,这是怎么回事呢?

14 楼

怎么理解, 串行时间不是原来程序的时间? 那个基准消耗时间不是不变的? 那个串行时间怎么得出来的啊?

15 楼

楼主你可不可以这样测试一下看看运行时间究竟怎样? systemclock 转换一下的成单位秒:


!cccccccccccccccc------------Subroutine MVP    Y=A*X--------------cccccccccccccccc
subroutine MVP(A,X,Y,N)
    implicit none
  integer i,j,N,k
  integer time(2), time_rate
  real :: X(N),Y(N),A(N,N)
  Y=0
  k=N/4
  ! 测试串行程序所花的时间.
  call system_clock(time(1))
  do i=1,N
    do j=1,N
      Y(i)=Y(i)+A(j,i)*X(j)
    enddo
  enddo
  call system_clock(time(2), time_rate)
  diff=time(2)-time(1)
  write(*,*) "one thread(s):", , real(diff)/real(time_rate)
  
  ! 测试parallel do所花的时间.
  call system_clock(time(1))
!$OMP parallel do default(shared) priavete(i,j)
  do i=1,N
    do j=1,N
      Y(i)=Y(i)+A(j,i)*X(j)
    enddo
  enddo
!$OMP end parallel do 
  call system_clock(time(2), time_rate)
  diff=time(2)-time(1)
  write(*,*) "parallel do(s):", , real(diff)/real(time_rate)
  
  ! 测试parallel section所花的时间.
  call system_clock(time(1))
!$OMP parallel default(shared) priavete(i,j)
!$OMP sections
 !$OMP section
  do i=1,k
    do j=1,N
      Y(i)=Y(i)+A(j,i)*X(j)
    enddo
  enddo

  !$OMP section
  do i=k+1,2*k
    do j=1,N
      Y(i)=Y(i)+A(j,i)*X(j)
    enddo
  enddo
   
  !$OMP section
  do i=2*k+1,3*k
    do j=1,N
      Y(i)=Y(i)+A(j,i)*X(j)
    enddo
  enddo

  !$OMP section
  do i=3*k+1,4*k
      do j=1,N
        Y(i)=Y(i)+A(j,i)*X(j)
      enddo
    enddo

!$OMP end sections
!$OMP end parallel
  call system_clock(time(2), time_rate)
  diff=time(2)-time(1)
  write(*,*) "section parallel(s):", real(diff)/real(time_rate)
  return
end

16 楼

由于我这个程序只是某一个大程序的子程序,实际上程序里的数组Y(N)是一个动态数组。在这种情况下,并行计算所花费的时间为串行所花费时间的一半(我将这种情况下串行所花费的时间作为基准时间);如果我将数组Y(N)的声明改一下,改成是静态的数组,那这种情况下,并行计算所花费的时间为在这种情况下串行所花费时间的四分之一。但在这种情况下,无论是并行计算还是串行计算,所花费的时间都比上述所提到的基准时间要慢很多。不知道您明白我的意思了吗?

17 楼

我把您的程序做了一点改动,我把修改后的程序和结果发给您
program main
  implicit none
  integer,parameter :: N=50000
  integer i,j,k,yy
  integer time(2), time_rate,diff
  real,allocatable :: X(:),Y(:),A(:,:)
  allocate(X(N),Y(N),A(N,N))
  
  do i=1,N
    X(i)=i
    do j=1,N
      A(j,i)=i*j
    enddo
  enddo
  Y=0
  k=N/4

  ! 测试串行程序所花的时间.
  call system_clock(time(1))
  do i=1,N
    do j=1,N
      Y(i)=Y(i)+A(j,i)*X(j)
    enddo
  enddo
  call system_clock(time(2), time_rate)
  diff=time(2)-time(1)
  yy=0
  do i=1,N
    yy=yy+Y(i)
  enddo
  write(*,*) yy
  write(*,*) "one thread(s):" , real(diff)/real(time_rate)
  
  ! 测试parallel do所花的时间.
  call system_clock(time(1))
!$OMP parallel do default(shared) private(i,j)
  do i=1,N
    do j=1,N
      Y(i)=Y(i)+A(j,i)*X(j)
    enddo
  enddo
!$OMP end parallel do 
  call system_clock(time(2), time_rate)
  diff=time(2)-time(1)
  yy=0
  do i=1,N
    yy=yy+Y(i)
  enddo
  write(*,*) yy
  write(*,*) "parallel do(s):", real(diff)/real(time_rate)
  
  ! 测试parallel section所花的时间.
  call system_clock(time(1))
!$OMP parallel default(shared) private(i,j)
!$OMP sections
 !$OMP section
  do i=1,k
    do j=1,N
      Y(i)=Y(i)+A(j,i)*X(j)
    enddo
  enddo

  !$OMP section
  do i=k+1,2*k
    do j=1,N
      Y(i)=Y(i)+A(j,i)*X(j)
    enddo
  enddo
   
  !$OMP section
  do i=2*k+1,3*k
    do j=1,N
      Y(i)=Y(i)+A(j,i)*X(j)
    enddo
  enddo

  !$OMP section
  do i=3*k+1,4*k
      do j=1,N
        Y(i)=Y(i)+A(j,i)*X(j)
      enddo
    enddo

!$OMP end sections
!$OMP end parallel
  call system_clock(time(2), time_rate)
  diff=time(2)-time(1)
  yy=0
  do i=1,N
    yy=yy+Y(i)
  enddo
  write(*,*) yy
  write(*,*) "section parallel(s):", real(diff)/real(time_rate)
  deallocate(A,X,Y)
  pause
end

one thread:0.928
parallel do:0.556
section parallel:0.546

我的PC机是Intel(R) Core(TM) i5-2300 CPU@2.8GHz 2.8GHz,内存12GB,64位win7

18 楼

发帖延迟, 后来才看到你的回复.

那就很奇怪了, 动态数组的维度跟你后来静态数组的一样大吗? 一样大的话动态数组比使用静态数组还快很多就难以理解了...

19 楼

我用你提供的代码运行.
  real :: X(N),Y(N),A(N,N)
代替
  !real,allocatable :: X(:),Y(:),A(:,:)
  !allocate(X(N),Y(N),A(N,N))
得到的时间也差不多

前者
 -2147483648
 one thread(s):   2.493900    
 -2147483648
 parallel do(s):   1.075000    
 -2147483648
 section parallel(s):   1.042100  

后者
 -2147483648
 one thread(s):   2.222100    
 -2147483648
 parallel do(s):   1.080300    
 -2147483648
 section parallel(s):   1.028600  

Linux redhat 6.0 Xeon5690 (代码我改了一点, 锁定4核, 这台机计算有人在用.) 96G内存.
确实只提升了一倍.
我觉得是内存带宽和延迟成为了瓶颈,cpu过频密访问内存而导致的. 我把程序修乱改成下面那样(已经不是你需要的计算),运行主要在cpu与cache之间交换,加速比率提高.

program main
  implicit none
  integer,parameter :: N=50000
  integer,parameter :: TD=4
  integer i,j,k,yy
  integer time(2), time_rate,diff
  real :: X(N),Y(N),A(N,N)
  
  do i=1,N
    X(i)=real(i)
    do j=1,N
      A(j,i)=real(i*j)
    enddo
  enddo
  Y=0
  k=N/4

  ! 测试串行程序所花的时间.
  call system_clock(time(1))
  do i=1,N
    do j=1,N
      Y(i)=Y(i)**5.2+(A(j,i)*X(j))**2.5   ! 计算量增大
    enddo
  enddo
  call system_clock(time(2), time_rate)
  diff=time(2)-time(1)
  write(*,*) "one thread(s):" , real(diff)/real(time_rate)
  
  Y=0
  ! 测试parallel do所花的时间.
  call system_clock(time(1))
!$OMP parallel do default(shared) private(i,j) num_threads(TD)
  do i=1,N
    do j=1,N
      Y(i)=Y(i)**5.2+(A(j,i)*X(j))**2.5  ! 计算量增大
    enddo
  enddo
!$OMP end parallel do 
  call system_clock(time(2), time_rate)
  diff=time(2)-time(1)
  write(*,*) "parallel do(s):", real(diff)/real(time_rate)
end

 one thread(s):   22.45650    
 parallel do(s):   5.834900 

加速比3.85倍. 我觉得这样应该能部分说明内存带宽和延迟导致加速比的问题, 线程数增加但带宽还是那么多.

20 楼

我试过了,确实像您说的那样是内存的问题,谢谢您!

我来回复

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