Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/fortran/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Parallel processing 为什么在启动更多进程时,基于Fortran的MPI例程需要更多的计算时间_Parallel Processing_Fortran_Mpi - Fatal编程技术网

Parallel processing 为什么在启动更多进程时,基于Fortran的MPI例程需要更多的计算时间

Parallel processing 为什么在启动更多进程时,基于Fortran的MPI例程需要更多的计算时间,parallel-processing,fortran,mpi,Parallel Processing,Fortran,Mpi,我基于Fortran的MPI代码不涉及进程之间的通信。只对每个进程进行计算并计时。我的平台是英特尔桑迪桥。代码是使用MPiFort包装器编译的。我有两个无法解释的观察结果: 1) 计算时间随着涉及更多机器而增加,其中每台机器有16个核(2个处理器,每个处理器8个核)。例如,16个MPI级别最多耗时5.74秒,32个级别最多耗时13.64秒,48个级别最多耗时18.26秒,而64个级别最多耗时25.92秒。由于不涉及任何通信,所以我希望无论如何启动名称MPI列组,都能获得相同的时间。代码包括在下面

我基于Fortran的MPI代码不涉及进程之间的通信。只对每个进程进行计算并计时。我的平台是英特尔桑迪桥。代码是使用MPiFort包装器编译的。我有两个无法解释的观察结果:

1) 计算时间随着涉及更多机器而增加,其中每台机器有16个核(2个处理器,每个处理器8个核)。例如,16个MPI级别最多耗时5.74秒,32个级别最多耗时13.64秒,48个级别最多耗时18.26秒,而64个级别最多耗时25.92秒。由于不涉及任何通信,所以我希望无论如何启动名称MPI列组,都能获得相同的时间。代码包括在下面

2) 在下面代码的步骤2中,调用了一个子例程。如果我用实际代码替换子程序调用,程序运行会更快。例如,16个MPI等级最多耗时5.63E-02秒,32个等级最多耗时0.1762秒,48个等级最多耗时0.33秒,而64个等级最多耗时0.3612秒

使用ifort编译的程序的串行版本也表现出类似的行为:使用子例程调用需要0.77秒,不使用子例程则需要5.19E-02秒

我在这里附上代码。第一个是无子例程调用,第二个是有子例程调用,第三个是子例程本身

mult.f(无子例程调用):

mult.f(带子程序)

mysubroutine(子例程调用):

使用以下命令编译代码:

mpiifort -O3 -mcmodel=large -xavx mult.f rose.f -o baseline

