[PDC - Center for Parallel Computers, KTH]

Tutorial on Compiling and Running XL Fortran Programs

Runs on: IBM SP


Table of Contents


Objectives and Prerequisites

This tutorial introduces you to the process of running a serial Fortran code on the SP. It assumes you are familiar with Fortran and its syntax, but not with compiling and linking programs under UNIX.

In this tutorial you will work through several examples, beginning with the very simplest compile and link. Then you will move on to other situations which are commonly encountered: using data files for I/O, using shell scripts, handling program units which are in separate files, and using the UNIX make program.


What is the Fortran Compiler?

The Fortran compiler on the Parallel Computing Center's SP is IBM's XL Fortran, also known as xlf.

The xlf compiler accepts the full Fortran 90 language, with new statements, intrinsic functions, free-form input, procedure enhancements, and much more.

Fortran programs (i.e.,the source code) can be easily created and modified using the system editor of your choice. Once you have created and saved your source file, you can compile the program, link it with the necessary Fortran library routines, and then execute the program. The executable file is saved with the name a.out (by default) and it can be executed for different runs (i.e., with different input). Of course, if you change the source code, you will need to recompile and link to create an updated executable.


Before You Begin


A Simple Example

For this example, use the sample Fortran file myprog.f. If you look at the file, you will see that it is a very simple Fortran program which adds two numbers.

Issue the commands shown below to try out the example.

If there were any syntax errors in your source file or problems during the link, they would be displayed. In this case, both the compile and link steps should be fine, so you should be able to run the program. Then the program should compute and print the answer: Edit the program to change the values of A and B. Run the program several times with different values. You will need to recompile and link.

What if you don't want your executable named "a.out"? You can either rename the file (with the mv command), or you can use the -o flag on the xlf command to override the default name of a.out. For example:

Generally, if you intend to keep the program around for later, you will probably want to give it a name that indicates what the program is. However, be careful about keeping around lots of executables that are not frequently used; they tend to be very large and can use up disk space quickly.


Don't Forget to Optimize

By default, the compiler does not optimize the code it generates; however, you can specify a flag on the "xlf" command to significantly speed up your program. Use one of these two compile-time flags:

-O2 (identical to -O) enables a base set of optimizations, designed to generate fast object code with a relatively short compilation time.

-O3 provides the same optimizations as -O2, plus more thorough optimizations that are memory or compile time intensive. Although its use is fine for most codes, there is a possibility of changing the results of the program -- usually in the precision of the answers, although in rare cases program results could be wrong. So you should always check your program's answers against those produced without optimization before routinely running the -O3 optimized code.

For example, compiling with the lower level of optimization:

To run with the higher level of optimization:

If you want to ensure that optimizations done by the -O3 option do not alter the semantics of a program, use the -qstrict flag:

The above optimization discussion is just a brief overview of one easy way for every programmer to optimize code. Additional information about optimizing serial Fortran code are discussed in the talk Single-Processor Performance Considerations for the SP2 and the corresponding lab Optimizing on a Single Processor, as well as IBM's XL Fortran manuals (see references at the end of this lab).


Using Data Files

In addition to (or instead of) editing the program, or using the terminal as an input and output device, it is usually necessary to access data files for input and output. The common methods for using data files are described in the following examples.

Fortran "read" (from unit 5) and "write" (to unit 6) statements default to stdin and stdout, respectively (which in turn default to the terminal). You can redirect stdin and stdout by the standard UNIX method. Run the program myprog1.f using the sample input file myprog1.input and directing the output to a file called myprog.output.

The above method works fine when you have one input file and one output file. However, programs which have more than one input or output file must use another method, in which different unit numbers (specified on read and write statements) are associated with specific files. The files used for input/output operations must be connected to a unit, which is a means of accessing a file. Units and files become connected through an OPEN statement. The syntax for an OPEN statement is given below:

where un specifies an integer expression whose value must be positive and nonzero, and fn is a valid relative or absolute pathname. The "UNIT=" keyword is optional and can be omitted. This language construct is more fully described in the XL Fortran Language Reference. It is important to remember that AIX distinguishes between uppercase and lowercase characters. Thus, specify three different files.

Once a file is no longer needed by a program, it should be disconnected from a unit. Files are disconnected through a CLOSE statement or at the end of program execution. The syntax for a CLOSE statement is given below:

where un specifies an integer expression whose value must be positive and nonzero and must match the unit identifier that was previously opened. This language construct is more fully described in the XL Fortran Language Reference.

Thus, to associate the file mydata.input with unit 10, include the following statements in your code:

(The beginning period and slash in the filename of the OPEN statement are used to specify the current working directory).

Look at the example program myprog2.f. Notice that the open statements associate unit 10 with the input file (myprog2.input) and unit 11 with an output file (myprog.output). Compile and execute the program using the steps below:

