Přeskočit na obsah

Coarry fortran

Fortran 2008 zavádí interní příkazy pro paralelizaci programu a konstrukci coarry. Kolem paralelního programování je opět mnoho teorie a proto vás asi nepřekvapí, když napíši že tato část je spíše pro ty, kteří mají s paralelním programováním již nějakou zkušenost.

Fortran 2008 není příliš bohatý na příkazy, které se pojí s paralelními výpočty. Většina z nich je následujím textu. Technická specifikace TS18508 z roku 2015, která se stala součástí nového standardu v roce 2018 přišla s novymi možnostmi. Nové prvky z roku 2018 ovšem zatím nejsou součástí většiny kompilátorů.

Coarry Fortran operuje s tzv obrazy. To jsou paralelní běhy programu. Každý obraz běží nezávisle na ostatních. Všechny proměnné jsou lokální a patří dannému obrazu.

Kompilace s Itel Fortran

Testovací soubor: helloWorld.f90

        
    ifort -coarray -coarray-num-images=4 helloWorld.f90
    ./a.out
    					

Výsledkem bude:

        
    Hello World
    Hello World
    Hello World
    Hello World
    					

Kompilace s GFortran

V mém případě nebyly paralelní programy kompilovane pomoci gfortranu vždy stabilní a ne vždy se chovaly tak jak by se dalo očekávat. Pokud se rozhodnete používat GFortran ověřte zda alespoň ukázkové programy dělají co mají.

Testovací soubor: helloWorld.f90

Info:

        
    gfortran -fcoarray=lib helloWorld.f90 -lcaf_mpi
    mpirun -n 4 ./a.out
    					

Výsledkem bude:

        
    Hello World
    Hello World
    Hello World
    Hello World
    					

Obrazy

Každá obraz zná své číslo (this_image()) a celkový počet obrazů (num_images()).

Všechny obrazy běží zároveň a ten který dříve příjde ten první vypíše na obrazovku, proto výsledkem příštího příkazu budou různě uspořádené výpisy z obrazů. Všechny obrazy mohou vypisovat, ale pouze obraz č. 1 může používat read(*,*) a některé další funkce.

Pokud chceme program předčasně ukončit měli bychom spíše používat příkaz error stop než klasické stop.

        
    program coarray
      use iso_fortran_env
      implicit none
      
      integer :: i, n = 0
    
      write(*,*) "Tohle je obraz č. ",this_image()," z ", num_images()
    
      i = this_image()
      
      if(this_image() == 1) then
        write(*,*) "zadej n:"
        read(*,*) n
      end if
      
      i = i + n
      write(*,*) "V obrazu č. ",this_image()," se i = ",i
     
    end program coarray
    

Synchronizace obrazů

Obrazy synchronizujeme pomocí sync all. Tímto příkazem zařídíme, že na sebe všechny obrazy počkají.

        
    program coarray
      use iso_fortran_env
      implicit none
      
      integer :: i, n = 0
    
      write(*,*) "Tohle je obraz č. ",this_image()," z ", num_images()
      sync all
      
      i = this_image()
      
      if(this_image() == 1) then
        write(*,*) "zadej n:"
        read(*,*) n
      end if
      
      i = i + n
      
      sync all
      write(*,*) "V obrazu č. ",this_image()," se i = ",i
     
    end program coarray
    

Nyní by měl výsledek vypadet nějak takto:

        
     Tohle je obraz č.            1  z            4
     Tohle je obraz č.            2  z            4
     Tohle je obraz č.            3  z            4
     Tohle je obraz č.            4  z            4
     zadej n:
    5
     V obrazu č.            1  se i =            6
     V obrazu č.            2  se i =            2
     V obrazu č.            3  se i =            3
     V obrazu č.            4  se i =            4
    				

Coarray