这里有两个截然不同的问题:

  • 为什么用子程序调用的代码比较慢;及
  • 为什么随着MPI进程数量的增加,代码会变慢
  • 我将试着(用我必须承认的胡乱猜测)按顺序回答这两个问题

    子例程速度较慢

    如果你看一下你的代码,你会发现为了增加计算的时间,你把你的实际计算包含在一个5001次迭代的
    m
    循环中。但是如果你看一下你的实际计算,你会发现,尽管你确实使用了
    dudr
    数组来更新它,但是你总是从
    dudr(t8,t4,t6,ie)=0开始。所以它以前的值是不相关的,因为您删除了它。因此,只有
    m
    循环的最后一次迭代才有任何效果

    但是为了让编译器看到这一点,它需要看到例程的主体。因此,通过调用一个子例程,您可以强制编译器实际执行5001次,而通过内联它,您可以让它有机会意识到它是毫无意义的,并且只有一次迭代才能做到这一点

    嗯,这主要是我的猜测,但我已经看到了很多次,我相信我离真相不远。事实上,由于您随后没有对
    dudr
    执行任何操作,我甚至对编译器从您的大循环中生成任何内容感到惊讶

    更多MPI进程需要更长的时间

    对于这一个,我将做一个更疯狂的猜测:我相信在某种程度上,不是在几个节点中提交,而是在一个节点上错误地提交了代码。在那里,许多MPI进程竞相访问资源(尤其是内存带宽),并且随着进程数量的增加,时间越来越长。。。我可能错了,但目前,这是我得到的最好的解释

    智慧之词


    当您尝试对性能进行基准测试时,请确保始终向编译器隐藏您对计算的实际结果不感兴趣的信息。这可以通过打印一些值或将其传递给外部例程来实现(可能什么都不做,但编译器不会知道)。否则,您可能会惊讶于它有多么聪明,它能删除多少死代码。

    您在该程序中从何处发送或接收任何消息?并行性在哪里?如果你把它全部放在程序中,那么优化者可能会发现,由于dudr在循环之后从未被引用,它可以完全优化循环结构,如果调用一个子例程,这将更加困难。如果在程序结束时将dudr写入文件,会发生什么情况?哦,请把你的fortran从80年代拖出来。至少请使用隐式无。MPI问题可能是由于1个节点上的内存带宽问题,以及多个节点上的错误进程放置。您确定所有进程都不在1个节点上吗?再次感谢您富有洞察力的回答。我想你把他们俩都钉死了。我将做一些进一步的实验,并发布研究结果。我在这个集群上使用的是mpirun而不是srun。因此,对于srun,我不再看到更多MPI进程需要更长时间的问题。非常感谢。此外,我还在代码中添加了外部例程。时间没有改变,但你的建议被采纳了。
      program mul
      include 'mpif.h'
    
      integer DIM, dim1, dim2, dim3, E
      parameter (DIM=8, E=512, dim1=DIM, dim2=DIM, dim3=DIM)
      integer ierr, rank, size
      real*8 A(dim1, dim1), B(dim2, dim2), C(dim3, dim3)
      real*8 u(dim1, dim2, dim3, E)
      real*8 dudr (dim1, dim2, dim3, E), duds (dim1, dim2, dim3, E)
      real*8 dudt (dim1, dim2, dim3, E), dudr2 (dim1, dim2, dim3)
      integer val, i, j, k, l, r, s, t, start, end
      integer iseed /3/
      real tbeg, tend
      CHARACTER(len=32) :: arg
    
      call getarg(1, arg)
      call MPI_INIT (ierr)
      call MPI_COMM_SIZE(MPI_COMM_WORLD, size, ierr)
      call MPI_COMM_RANK(MPI_COMM_WORLD, rank, ierr)
    
      ! Step 1: Initialize
      do i = 1, dim1
           do j = 1, dim1
               A (i, j) = ran(iseed);
           enddo
      enddo
    
      do i = 1, dim2
           do j = 1, dim2
               B (i, j) = A(j, i);
           enddo
      enddo
    
      do i = 1, dim3
           do j = 1, dim3
               C (i, j) = A(j, i);
           enddo
      enddo
    
      do m = 1, E
      do i=1, dim1
            do j=1, dim2
                  do k = 1, dim3
                         u (i, j, k, m) = (ran(iseed)*400.0) - 200.0
                      enddo
            enddo
      enddo
      enddo
    
      ! Step 2: Compute derivatives
      tbeg = mpi_wtime()
    
      do m = 0, 5000
          do ie=1, E
             call mysubroutine(u, C, dudr, ie)
          enddo
      enddo
      tend = mpi_wtime()
      print *, DIM, E, tend-tbeg, rank
    
      call MPI_FINALIZE (ierr)
    
      end
    
       SUBROUTINE mysubroutine(u, a, dudr, ie)
       integer DIM, dim1, dim2, dim3, E
       parameter (DIM=8, E=512, dim1=DIM, dim2=DIM, dim3=DIM)
       real*8 u(DIM, DIM, DIM, E)
       real*8 a(DIM, DIM)
       real*8 dudr (DIM, DIM, DIM, E)
         integer t8
         integer t6
         integer t4
         integer t2
         integer i
         integer j
         integer k
         integer l
       !dir$ ASSUME_ALIGNED a:    64
       !dir$ ASSUME_ALIGNED u:    64
       !dir$ ASSUME_ALIGNED dudr: 64
         DO t2=1,8
         DO t4=1,8
         DO t6=1,8
         DO t8=1,8
         dudr(t8,t4,t6,ie)
     $     =0
         dudr(t8,t4,t6,ie)=dudr(t8,t4,t6,ie)+u(t2,t4,t6,ie)
     $     *a(t8,t2)
       ENDDO
         DO t8=1,8
         dudr(t8,t4,t6,ie)=dudr(t8,t4,t6,ie)
     $     +u(t2,t4,t6,ie)*a(t8,t2)
       ENDDO
       ENDDO
       ENDDO
       ENDDO
       END
    
    mpiifort -O3 -mcmodel=large -xavx mult.f rose.f -o baseline