The answer is straightforward but detailed.
| pgf90: | -lpgf90 -lpgf90_rpm1 -lpgf902 -lpgf90rtl -lpgftnrtl -lm -lpgc -lgcc |
| pghpf: | -lpghpf -lpghpf_rpm -lpghpf2 -lpgftnrtl -lm -lpgc -lgcc -lc -lgcc |
main.c:
extern "C" void foo_ ( int * );
extern "C" void pghpf_init(int *);
int __argc_save;
char **__argv_save;
static int zz = 0;
main(int argc, char **argv)
{
int i;
__argc_save = argc;
__argv_save = argv;
pghpf_init(&zz);
i = 19;
foo_(&i);
}
foo.f90:
subroutine foo (i)
integer i
print *, i
end subroutine foo
pgf90 -c foo.f90
g++ main.c foo.o -L/usr/pgi/linux86/lib
-I/usr/pgi/linux86/include -lpgf90 -lpgf90_rpm1
-lpgf902 -lpgf90rtl -lpgftnrtl -lm -lpgc -lgcc
(assumes compilers installed in /usr/pgi)
In the case of code compiled with pgf77, the similar example is as follows:
main.c:
extern "C" void foo_ ( int * );
int __argc_save;
char **__argv_save;
main(int argc, char **argv)
{
int i;
__argc_save = argc;
__argv_save = argv;
i = 19;
foo_(&i);
}
foo.f:
subroutine foo (i)
integer i
print *, i
end
pgf77 -c foo.f
g++ main.c foo.o -L/usr/pgi/linux86/lib
-I/usr/pgi/linux86/include -lpgftnrtl -lpgc
The follow up question is
Only the f77-style of passing arrays to an f90 routine from c++ will work. E.g., the dummy arrays of the f90 routine called from c/c++/f77 cannot be assumed shape or f90 pointer.
Example:
C/C++
float a[100];
int n = 100;
foo_(a,&n);
...
F90
subroutine foo(a,n)
real a(:) ! does not work - a cannot be assumed shape
...
subroutine foo(a,n)
real a(n) ! should work
What about passing command line arguments (argc, argv) from gcc programs to pgf77 compiled code?
Again remember that pgi compilers use different globals than gcc does.
% more cmain.c
int __argc_save;
char **__argv_save;
main(int argc, char **argv)
{
char bool1, letter1;
int numint1, numint2;
float numfloat1;
double numdoub1;
short numshor1;
extern void forts_ ();
int i;
__argc_save = argc;
__argv_save = argv;
for (i=0; i < argc; i++){
printf("main: command line arg %d is %s\n",i,argv[i]);
}
forts_(&bool1,&letter1,&numint1,&numint2,&numfloat1,
&numdoub1,&numshor1, 1);
printf("main: %s %c %d %d %3.1f %.0f %d\n",
bool1?"TRUE":"FALSE",letter1,numint1,
numint2, numfloat1, numdoub1, numshor1);
}
% more forts.f
subroutine forts ( bool1, letter1, numint1,
& numint2, numfloat1, numdoub1, numshor1)
character*(128) arg
logical*1 bool1
character letter1
integer numint1, numint2
double precision numdoub1
real numfloat1
integer*2 numshor1
integer i,j
bool1 = .true.
letter1 = "v"
numint1 = 11
numint2 = -44
numdoub1 = 902
numfloat1 = 39.6
numshor1 = 299
i = iargc()
print *,"forts: argc=",i
do j=0,i
call getarg(j,arg)
print *,"forts: arg ",j," is ", arg
end do
return
end
% gcc -c cmain.c
% pgf77 -o c2f -Mnomain cmain.o forts.f
Linking:
% c2f 123 456 momma
main: command line arg 0 is c2f
main: command line arg 1 is 123
main: command line arg 2 is 456
main: command line arg 3 is momma
forts: argc= 3
forts: arg 0 is c2f
forts: arg 1 is 123
forts: arg 2 is 456
forts: arg 3 is momma
main: TRUE v 11 -44 39.6 902 299
Here are some examples. See Chapter 10 of the User's Guide for more information.
% more fmain.f
program fort2c
logical*1 bool1
character letter1
integer*4 numint1, numint2
real numfloat1
double precision numdoub1
integer*2 numshor1
external cfunc
call cfunc (bool1, letter1, numint1, numint2,
& numfloat1, numdoub1, numshor1)
write( *, 100)
& bool1, letter1, numint1, numint2, numfloat1,
& numdoub1, numshor1
100 format(1x,"bool1 = ", L2,/,
& " letter1 = ", A2,/,
& " numint1 = ", I5,/,
& " numint2 = ", I5,/,
& " numfloat1 = ", F6.1,/,
& " numdoub1 = ", F6.1,/,
& " numshor1 = ", I5,/)
end
% more csub.c
#define TRUE 0xff
#define FALSE 0
void
cfunc_( bool1, letter1, numint1, numint2, numfloat1,\
numdoub1, numshor1, len_letter1)
char *bool1, *letter1;
int *numint1, *numint2;
float *numfloat1;
double *numdoub1;
short *numshor1;
int len_letter1;
{
*bool1 = TRUE;
*letter1 = 'v';
*numint1 = 11;
*numint2 = -44;
*numfloat1 = 39.6 ;
*numdoub1 = 39.2 ;
*numshor1 = 981;
}
%pgcc -c csub.c
%pgf77 fmain.f csub.o
% a.out
bool1 = T
letter1 = v
numint1 = 11
numint2 = -44
numfloat1 = 39.6
numdoub1 = 39.2
numshor1 = 981
% more cmain.c
main ()
{
char bool1, letter1;
int numint1, numint2;
float numfloat1;
double numdoub1;
short numshor1;
extern void forts_ ();
forts_(&bool1,&letter1,&numint1,&numint2,&numfloat1,
&numdoub1,&numshor1, 1);
printf(" bool1 = %s\n letter1 = %c\n numint1 = %d \n numint2 = %d \n numfloat1 = %3.1f \n
numdoub1 = %.0f \n numshor1 = %d\n",
bool1?"TRUE":"FALSE",letter1,numint1,
numint2, numfloat1, numdoub1, numshor1);
}
% more fsub.f
subroutine forts ( bool1, letter1, numint1,
& numint2, numfloat1, numdoub1, numshor1)
logical*1 bool1
character letter1
integer numint1, numint2
double precision numdoub1
real numfloat1
integer*2 numshor1
bool1 = .true.
letter1 = "v"
numint1 = 11
numint2 = -44
numdoub1 = 902
numfloat1 = 39.6
numshor1 = 299
return
end
% pgcc -c cmain.c
% pgf77 -Mnomain cmain.o fsub.f
Linking:
% a.out
bool1 = TRUE
letter1 = v
numint1 = 11
numint2 = -44
numfloat1 = 39.6
numdoub1 = 902
numshor1 = 299
% more cpmain.C
#include <iostream>
extern "C" {
extern void forts_ ( char *, char *, int *, int *, float *, double *, short * );
}
main ()
{
char bool1, letter1;
int numint1, numint2;
float numfloat1;
double numdoub1;
short numshor1;
forts_(&bool1, &letter1, &numint1, &numint2, &numfloat1, &numdoub1, &numshor1);
cout << " bool1 = ";
bool1?cout << "TRUE ":cout << "FALSE "; cout << endl;
cout << " letter1 = " << letter1 << endl;
cout << " numint1 = " << numint1 << endl;
cout << " numint2 = " << numint2 << endl;
cout << " numfloat1 = " << numfloat1 << endl;
cout << " numdoub1 = " << numdoub1 << endl;
cout << " numshor1 = " << numshor1 << endl ;
}
% more fsub.f
subroutine forts ( bool1, letter1, numint1,
& numint2, numfloat1, numdoub1, numshor1)
logical*1 bool1
character letter1
integer numint1, numint2
double precision numdoub1
real numfloat1
integer*2 numshor1
bool1 = .true.
letter1 = "v"
numint1 = 11
numint2 = -44
numdoub1 = 902
numfloat1 = 39.6
numshor1 = 299
return
% pgf77 -c fsub.f
% pgCC cpmain.C fsub.o
cpmain.C:
Linking:
% a.out
bool1 = TRUE
letter1 = v
numint1 = 11
numint2 = -44
numfloat1 = 39.6
numdoub1 = 902
numshor1 = 299
% more fmain.f
logical*1 bool1
character letter1
integer*4 numint1, numint2
real numfloat1
double precision numdoub1
integer*2 numshor1
external cfunc
call cplus_func (bool1, letter1, numint1,
& numint2, numfloat1, numdoub1, numshor1)
write( *, 100)
& bool1, letter1, numint1, numint2, numfloat1,
& numdoub1, numshor1
100 format(1x,"bool1 = ", L2,/,
& " letter1 = ", A2,/,
& " numint1 = ", I5,/,
& " numint2 = ", I5,/,
& " numfloat1 = ", F6.1,/,
& " numdoub1 = ", F6.1,/,
& " numshor1 = ", I5,/)
end
% more cpsub.C
#define TRUE 0xff
#define FALSE 0
extern "C" {
extern void cplus_func_ (
char *bool1,
char *letter1,
int *numint1,
int *numint2,
float *numfloat1,
double *numdoub1,
short *numshort1,
int len_letter1) {
*bool1= TRUE;
*letter1 = 'v';
*numint1 = 11;
*numint2 = -44;
*numfloat1 = 39.6;
*numdoub1 = 39.2;
*numshort1 = 981;
}
}
% pgCC -c cpsub.C
cpsub.C:
% pgf77 fmain.f cpsub.o
Linking:
% a.out
bool1 = T
letter1 = v
numint1 = 11
numint2 = -44
numfloat1 = 39.6
numdoub1 = 39.2
numshor1 = 981
% more cmain.c
extern void func2_cplus(int a, int b, int *c);
#include <stdio.h>
main()
{
int a,b,c;
a=8;
b=2;
printf("main: a = %d b = %d ptr c = %x\n",a,b,&c);
func2_cplus(a,b,&c);
printf("main: res = %d\n",c);
}
% more cpsub.C
#include <iostream>
extern "C" void func2_cplus(int num1,int num2,int *res)
{
cout << "sub: a = " << num1 << " b = " << num2 << " ptr c = " << res << endl << endl;
*res=num1/num2;
cout << "sub: res = " << *res << endl ;
}
% pgcc -c cmain.c
% pgCC cmain.o cpsub.C
cpsub.C:
Linking:
% a.out
main: a = 8 b = 2 ptr c = bffffca4
sub: a = 8 b = 2 ptr c = 0xbffffca4
sub: res = 4
main: res = 4
% more cpmain.C
extern "C" void func1_cplus(int n, int m, int *p);
#include <iostream>
main()
{
int a,b,c;
a=8;
b=2;
cout << "main: a = " << a << " b = " << b << " ptr c = " << &c << endl;
func1_cplus(a,b,&c);
cout << "main: res = " << c << endl;
}
% more csub.c
#include <stdio.h>
void func1_cplus(num1, num2, res)
int num1, num2, *res;
{
printf("sub: a = %d b = %d ptr c = %x\n",num1,num2,res);
*res=num1/num2;
printf("\nsub: res = %d\n",*res);
}
% pgcc -c csub.c
% pgCC cpmain.C csub.o
cpmain.C:
Linking:
% a.out
main: a = 8 b = 2 ptr c = 0xbffffca4
sub: a = 8 b = 2 ptr c = bffffca4
sub: res = 4
main: res = 4
In summary, you need a function prototype in your fortran program if the C function returns a value. Next you need to explicitly pass the length of the string to the C function. In the example below we pass in the string followed by its length using the len_trim intrinsic. By default values are passed by reference, so the C function receives the addresses of the actual arguments...
First the HPF:
program p
integer*4 call_c ! C function prototype
character(len=128) :: my_string
my_string = 'this is a test 1234567890'
ierr=call_c(my_string,len_trim(my_string))
print *, ierr
end
Here's the C:
int call_c_(char *string, unsigned *len){
char * c_string;
c_string = (char*) malloc(sizeof(char)*((*len)+1));
memcpy((void*) c_string, (const void*) string, *len);
c_string[*len] = '\0';
printf("string = %s\n",c_string);
free(c_string);
return 0;
}
% more fmain.f
program fmain
integer i
i= 19
call f90sub(i)
end
% more f90sub.f90
subroutine f90sub (i)
include 'lib3f.h'
character(32) argstring
integer i
print *, i
print *, "iargc = ", iargc()
do j=0,iargc()
call getarg(j,argstring)
print *,"arg",j," is ",argstring
end do
end subroutine f90sub
% pgf77 -c fmain.f
% pgf90 -o f2f90 f90sub.f90 fmain.o
f90sub.f90:
Linking:
% f2f90 is a good call 123
19
iargc = 5
arg 0 is f2f90
arg 1 is is
arg 2 is a
arg 3 is good
arg 4 is call
arg 5 is 123
% more f90main.f90
program f90main
interface
extrinsic (f77_local) subroutine f77sub(i)
integer , intent(in) :: i
end subroutine f77sub
end interface
integer i
i= 19
call f77sub(i)
end
% more f77sub.f
subroutine f77sub (i)
character*32 argstring
integer i
print *, i
print *, "iargc = ", iargc()
do j=0,iargc()
call getarg(j,argstring)
print *,"arg",j," is ",argstring
end do
end
% pgf77 -c f77sub.f
% pgf90 -o f902f f90main.f90 f77sub.o
f90main.f90:
Linking:
% f902f is a good call also 123
19
iargc = 6
arg 0 is f902f
arg 1 is is
arg 2 is a
arg 3 is good
arg 4 is call
arg 5 is also
arg 6 is 123
Here is an example of an hpf program calling a c, f77, f90, hpf, and C++, program, and even a subroutine compiled.
pghpf -Mf90 -c
% more hpfmain.f
program hpf2all
logical*1 bool1
character letter1
integer*4 numint1, numint2
real numfloat1
double precision numdoub1
integer*2 numshor1
interface
extrinsic (f77_local) subroutine csub(
& bool1, letter1, numint1, numint2,
& numfloat1, numdoub1, numshor1)
logical*1, intent(out) :: bool1
character, intent(out) :: letter1
integer*4, intent(out) :: numint1, numint2
real, intent(out) :: numfloat1
double precision, intent(out) :: numdoub1
integer*2, intent(out) :: numshor1
end subroutine csub
end interface
interface
extrinsic (f77_local) subroutine f77sub(
& bool1, letter1, numint1, numint2,
& numfloat1, numdoub1, numshor1)
logical*1, intent(out) :: bool1
character, intent(out) :: letter1
integer*4, intent(out) :: numint1, numint2
real, intent(out) :: numfloat1
double precision, intent(out) :: numdoub1
integer*2, intent(out) :: numshor1
end subroutine f77sub
end interface
interface
subroutine hpfsub(
& bool1, letter1, numint1, numint2,
& numfloat1, numdoub1, numshor1)
logical*1, intent(out) :: bool1
character, intent(out) :: letter1
integer*4, intent(out) :: numint1, numint2
real, intent(out) :: numfloat1
double precision, intent(out) :: numdoub1
integer*2, intent(out) :: numshor1
end subroutine hpfsub
end interface
interface
extrinsic (f90_local) subroutine hpf90sub(
& bool1, letter1, numint1, numint2,
& numfloat1, numdoub1, numshor1)
logical*1, intent(out) :: bool1
character, intent(out) :: letter1
integer*4, intent(out) :: numint1, numint2
real, intent(out) :: numfloat1
double precision, intent(out) :: numdoub1
integer*2, intent(out) :: numshor1
end subroutine hpf90sub
end interface
interface
extrinsic (f90_local) subroutine f90sub(
& bool1, letter1, numint1, numint2,
& numfloat1, numdoub1, numshor1)
logical*1, intent(out) :: bool1
character, intent(out) :: letter1
integer*4, intent(out) :: numint1, numint2
real, intent(out) :: numfloat1
double precision, intent(out) :: numdoub1
integer*2, intent(out) :: numshor1
end subroutine f90sub
end interface
interface
extrinsic (f77_local) subroutine cplus(
& bool1, letter1, numint1, numint2,
& numfloat1, numdoub1, numshor1)
logical*1, intent(out) :: bool1
character, intent(out) :: letter1
integer*4, intent(out) :: numint1, numint2
real, intent(out) :: numfloat1
double precision, intent(out) :: numdoub1
integer*2, intent(out) :: numshor1
end subroutine cplus
end interface
call csub (bool1, letter1, numint1, numint2,
& numfloat1, numdoub1, numshor1)
write( *, 100)
& bool1, letter1, numint1, numint2, numfloat1,
& numdoub1, numshor1
100 format(1x,"csub: ",/,
& " bool1 = ", L2,/,
& " letter1 = ", A2,/,
& " numint1 = ", I5,/,
& " numint2 = ", I5,/,
& " numfloat1 = ", F9.1,/,
& " numdoub1 = ", F9.1,/,
& " numshor1 = ", I5,/)
call f77sub (bool1, letter1, numint1, numint2,
& numfloat1, numdoub1, numshor1)
write( *, 200)
& bool1, letter1, numint1, numint2, numfloat1,
& numdoub1, numshor1
200 format(1x,"f77sub: ",/,
& " bool1 = ", L2,/,
& " letter1 = ", A2,/,
& " numint1 = ", I5,/,
& " numint2 = ", I5,/,
& " numfloat1 = ", F9.1,/,
& " numdoub1 = ", F9.1,/,
& " numshor1 = ", I5,/)
call hpfsub (bool1, letter1, numint1, numint2,
& numfloat1, numdoub1, numshor1)
write( *, 400)
& bool1, letter1, numint1, numint2, numfloat1,
& numdoub1, numshor1
400 format(1x,"hpfsub: ",/,
& " bool1 = ", L2,/,
& " letter1 = ", A2,/,
& " numint1 = ", I5,/,
& " numint2 = ", I5,/,
& " numfloat1 = ", F9.1,/,
& " numdoub1 = ", F9.1,/,
& " numshor1 = ", I5,/)
call hpf90sub (bool1, letter1, numint1, numint2,
& numfloat1, numdoub1, numshor1)
write( *, 500)
& bool1, letter1, numint1, numint2, numfloat1,
& numdoub1, numshor1
500 format(1x,"hpf90sub: ",/,
& " bool1 = ", L2,/,
& " letter1 = ", A2,/,
& " numint1 = ", I5,/,
& " numint2 = ", I5,/,
& " numfloat1 = ", F9.1,/,
& " numdoub1 = ", F9.1,/,
& " numshor1 = ", I5,/)
call f90sub (bool1, letter1, numint1, numint2,
& numfloat1, numdoub1, numshor1)
write( *, 600)
& bool1, letter1, numint1, numint2, numfloat1,
& numdoub1, numshor1
600 format(1x,"f90sub: ",/,
& " bool1 = ", L2,/,
& " letter1 = ", A2,/,
& " numint1 = ", I5,/,
& " numint2 = ", I5,/,
& " numfloat1 = ", F9.1,/,
& " numdoub1 = ", F9.1,/,
& " numshor1 = ", I5,/)
call cplus (bool1, letter1, numint1, numint2,
& numfloat1, numdoub1, numshor1)
write( *, 700)
& bool1, letter1, numint1, numint2, numfloat1,
& numdoub1, numshor1
700 format(1x,"cplus: ",/,
& " bool1 = ", L2,/,
& " letter1 = ", A2,/,
& " numint1 = ", I5,/,
& " numint2 = ", I5,/,
& " numfloat1 = ", F9.1,/,
& " numdoub1 = ", F9.1,/,
& " numshor1 = ", I5,/)
end program hpf2all
% more csub.c
#define TRUE 0xff
#define FALSE 0
void
csub_( bool1, letter1, numint1, numint2, numfloat1,\
numdoub1, numshor1, len_letter1)
char *bool1, *letter1;
int *numint1, *numint2;
float *numfloat1;
double *numdoub1;
short *numshor1;
int len_letter1;
{
*bool1 = TRUE;
*letter1 = 'a';
*numint1 = 11;
*numint2 = -11;
*numfloat1 =10123.4;
*numdoub1 = 20123.4;
*numshor1 = 111;
}
% more f77sub.f
subroutine f77sub ( bool1, letter1, numint1,
& numint2, numfloat1, numdoub1, numshor1)
logical*1 bool1
character letter1
integer numint1, numint2
double precision numdoub1
real numfloat1
integer*2 numshor1
bool1 = .false.
letter1 = "b"
numint1 = 22
numint2 = -22
numfloat1 = 30123.4
numdoub1 = 40123.4
numshor1 = 222
return
end
% more hpfsub.f
subroutine hpfsub ( bool1, letter1, numint1,
& numint2, numfloat1, numdoub1, numshor1)
logical*1 bool1
character letter1
integer numint1, numint2
double precision numdoub1
real numfloat1
integer*2 numshor1
bool1 = .true.
letter1 = "c"
numint1 = 33
numint2 = -33
numfloat1 = 50123.4
numdoub1 = 60123.4
numshor1 = 333
return
end subroutine hpfsub
% more hpf90sub.f
subroutine hpf90sub ( bool1, letter1, numint1,
& numint2, numfloat1, numdoub1, numshor1)
logical*1 bool1
character letter1
integer numint1, numint2
double precision numdoub1
real numfloat1
integer*2 numshor1
bool1 = .false.
letter1 = "d"
numint1 = 44
numint2 = -44
numfloat1 = 70123.4
numdoub1 = 80123.4
numshor1 = 444
return
end subroutine hpf90sub
% more f90sub.f
subroutine f90sub ( bool1, letter1, numint1,
& numint2, numfloat1, numdoub1, numshor1)
logical*1 bool1
character letter1
integer numint1, numint2
double precision numdoub1
real numfloat1
integer*2 numshor1
bool1 = .true.
letter1 = "e"
numint1 = 55
numint2 = -55
numfloat1 = 90123.4
numdoub1 = 100123.4
numshor1 = 555
return
end subroutine f90sub
% more cplus.C
#define TRUE 0xff
#define FALSE 0
extern "C" {
extern void cplus_ (
char *bool1,
char *letter1,
int *numint1,
int *numint2,
float *numfloat1,
double *numdoub1,
short *numshort1,
int len_letter1) {
*bool1= FALSE;
*letter1 = 'f';
*numint1 = 66;
*numint2 = -66;
*numfloat1 = 110123.4;
*numdoub1 = 120123.4;
*numshort1 = 666;
}
}
% pgcc -c csub.c
% pgf77 -c f77sub.f
% pghpf -c hpfsub.f
% pghpf -c -Mf90 hpf90sub.f
% pgf90 -c -Mx,50,2 f90sub.f
% pgCC -c cplus.C
% pghpf -o hpf2all hpfmain.f csub.o f77sub.o hpfsub.o hpf90sub.o f90sub.o cplus.o
% hpf2all
csub:
bool1 = T
letter1 = a
numint1 = 11
numint2 = -11
numfloat1 = 10123.4
numdoub1 = 20123.4
numshor1 = 111
f77sub:
bool1 = F
letter1 = b
numint1 = 22
numint2 = -22
numfloat1 = 30123.4
numdoub1 = 40123.4
numshor1 = 222
hpfsub:
bool1 = T
letter1 = c
numint1 = 33
numint2 = -33
numfloat1 = 50123.4
numdoub1 = 60123.4
numshor1 = 333
hpf90sub:
bool1 = F
letter1 = d
numint1 = 44
numint2 = -44
numfloat1 = 70123.4
numdoub1 = 80123.4
numshor1 = 444
f90sub:
bool1 = T
letter1 = e
numint1 = 55
numint2 = -55
numfloat1 = 90123.4
numdoub1 = 100123.4
numshor1 = 555
cplus:
bool1 = F
letter1 = f
numint1 = 66
numint2 = -66
numfloat1 = 110123.4
numdoub1 = 120123.4
numshor1 = 666
The answer is pretty straight forward. You link the same f77/f90 libraries you would link under pgCC, using the Visual C++ linker.
To build an executable using the following source on a linux86 and NT system using pgCC or VC++ and pgf77 or pgf90.
c.C
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef __PGI
extern "C" {
void f_(float *, char *, int *, int *, int);
void c2_(int *, float *);
}
#else
void f_(float *, char *, int *, int *, int);
void c2_(int *, float *);
#endif
void main (void) {
int i, nchar;
char b[80];
char *c;
float a[3];
i = 3;
a[0] = 10.0;
a[1] = 20.0;
a[2] = 30.0;
sprintf(b, "a.dat");
nchar = strlen(b);
printf("string length = %d\n", nchar);
c = (char *) malloc (nchar*sizeof(char));
strncpy(c,b,nchar);
f_(a, c, &i, &nchar, nchar);
}
void c2_(int *b, float *a){
printf("CALLBACK %d %f %f %f\n",*b, a[0], a[1], a[2]);
}
The following fortran file was used
f.f
subroutine f(a,b,i,j)
character *(*) b
dimension a(*)
print*, i, a(1), a(2), a(3)
print*, j
print*, "String in F77=",b(1:j)
open(11,file=b(1:j))
write(11,*) "writing in f77"
close(11)
call c2(i,a)
return
end
The program 'c' was built and executed on a linux x86 system as follows
% pgf77/f90 -c f.f
% pgCC -o c c.C f.o -lpgftnrtl -lpgc
% pgCC -o c90 c.C
f.o -lpgf90 -lpgf90_rpm1 -lpgf902 -lpgf90rtl -lpgftnrtl -lm -lpgc
% c
string length = 5
3 10.00000 20.00000 30.00000
5
String in F77=a.dat
CALLBACK 3 10.000000 20.000000 30.000000
On an NT system, under PGI Workstation, create the following object files using pgf77/f90.
PGI> pgf77 -c -Munix f.f PGI> pgf90 -o f90.o -c -Munix f.f
Under VC++ The file c.C opened as a C++ source file. It was compiled w/o errors.
The object file c.obj was copied to the pgi working area my_dir milesto c.o.
Linking on NT using pgf90/pgf77 (not necessary to recompile, but okay to)
PGI> pgf90 -o c90 c.o f.f -Munix -Mnomain -msvcrt -mslibs PGI> pgf77 -o c c.o f.f -Munix -Mnomain -msvcrt -mslibs
Linking on NT using VC++
Under Project>Settings
In the Link page, add the following objects and libraries to the 'Object/library modules' window, at the beginning:
for f.o
C:\PGI\nt86\lib\pgstdinit-mslibs.o my_dir\f.o C:\PGI\nt86\lib\libpgftnrtl.a C:\PGI\nt86\lib\libpgc.a libc.lib oldnames.lib
for f90.o
C:\PGI\nt86\lib\pgstdinit-mslibs.o my_dir\f90.o C:\PGI\nt86\lib\libpgf90.a C:\PGI\nt86\lib\libpgf90_rpm1.a C:\PGI\nt86\lib\libpgf902.a C:\PGI\nt86\lib\libpgf90rtl.a C:\PGI\nt86\lib\libpgftnrtl.a C:\PGI\nt86\lib\libpgc.a libc.lib oldnames.lib
set the flag 'Ignore all default libraries'
The executables run as on linux86
The big question is not whether you can or not, but whether the resulting application is truly running parallel. This example assumes a version of NT (service pack 4.0 or greater?) that supports SMP.
Here is a simple program that wastes cycles but scales well. It times the process so that a successful execution on two processors will take half the time of one processor.
Here is a timer we use, dclock. It is written in assembler.
dclock.s
.file "dclock-pentium.s"
.align 8
.data
# .clock: .double 0.0000000075 # 133MHz
# .clock: .double 0.00000000666667 # 150 MHz
# .clock: .double 0.0000000060 # 166 MHz
.clock: .double 0.0000000050 # 200 MHz
# .clock: .double 0d4.28571429183673480000e-09 # 233.3333 MHz
# .clock: .double 0.00000000333333 # 300 MHz
# .clock: .double 0.00000000222223 # 450 MHz
# .clock: .double 0.000000002 # 500 MHz
# .clock: .double 0.0000000018181818 # 550 MHz
# .clock: .double 0.0000000016666667 # 600 MHz
# .clock: .double 0.0000000015384615 # 650 MHz
# .clock: .double 0.0000000014285714 # 700 MHz
# .clock: .double 0.0000000013658095 # 733 MHz
# .clock: .double 0.0000000013333333 # 750 MHz
# .clock: .double 0.0000000012500000 # 800 MHz
# .clock: .double 0.000000001 # 1.0 GHz
# .clock: .double 0.000000000666 # 1.5 GHz
# .clock: .double 0.00000000059 # 1.7 GHz
# .clock: .double 0.0000000005 # 2.0 GHz
.low: .long 0x00000000
.high: .long 0x00000000
.text
.align 4
.globl _DCLOCK@0, dclock, _dclock, _dclock_, dclock_
_DCLOCK@0:
dclock:
_dclock:
_dclock_:
dclock_:
.byte 0x0f, 0x31
movl %eax, .low
movl %edx, .high
fildll .low
fmull .clock
ret
Here is a c main program that you will compile with VC++
ex4main.c
#include <stdio.h>
#include <stdlib.h>
void ex4_(int *);
void main(int argc, char** argv)
{
int i;
if(argc == 1) exit(0);
i = atoi(argv[1]);
printf( "(main) calling ex4 with i = %d \n", i );
ex4_(&i);
}
Here is a fortran OpenMP routine that will use 2 cpus if you have them. Compile it.
pgf77 -mp -c -Munix ex4,f
ex4.f
subroutine ex4(i)
integer i,k
integer omp_get_num_procs, omp_get_max_threads
real*8 dclock, time1, time2
print *,"num procs =", omp_get_num_procs()
print *,"max threads =", omp_get_max_threads()
call omp_set_num_threads(1)
time1 = dclock()
!$omp parallel
!$omp do
do k = 1, 2
call delay(i)
enddo
!$omp end parallel
time2 = dclock() - time1
print *, " 1 cpu test - delay value doubled =", i*2,
* " time =", time2, " seconds"
if (omp_get_num_procs() .ge. 2) then
call omp_set_num_threads(2)
print *, " more than 1 cpu, trying parallel"
else
print *, " only 1 cpu, program exiting"
return
endif
time1 = dclock()
!$omp parallel
!$omp do
do k = 1, 2
call delay(i)
enddo
!$omp end parallel
time2 = dclock() - time1
print *, " 2 cpu test - delay value doubled =", i*2,
* " time =", time2, " seconds"
return
end
subroutine delay(n)
integer n
integer i
do i=1,n
call abc()
end do
return
end
subroutine abc()
integer i
do i=1,1000000
call def()
end do
return
end
subroutine def()
return
end
To link using pgf77, do the following
pgf77 -Mnomain -Munix -mp -msvcrt -mslibs -o ex4 ex4main.o ex4.f delay.f dclock.s
Note that ex4main.obj was renamed ex4main.o (not necessary) Also note that the -mslibs switch causes the MS linker to be used.
To link using VC++
pgf77 -c -Munix -mp ex4.f pgf77 -c -Munix dclock.s pgf77 -c -Munix delay.f
in VC++ project settings, add the following files to the object/library module section of Link page in the front (you may also want to set the nodefault libs flag)
C:\PGI\nt86\lib\pgstdinit-mslibs.o my_dir\ex4.o my_dir\delay.o my_dir\dclock.o C:\PGI\nt86\lib\libpgmp.a <---- only needed when compiling -mp C:\PGI\nt86\lib\libpgftnrtl.a C:\PGI\nt86\lib\libpgc.a libc.lib oldnames.lib
PGI$ ex4main.exe 113 (main) calling ex4 with i = 113 num procs = 2 max threads = 1 1 cpu test - delay value doubled = 226 time = 8.014712320000399 seconds more than 1 cpu, trying parallel 2 cpu test - delay value doubled = 226 time = 4.028287394990912 seconds