En grupp med säte i Houston, High Performance Fortran Forum, har därför utarbetat ett förslag i form av ett tillägg till Fortran 90. Syftet med detta projekt High Performance Fortran eller HPF är att erbjuda ett flyttbart (portabelt) språk som ger ett effektivt utnyttjande av olika parallella system. Projektet har avgett ett slutligt genomarbetat förslag i maj 1993, se litteraturlistan, och syftar mot en de facto standard, dvs ej en formell standard fastlagd av ANSI eller ISO. För att få en snabbare spridning av HPF finns även ett "HPF Subset", baserat på Fortran 77 utvidgad med en mindre del ur Fortran 90. Ett flertal tillverkare medarbetade i gruppen, som förväntas få ett snabbt genomslag av sina förslag. Eftersom gruppen med sina 45 aktiva medlemmar har arbetat mycket öppet, bland annat med en omfattande elektronisk distribution av sina arbetspapper till mer än 500 personer, så har många synpunkter från utomstående experter och framtida användare kommit in. Dessutom skedde en rejäl remissomgång av utkasten daterade november 1992 och januari 1993. De delar av förslaget som man ej har lyckats ena sig om, oftast på grund av tidsbrist, har förts till ett separat dokument kallat "Journal of Development".
Förslaget innebär ett Fortran-baserat språk för att kontrollera fördelningen av fält på parallella system med fördelat minne (eng. to control distribution of arrays onto distributed memory parallel computers) och innefattar tillägg för
CHPF$ direktiv ! Fortran 77 och fix form Fortran 90 !HPF$ direktiv ! Fortran 90 (både fix och fri form)Redan nu har bland andra Digital Equipment ett nästan fullständigt HPF system. Dessutom har Portland-gruppen en HPF Fortran 90 kompilator för flera olika system.
Senaste versionerna av rapporterna kan erhållas i PostScript eller hypertext-form (HTML). De kan även erhållas från Wien.
Den aktuella listan över tillgängliga och planerade implementationer är enligt HPFF protokoll från september 1995:
Gruppen High Performance Fortran Forum har fortsatt att utveckla HPF språket och siktar mot att publicera en ny version mot slutet av 1996. Nya egenskaper är asynkron in/utmatning, parallella processer (tasking), mer allmänna fördelningar, gränssnitt till ANSI C, reduceringsoperationer med mera. Det är troligt att själva kärnan till språket inte kommer att utvidgas särskilt mycket, men att den nya specifikationen kommer att definiera vissa standardiserade men frivilliga tillägg.
REAL A(1000), B(1000), C(1000), X(500), Y(0:501) INTEGER INX(1000) !HPF$ PROCESSORS PROCS(10) !HPF$ DISTRIBUTE A(BLOCK) ONTO PROCS !HPF$ DISTRIBUTE B(BLOCK) ONTO PROCS !HPF$ DISTRIBUTE INX(BLOCK) ONTO PROCS !HPF$ DISTRIBUTE C(CYCLIC) ONTO PROCS !HPF$ ALIGN X(I) WITH Y(I-1) A(I) = B(I) ! (1) X(I) = Y(I-1) ! (2) A(I) = C(I) ! (3) A(I) = A(I-1) + A(I) + A(I+1) ! (4) C(I) = C(I-1) + C(I) + C(I+1) ! (5) X(I) = Y(I) ! (6) A(I) = A(INX(I)) + B(INX(I)) ! (7)Vi arbetar här med 10 processorer, och har fördelat flyttalsvektorerna A och B samt heltalsvektorn INX blockvis över dessa, dvs de hundra första elementen från var och en av dem på den första processorn, de 100 följande på nästa processor, osv. Vektorn C har däremot fördelats cykliskt, så att elementen nr 1, 11, 21 osv finns på den första processorn, elementen nr 2, 12, 22 osv på den andra, osv. Utan att mer exakt ange hur vektorerna X och Y skall fördelas har vi dessutom föreskrivet att element nr I av X-vektorn skall finnas på samma processor som element nr I-1 av Y-vektorn.
Tilldelningarna i fallen (1) och (2) ovan kan därför ske utan kommunikation mellan processorerna, eftersom alla data hela tiden finns på rätt processor. Tilldelningen i fall (3) kommer normalt att ske mellan olika processorer, endast i undantagsfall finns data på samma. Tvärtom kommer det i fall (4) att endast vara i undantagsfall (vid blockgränsen) som alla tre data inte är på samma processor.
Fall (5) ser ut som fall (4), men i detta fall ligger data alltid på tre olika processorer.
För fall (6) vet vi bara att X(I) och Y(I-1) ligger på samma processor, men detta säger inget om hur X(I) och Y(I) är lagrade. Vid blockvis lagring av båda ligger de normalt på samma, vid cyklisk lagring av båda ligger de däremot alltid på olika (om man har samma cykel på båda). Vi har således ingen egentlig information om hur dessa är lagrade.
Inte heller i fall (7) vet vi så mycket. Man inser dock att A(INX(I)) och B(INX(I)) alltid är på samma processor, samt att A(I) och INX(I) likaså är det (men ej nödvändigtvis på samma som de båda tidigare nämnda elementen).
Även de formella argumenten i en funktion eller subrutin kan lagras på olika sätt med hjälp av direktiv. Antag att vi har en subrutin med två argument, som båda är flyttalsvektorer.
SUBROUTINE CAROLUS (KARL, CHARLES) REAL, DIMENSION (:) :: KARL, CHARLES !HPF$ INHERIT :: KARL !HPF$ ALIGN WITH KARL :: CHARLES ! ... END SUBROUTINE CAROLUSoch att denna anropas med
PROGRAM APP83B REAL, DIMENSION (1718) :: GUSTAV, ADOLF INTERFACE SUBROUTINE CAROLUS (KARL, CHARLES) REAL, DIMENSION (:) :: KARL, CHARLES END SUBROUTINE CAROLUS END INTERFACE ! .. CALL CAROLUS (GUSTAV, ADOLF) END PROGRAM APP83BDetta innebär att det första formella argumentet KARL fördelas över processorerna likadant som det verkliga argumentet, nämligen som GUSTAV. Det andra formella argumentet CHARLES fördelas däremot över processorer likadant som det första formella argumentet, dvs även det i verkligheten som GUSTAV, och ej som det andra verkliga argumentet ADOLF.
Ett annat exempel är när man behöver de fyra hörn-delfälten av storlek N*N från ett större fält av storlek (N+1)*(N+1). I detta fall anges det stora fältet kallat EARTH enbart som TEMPLATE, dvs formbräde, mall, mönster eller schablon. Det lagras aldrig i verkligheten, eftersom det egentligen aldrig användes!
!HPF$ TEMPLATE, DISTRIBUTE (BLOCK,BLOCK) :: EARTH (N+1,N=!) REAL, DIMENSION (N,N) :: NW, NE, SW, SE !HPF$ ALIGN NW(I,J) WITH EARTH(I,J) !HPF$ ALIGN NE(I,J) WITH EARTH(I,J+1) !HPF$ ALIGN SW(I,J) WITH EARTH(I+1,J) !HPF$ ALIGN SE(I,J) WITH EARTH(I+1,J+1)Eftersom TEMPLATE inte representerar något verkligt lagringsutrymme kan det inte heller ingå i COMMON. Riktningsindex (eng. alignment subscript) får bara vara aningen mer komplicerat än index i Fortran 66, dvs av typen m*i+n, endast en variabel i får förekomma och då endast en gång, parametrarna m och n svarar mot konstanter.
Ett något mer komplicerat direktiv är
!HPF$ ALIGN A(:) WITH D(:,*)vilket innebär att en kopia av vektorn A samordnas med varje kolumn av matrisen D. Ett exempel på detta följer. Av detta framgår dessutom att syntaxen tillåter väldigt många varianter för att skriva samma sak.
!HPF$ TEMPLATE, D1(N), D2(N,N) REAL, DIMENSION (N,N) :: X, A, B, C, AR1, AR2, P, Q, R, S !HPF$ ALIGN X(:,*) WITH D1(:) !HPF$ ALIGN (:,*) WITH D1 :: A, B, C, AR1, AR2 !HPF$ ALIGN WITH D2, DYNAMIC :: P, Q, R, SFöljande är ett mer fullständigt exempel där först en inbyggd funktion känner av det aktuella antalet processorer, vilket sedan utnyttjas vid fördelningen av fälten. Dessutom användes här en funktion.
!HPF$ TEMPLATE, D1(N), D2(N, N) REAL, DIMENSION (N, N) :: X, A, B, C, AR1, AR2, & P, Q, R, S !HPF$ ALIGN X(:,*) WITH D1(:) !HPF$ ALIGN (:,*) WITH D1 :: A, B, C, AR1, AR2 !HPF$ ALIGN WITH D2, DYNAMIC :: P, Q, R, SThe following is a more complete example, where an intrinsic function finds the present number of processors, which is used at the distribution of the arrays. In addition a usual (external) function is being used.
PROGRAM APP83C IMPLICIT NONE INTERFACE FUNCTION F(X) REAL :: F REAL, DIMENSION (:) :: X END FUNCTION F END INTERFACE REAL, DIMENSION (1000) :: X, Y, Z REAL, DIMENSION (5000) :: V, W REAL :: TEMP REAL, DIMENSION (10,1000) :: A !HPF$ PROCESSORS MPP(NUMBER_OF_PROCESSORS()) !HPF$ TEMPLATE T(1000), S(5000) !HPF$ DISTRIBUTE T(BLOCK) ONTO MPP ! Blockstorlek !HPF$ DISTRIBUTE S(CYCLIC) ONTO MPP ! kan specificeras !HPF$ ALIGN WITH T :: X, Y, Z !HPF$ ALIGN WITH S :: V, W !HPF$ ALIGN A(*,:) WITH T(:) ! Vektor av kolumner ! ... TEMP = F(V) ! ... END PROGRAM APP83C REAL FUNCTION F(X) IMPLICIT NONE REAL, DIMENSION (:) :: X !HPF$ INHERIT :: X ! Fördela formellt argument ! X som verkligt argument V REAL, DIMENSION (SIZE(X)) :: S !HPF$ ALIGN WITH X :: S ! Fördela lokalt argument S som ! formellt argument X, dvs som ! verkligt argument V ! ... F = SUM(S) ! Bara enkelt exempel RETURN END FUNCTION FMan kan specificera olika fördelningar för de olika dimensionerna i ett fält. Följande deklarationer
REAL A(100,100), B(100,100), C(200) !HPF$ DISTRIBUTE A(BLOCK,*), B(*,CYCLIC), C(BLOCK(5))medför att den första processorn vid en fyra processors-maskin lagrar följande fältsektioner
A(1:25, 1:100) B(1:100, 1:97:4) C(1:5), C(21:25), C(41:45), C(61,65), C(81:85), C(101:105), C(121:125), C(141:145), C(161,165), C(181:185),Det är likaså möjligt att fördela flera dimensioner på ett oberoende sätt
REAL D(8,100,100) !HPF$ DISTRIBUTE D(*,BLOCK,CYCLIC)medför att den första processorn vid en fyra processors-maskin konfigurerad som en 2*2 matris lagrar följande fältsektioner
D(1:8, 1:50, 1:99:2)Förutom ovanstående statiska direktiv finns även de båda dynamiska direktiven REDISTRIBUTE och REALIGN, vilka tillåter fält att ändra sin fördelning inom en subrutin. Vid användning av en subrutin kan tre olika möjligheter finnas för de formella argumenten:
FORALL (I = 1:N, J = 1:N) H(I,J) = 1.0/REAL(I+J-1) FORALL (I = 1:N, J = 1:N, Y(I,J) .NE. 0.0) & X(I,J) = 1.0/Y(I,J) FORALL (I = 1:N) A(I,I+1:N) = 3.141592654Den första av dessa definierar Hilbertmatrisen av ordning N, den andra inverterar elementen i en matris med undvikande av eventuella nollor. I den tredje tilldelas alla element över huvuddiagonalen i matrisen A ett approximativt värde på pi.
I dessa satser kan FORALL sägas vara en dubbelslinga som kan utföras i godtycklig ordning. Det allmänna utseendet är
FORALL ( v1 = l1:u1:s1, ... , vn = ln:un:sn, mask ) & a(e1, ... , em) = right_hand_sideoch beräknas enligt väl definierade regler, i princip beräknas alla index först.
Dessutom finns en FORALL-konstruktion. Denna skall tolkas som om de ingående satserna i stället vore skrivna som FORALL-satser i samma ordning. Denna restriktion är väsentlig för att få ett entydigt beräkningsresultat. I en FORALL-konstruktion är anrop av funktioner och subrutiner tillåtna om dessa är rena (eng. pure). Med direktivet PURE kan man ange att så är fallet. Först ett enkelt exempel på en FORALL-konstruktion.
REAL, DIMENSION(N,N) :: A, B ... FORALL (I = 2:N-1, J = 2:N-1) A(I,J) = 0.25*(A(I,J-1)+A(I,J+1)+A(I-1,J)+A(I+1,J)) B(I,J) = A(I,J) END FORALLEfter dessa satser har A och B exakt samma värden i alla inre punkter, medan B har kvar sina eventuella gamla värden på ränderna.
Dessutom införes ett direktiv om oberoende inom slingor av de båda typerna DO och FORALL. Detta direktiv, som helt enkelt heter !HPF$ INDEPENDENT placeras direkt efter den DO-sats eller FORALL konstruktion (dvs direkt efter inledande FORALL) och gäller fram till och med motsvarande END DO respektive END FORALL. Direktivet försäkrar kompilatorn att motsvarande del av programmet kan exekveras oberoende, dvs i godtycklig ordning eller parallellt, utan att medföra någon ändring i resultatet (ingen semantisk ändring).
I nedanstående exempel försäkras således att heltalsvektorn P inte har några upprepade värden (vilket skulle innebära att "sista vinner" tilldelningen skulle kunna bli en annan vid exekvering i annan ordning, och en konflikt vid rent parallell exekvering), samt att alla värdena P(I) ligger inom gränserna 1 och 200.
REAL, DIMENSION(200) :: A REAL, DIMENSION(100) :: B INTEGER, DIMENSION(100) :: P ... !HPF$ INDEPENDENT DO I = 1, 100 A(P(I)) = B(I) END DOMan kan även ange vilka delar av en multipelslinga som skall betraktas som oberoende. I nedanstående exempel så är den innersta slingan inte oberoende eftersom ett visst element i A tilldelas ett upprepat antal gånger.
REAL, DIMENSION(:, :, :), ALLOCATABLE :: A, B, C ... ALLOCATE (A(N,N,N)) ALLOCATE (B(N,N,N)) ALLOCATE (C(N,N,N)) ... !HPF$ INDEPENDENT (I1, I2, I3) DO I1 = 1, N1 DO I2 = 1, N2 DO I3 = 1, N3 DO I4 = 1, N4 ! The innermost loop is NOT ! independent ! A(I1, I2, I3) = A(I1, I2, I3) & + B(I1, I2, I4) * C(I2, I3, I4) END DO END DO END DO END DOEn DO-slinga och en FORALL-konstruktion med samma innehåll är ekvivalenta om de båda har getts ett INDEPENDENT direktiv.
0 -5 8 -3 A = 3 4 -1 2 1 5 6 -4ger följande värden
MAXLOC(A) = (/ 1, 3 /) MAXLOC(A, DIM = 1) = (/ 2, 3, 1, 2 /) MAXLOC(A, DIM = 2) = (/ 3, 2, 3 /)Följande helt nya funktioner tillkommer. Avsikten är att förfrågningsfunktionerna blir verkliga inbyggda funktioner, medan de övriga eventuellt i stället kommer att finnas i ett bibliotek (som externa funktioner).
De följande funktionerna är ej inbyggda utan finns i ett normalt bibliotek och kräver därför en USE-sats före användning. Jag har här valt att inte ge en detaljerad beskrivning, utan räknar bara upp dessa. För definitioner hänvisar jag till standarden.
Reduceringsfunktionerna IALL, IANY, IPARITY och PARITY finns och de svarar mot de i Fortran 90 inbyggda logiska funktionerna IAND, IOR, IEOR och operatorn .NEQV. Ett stort antal funktioner finns för att kombinera ihop och sprida ut data, de har namn av formen XXX_SCATTER, där XXX kan vara någon av SUM, COUNT, PRODUCT, MAXVAL, MINVAL, IALL, IANY, IPARITY, ALL, ANY och PARITY. För parallella operationer finns funktioner med namnen XXX_PREFIX och XXX_SUFFIX, där XXX har samma möjligheter som för XXX_SCATTER. Exempel: SUM_PREFIX summerar successivt talen i fältet, det första blir oförändrat, det andra blir summan av de två första, osv, medan SUM_SUFFIX gör det baklänges.
Det finns dessutom två sorteringsfunktioner, GRADE_UP och GRADE_DOWN. Däremot finns det för närvarande inga operationer i HPF för parallell in- eller utmatning (förutom i Journal of Development).
Man talar i Fortran om lagringsassociering (eng. storage association) och ordningsassociering (eng. sequence association). Fortranstandarden säger i (14.6.3.1) att lagringsassociering är den associering av två eller flera dataobjekt som förekommer när två eller flera följder av data delar en eller flera lagringsenheter, och i (12.4.1.4) att ordningsassociering avser den ordning för fältelement som Fortran kräver då ett fältuttryck associeras med ett formellt argument: Rang och mönster hos det verkliga argumentet behöver inte överensstämma med rang och mönster hos det formella argumentet.
Notera att HPF klarar det fall att fält, använda som argument, är fördelade över processorer, i det fall att verkligt argument och formellt argument båda har samma rang och mönster. Det är det (relativt vanliga) fallet att man tricksar i Fortran (dvs. utnyttjar de möjligheter som finns och är tillåtna) som ställer till problem i HPF. Om man har en subrutin som innehåller deklarationerna
SUBROUTINE HEMMA(X) DIMENSION X(20,10)så kan denna anropas med CALL HEMMA(Y(2,1)) endast om fältet X är deklarerat sekventiellt i subrutinen HEMMA och samtidigt fältet Y är deklarerat sekventiellt i den anropande rutinen. Det alternativa anropet CALL HEMMA(Y) är tillåtet om antingen X och Y båda är sekventiella eller om de båda är dimensionerade exakt lika.
Ett direktiv SEQUENCE har införts för att explicit tala om för systemet att en variabel eller ett COMMON-block skall betraktas som att vara rent sekventiell. Under HPF gäller normalt att alla COMMON-block behandlas som icke-sekventiella, medan variabler (utom i ett par specificerade undantagsfall, som när variabeln ingår i ett sekventiellt COMMON-block eller är ett fält med antagen dimension) likaså normalt är icke-sekventiella.
För att få alla "gamla" program att ge samma resultat under HPF som under Fortran rekommenderas följande att ingå i implementationer av HPF.
Denna katalog innehåller:
Sänd ett brev till Theresa Chatman, CITI/CRPC, Mail Stop 41, Rice University, 6100 Main Street Houston, TX 77005, USA; eller e-post till tlc@cs.rice.edu för att begära ett exemplar av CRPC-TR-92225. Det är en avgift på $50.00 för att täcka kopiering och porto.