主题:OpenMP中某段程序的并行时间与串行时不一样
saupt005
[专家分:0] 发布于 2012-03-07 17:15:00
各位老师:
我在学习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 楼
saupt005 [专家分:0] 发布于 2012-03-08 14:17:00
A数组很大,10000*10000量级的,我就是因为在parallel do的情况下感觉消耗时间比预想中的长才改成section模式的,目的就是想测一测每个核所消耗的时间……
12 楼
yeg001 [专家分:14390] 发布于 2012-03-08 15:48:00
变量的公有私有声明加了没有?
13 楼
saupt005 [专家分:0] 发布于 2012-03-08 18:19:00
加了,没用。不过我做了一个测试,如果把Y声明成非allocatable值,那么消耗的时间要比原来的程序时间长很多,不过做如此修改后的并行时间却差不多比串行提高了三倍多,这是怎么回事呢?
14 楼
yeg001 [专家分:14390] 发布于 2012-03-08 21:23:00
怎么理解, 串行时间不是原来程序的时间? 那个基准消耗时间不是不变的? 那个串行时间怎么得出来的啊?
15 楼
yeg001 [专家分:14390] 发布于 2012-03-08 21:54:00
楼主你可不可以这样测试一下看看运行时间究竟怎样? 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 楼
saupt005 [专家分:0] 发布于 2012-03-08 22:34:00
由于我这个程序只是某一个大程序的子程序,实际上程序里的数组Y(N)是一个动态数组。在这种情况下,并行计算所花费的时间为串行所花费时间的一半(我将这种情况下串行所花费的时间作为基准时间);如果我将数组Y(N)的声明改一下,改成是静态的数组,那这种情况下,并行计算所花费的时间为在这种情况下串行所花费时间的四分之一。但在这种情况下,无论是并行计算还是串行计算,所花费的时间都比上述所提到的基准时间要慢很多。不知道您明白我的意思了吗?
17 楼
saupt005 [专家分:0] 发布于 2012-03-08 22:52:00
我把您的程序做了一点改动,我把修改后的程序和结果发给您
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 楼
yeg001 [专家分:14390] 发布于 2012-03-08 22:54:00
发帖延迟, 后来才看到你的回复.
那就很奇怪了, 动态数组的维度跟你后来静态数组的一样大吗? 一样大的话动态数组比使用静态数组还快很多就难以理解了...
19 楼
yeg001 [专家分:14390] 发布于 2012-03-09 00:18:00
我用你提供的代码运行.
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 楼
saupt005 [专家分:0] 发布于 2012-03-09 13:04:00
我试过了,确实像您说的那样是内存的问题,谢谢您!
我来回复