Coarray jsou pole které mají co-dimenzi. Každá proměnná v obrazu patří jen dannému obrazu. Díky codimenzi můžeme proměnné či poli přidělit logickou strukturu indexů a odkazovat se na paralelní verze proměnných. Codimenzi píšeme do hranatých závorek a poslední index musí být vždy *. Pokud při práci s coarray nezadáme souřadnici, automaticky používáme codimenzi příslušící danému obrazu. Kodimenzí může být víc než je obrazů, ale potom ne všechny codimenze přísluší nějakému obrazu.

Zkuste v následujícím příkladě zakomentovat poslední sync all. Mělo by dojít k výpisu z obrazů > 1 dříve než vůbec zadáte hodnotu n.

        
    program coarray
      use iso_fortran_env
      implicit none
      
      integer :: i, n = 0
      
      integer :: scalar
      integer :: co_scalar[*]
      integer :: co_array1(5)[-1:*]
      integer,codimension[2,*] :: co_array2(5)
      integer,allocatable :: co_array_alloc(:)[:,:]
      
      allocate(co_array_alloc(0:num_images())[0:1,0:*])
    
      write(*,*) "Tohle je obraz č. ",this_image()," z ", num_images()
      sync all
      
      co_scalar = this_image()
      
      
      if(this_image() == 1) then
        write(*,*) "zadej n:"
        read(*,*) n
        
        do i=1,num_images()
          co_scalar[i] = co_scalar[i] + n
        end do
      end if
      
      sync all
      write(*,*) "V obrazu č. ",this_image()," se i = ",co_scalar
    
    end program coarray
    

Zajímavý test kompilátoru je v následujícím příkladě. Zatímco jsou obrazy > 1 zaměstnány generováním velkého množství náhodných čísel má obraz 1 dost času na to, aby vypsal všechny hodnoty. Kompilárot Íntel Fortran 19.0.1.144 v mém případě čeká až obrazy > 1 udělají co je třeba a až potom vipíše hodnotu tj. výsledek je 1 2 3 4. GFortran 7.3.0 v mém případě vypíše 1 0 0 0 tj. na nic nečeká a vypíše jak to je. Řešením je synchronizace před výpisem.

        
    program coarray
      use iso_fortran_env
      implicit none
      
      integer :: i, n = 0
      
      integer :: scalar
      integer :: co_scalar[*]
    
      write(*,*) "Tohle je obraz č. ",this_image()," z ", num_images()
      sync all
      
      co_scalar = 0
    
      if(this_image()/=1) then
        call loop(100000000)
      end if
      
      co_scalar = this_image()
      
      if(this_image()==1) then
        do i=1,num_images()
          write(*,*) i,co_scalar[i]
        end do
      end if
    
    contains
    !--------------------------------------
    subroutine loop(a)
      integer :: i,a
      real :: g
      do i=1,a
        call random_number(g)
      enddo
    end subroutine
    end program coarray
    				

Funkce s coarray

Funkce this_image můžeme mít jako argument coarray. V tomto případě vrátí pole indexu odkazujících na codimenzi daného obrazu.

Image_index má jako argument coarray a sub=[pole indexů], kde pole indexů jsou souřadnice codimenze. Výsledkem je číslo obrazu, kterému tado adresa v tomto coarray patří.

Jaká je horní a dolní mez codimenze lze zjistit pomoci lcobound() a ucobound().

        
    	program coarray
    	  use iso_fortran_env
    	  implicit none
    	 
    	  integer :: co_array1(5)[-1:*]
    	  integer,codimension[2,*] :: co_array2(5)
    
    	  
    	  write(*,'(a,1x,i2,1x,a,*(i2,1x))') "ja jsem obraz č.",this_image(),&
    	  " a codimenze co_array2 ktera mi patri je ",this_image(co_array2)
    	  
    	  sync all
    	  if(this_image() == 1) write(*,*) ""
    	  
    	  write(*,'(a,1x,i2)') "codimenze [2,1] patri obrazu ",image_index(co_array2,sub=[2,1])
    	  
    	  sync all
    	  if(this_image() == 1) write(*,*) ""
    	  
    	  write(*,*) "lcobound",lcobound(co_array1)
    	  write(*,*) "ucobound",ucobound(co_array1)
    	  
    	end program coarray