Input and output are from/to files instead of the terminal. In this case, you need to know what's in the source code to know what the files names are (fyi, it is possible to have the program prompt the user for names of I/O files and then use those values in the OPEN statement). To view the input and output for this program, issue:


Using Shell Scripts

It is convenient to put frequently used statements into a shell script file so that they do not have to be typed in each time. A shell script can contain any commands you can enter in response to a shell prompt. For example, a command in a shell script might invoke a utility, a compiled program, or another shell script.

Shell scripts can become quite complicated, utilizing variables, pipes and redirection, as well as flow control commands. These topics are beyond the scope of this tutorial. More information can be found on these topics in any textbook on UNIX, and in our tutorial on Using and Writing Shell Scripts. Here we cover only the very basics of a shell script.

Simple shell scripts include groups of commands which can be processed by the shell. Commands are separated by newline characters (entered from the keyboard by pressing the RETURN key), or may be stacked on a single line and separated by semicolons. Comments are specified by pound (#) signs.

C shell scripts can be executed with the following command:

where filename is the valid pathname of a script file. This utility will read the commands from the specified file and direct standard input and standard output to the console. To display the commands as they are passed to the shell, use the -v option.

For example, the following C shell script file, named myprog2.script, will compile, load and run the program from the previous section, myprog2.f.

# This is a sample C shell script
# Execute this script by issuing the command:  csh -v myprog2.script
#        
xlf -O myprog2.f             # compile and load this program
./a.out                      # run this program

Issue the following command to run the script: Then try it again but without the -v flag:


Handling Subprograms in Separate Source Files

For large programs, it is usually convenient to keep each program unit in a separate file. When recompiling the entire program, you just specify all the filenames on the xlf command. In the following example, the main program, residing in the file myprog3.f, calls a subroutine in a second file, mysub3.f. Look at those files before running this example. With large programs, keeping all program units in a single file is also impractical because a change to a single subroutine would require recompilation of the entire program--which can be time consuming as a program gets large. Instead, each program unit can be kept in a separate file. When changes are made to some of the source files, then only the updated files need to be recompiled, although all relevant files must be linked to create the new executable.

The compile step alone is done by specifying the -c flag on the xlf command. Instead of an executable file, only the object files (.o files) are created. Then to link all the object files together to create the executable, you issue the xlf command with the .o files as arguments. Here are the specific commands for this example:

Suppose you then were to update the subprogram "compute" in the source file mysub3.f. To update your executable, you need to recompile mysub3.f (but not myprog3.f) and link with the existing myprog3.o file. Here are the commands: The above example is so small that the time saved (by not recompiling everything) may not be worth the trouble of keeping the .o files around. However, if you had a very large program with lots of separate source files, the time saving could be substantial.

If you inherit a large program where the main program and all subroutines are in one file, you can use fsplit to split the single file into separate files for each program unit. See the man page for details on fsplit


Using Make

With a large program that consists of many source files, the files typically depend on one another in complex ways. Whenever changes are made to one that other files depend on, all dependent files will need to be recompiled. Determining exactly which files need to be updated can become a difficult and time-consuming task. The make utility helps automate this process. To take full advantage of make, you need to keep each program unit (i.e., main program or subprogram) in a separate source file as discussed in the previous section.

make builds programs from dependency information stored in a file named makefile in the working directory. A simple makefile has the following form:

         target-file:   prerequisite-file-list
         TAB            construction-commands
where target-file is the name of the file that depends on the files in the prerequisite-file-list. The construction-commands (one line or several lines) must start with a TAB and are the commands that construct the target file. As an example, suppose that the executable file mycode depended on the object files mycode.o and mysubs.o. An appropriate dependency line would be:
         mycode:        mycode.o mysubs.o
                        xlf -o mycode mycode.o mysubs.o
The following example uses the sample Fortran files
abc.f, a.f, b.f, c.f and the makefile which you obtained earlier. The main program resides in abc.f, and the subroutines reside in in the files a.f, b.f and c.f. Look at makefile and notice that there are commands to create the executable (abc) and each of the .o files. By default, the first target listed in the file is the one that will be created when the make command is issued (in this case, the file abc). Since abc depends on the files a.o, b.o, c.o and abc.o, all of the .o files must exist (and be up-to-date); make will take care of checking for them and re-creating them if necessary. Now suppose you were to update one of the files. You can simulate that by using the UNIX touch command. Then re-build the program.
What happened this time? make was able to determine from the rules in the makefile that, since b.f was updated, two targets needed to be rebuilt: b.o and then abc. The other .o files (upon which abc depends) were up-to-date and did not need to be recompiled.

The above example is a very simple one, but there is much more you can do with make. For more information and additional examples, see our tutorial on make. For complete information on how make can be used, see the man page. There are also textbooks that describe make in detail.


Cleanup

If you wish to delete the directory and files you created for this exercise:


References


[Copyright Statement] [Feedback]

Changed by:$Author: kinsella $,$Date: 1999/07/09 08:19:38 $