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