Content |
C und Fortran Funktionen
Wir beginnen mit
-
einem Fortran Hauptprogramm und
-
einer Fortran Subroutine
Das Fortran Hauptprogramm ruft die Subroutine auf und gibt ein davon abhängiges Ergebnis aus.
Dann ersetzen die Fortran Routine mit einer C Funktion. Das Fortran Programm wird das gar nicht merken.
Reines Fortran Beispiel
Hauptprogramm
* Externe Funktionen, die aufgerufen werden
DOUBLE PRECISION DSUM
* Wo wird I und X im Speicher abgelegt?
INTEGER I
DOUBLE PRECISION X(10), ALPHA
* Vektor X mit etwas pseudo wichtigem initialisieren
DO 10 I = 1, 10
X(I) = I ** 2
10 CONTINUE
WRITE (*,*) 'X =', X
ALPHA = DSUM(10, X, 1)
WRITE (*,*) 'ALPHA = DSUM(10, X, 1) =', ALPHA
ALPHA = DSUM(5, X, 2)
WRITE (*,*) 'ALPHA = DSUM(5, X, 2) =', ALPHA
ALPHA = DSUM(5, X(2), 2)
WRITE (*,*) 'ALPHA = DSUM(5, X(2), 2) =', ALPHA
END
Subroutine
* Definition der Argumente
* .. Skalare Argumente
INTEGER N, INCX
* .. Array Argument
DOUBLE PRECISION X(*)
* Lokale Variablen (Werden auf dem Stack angelegt)
INTEGER I, NINC
* Parameter (Wo werden die angelegt?)
DOUBLE PRECISION Zero
PARAMETER ( ZERO = 0.0D+0 )
* Wir beginnt die eigentliche Arbeit
DSUM = Zero
NINC = N*INCX
DO 10 I = 1, NINC, INCX
DSUM = DSUM + X(I)
10 CONTINUE
RETURN
END
Übersetzen, Linken und ausführen
Wir übersetzten die Fortran Source Dateien einzeln und erzeugen zunächst nur Object Files. Bei diesen kann man dann auch mit otool prüfen, wo genau Variablen angelegt wurden.
$shell> gfortran -Wall -fimplicit-none -c dsum.f $shell> gfortran -Wall -fimplicit-none -c sumtest.f
Dann Linken wir diese zu einer ausführbaren Datei (hier werden heimlich noch System Bibliotheken zu den Object Files dazugelinkt):
$shell> gfortran dsum.o sumtest.o
Jetzt kann man testen was das Ganze tut:
$shell> ./a.out
X = 1.0000000000000000 4.0000000000000000 9.0000000000000000 16.000000000000000 25.000000000000000 36.000000000000000 49.000000000000000 64.000000000000000 81.000000000000000 100.00000000000000
ALPHA = DSUM(10, X, 1) = 385.00000000000000
ALPHA = DSUM(5, X, 2) = 165.00000000000000
ALPHA = DSUM(5, X(2), 2) = 220.00000000000000
Aufgabe:
-
Findet mit otool heraus, wie die Funktion auf Assembler Ebene heisst.
-
Wie werden die Argumente übergeben? Call by value oder call by reference?
-
In der Praxis schaut man das schnell mit nm nach. Lest nach was nm tut und probiert es aus!
Fortran Funktion mit C-Funktion ersetzen
Mit folgender C-Funktion kann man die Fortran Routine ersetzen:
dsum_(int *n, double *x, int *incX)
{
double result = 0.0;
int i, nInc;
nInc = (*n) * (*incX);
for (i=0; i<nInc; i+=(*incX)) {
result += x[i];
}
return result;
}
Wir erzeugen für die C-Funktion das Object File
$shell> gcc-4.8 -Wall -c new_dsum.c
Und linken mit dem Object File des Hauptporgramms
$shell> gfortran new_dsum.o sumtest.o
Jetzt kann man wieder testen ob das Ganze tut:
$shell> ./a.out
X = 1.0000000000000000 4.0000000000000000 9.0000000000000000 16.000000000000000 25.000000000000000 36.000000000000000 49.000000000000000 64.000000000000000 81.000000000000000 100.00000000000000
ALPHA = DSUM(10, X, 1) = 385.00000000000000
ALPHA = DSUM(5, X, 2) = 165.00000000000000
ALPHA = DSUM(5, X(2), 2) = 220.00000000000000
Makefile
Schaut euch folgendes (viel zu primitives) Makefile an:
-
Mit make sum_f77 soll die reine Fortran Variante übersetzt und gelinkt werden. Die ausführbare Datei soll sum_f77 heissen.
-
Mit make sum_c soll die gemischte Fortran/C Variante übersetzt und gelinkt werden. Die ausführbare Datei soll sum_c heissen.
-
Mit make sollen beide Varianten erzeugt werden.
-
Mit make clean sollen alle Object Files und ausführbaren Dateien gelöscht werden.
-
Wenn ein Object File bereits existiert, dann soll es nur neu erzeugt werden, wenn sich das zugehörige Source File geändert hat.
-
Eine ausführbare Datei muss nur neu erzeugt (bzw. neu gelinkt) werden, wenn sich ein Object File geändert hat.
sum_f77: dsum.o sumtest.o
gfortran -Wall -fimplicit-none -o sum_f77 dsum.o sumtest.o
sum_c: new_dsum.o sumtest.o
gfortran -Wall -fimplicit-none -o sum_c new_dsum.o sumtest.o
new_dsum.o: new_dsum.c
gcc-4.8 -Wall -c new_dsum.c
dsum.o: dsum.f
gfortran -Wall -fimplicit-none -c dsum.f
sumtest.o: sumtest.f
gfortran -Wall -fimplicit-none -c sumtest.f
clean:
rm -f *.o sum_f77 sum_c
Aufgabe: Verbessert es
-
Prüft, ob das Makefile tut was es soll!
-
Zweimal hintereinander make aufrufen.
-
Zeitstempel eines Source Files ändern und nochmals make aufrufen
-
Object File löschen und nochmals make aufrufen.
-
...
-
-
Variablen für C-Compiler und Fortran-Compiler
-
Variablen für C bzw. Fortran Source Files ...
-
Variablen für Compiler Flags, Linker, ...
-
Regeln für das Erzeugen von Object Files.
-
Wieso rm -f und nicht nur rm?