Using Fortran preprocessor (1)
Library like BLAS or LAPACK are both very efficient and pretty often installed on linux computer. Anyway, when you have to share a source code, you can’t rely on the fact that those libraries are set up on the host computer. So the safe solution is to find a potentially less efficient alternative, which may sound annoying for people used to BLAS and LAPACK.
Fortunately, for this kind of dilemma there is the PreProcessor, that although not part of the standard, is understood by several fortran compiler (at least gfortran, ifort and xlf).
The basic idea of preprocessing is to allow conditional compilation : depending on certain conditions, one part or another of the code will be compiled.
A first exemple
To illustrate let’s make a sample program, that should be named “TestPreProcessor.F90”, the extension should be written with capital letters (i.e .F90 and not .f90). That’s the way most compilers automatically know that they should preprocess the code.
program TestPreProcessor implicit none #if TEST write(*,*)"Compiled with Test Flag" #ELIF write(*,*)"Not Compiled with Test Flag" #END end program TestPreProcessor
Now we have two ways to compile our program.
The classical way
#Compile with no flag ifort TestPreProcessor.F90 -o TestPreProcessor #Test ./TestPreProcessor #You should see something like this: Not Compiled with Test Flag
Now the alternative way
#Compile with no flag ifort -DTEST TestPreProcessor.F90 -o TestPreProcessor #Test ./TestPreProcessor #You should see something like this: Compiled with Test Flag
The important point is that the flag -D stand for “define”, then during preprocessing, the keyword written after -D will be considered as defined. The keys #IF…#ELIF…#END will then be examined.”#if TEST” will stand for “is TEST defined ?” if it is true then code below the #if will compiled otherwise code above #elif will be compiled. That ‘s as simple as it seems !
Wrap up and useful example
So we ‘ve explained the basic idea with preprocessing, let apply this to a real problem. Suppose, we want to make a dot product a two vectors, and would like to offer the possibility to use either the dot_product intrinsic or the equivalent lapack subroutine.
program Dotprod implicit none integer,parameter::n=1000000 real(kind=4)::V1(n),V2(n),results real,external::sdot V1=1.0; V2=1.0 !//Use BLAS if asked// #if BLAS results=sdot(n,V1,1,V2,1) #else results=dot_product(V1,V2) #endif !//only a test// write(*,*)results end program Dotprod
And the corresponding makefile could look like :
PROG=Dotprod COMP=ifort #-------------------------------------------------------------------- #Compilations options + Library path #-------------------------------------------------------------------- BLAS= -DBLAS -lblas FF= -O3 $(BLAS) # Comment or uncomment $(BLAS) #-------------------------------------------------------------------- #Main rules #-------------------------------------------------------------------- $(PROG): $(PROG).F90 $(COMP) $(FF) -o $(PROG) $(PROG).F90
These should allow to use either a BLAS subroutine or a standard internal subroutine throughout your code, and ask for very few changes to the end-user who would like to compile your code.
There are several other very useful preprocessor reserved key…but we will see them in an other post 🙂