Fortran 90 introduced the concept of modules, a separately compiled collection of declarations and procedures that could be referenced by other program units. This was borrowed from Ada, (and probably other languages), but omitted one very useful Ada feature: IS SEPARATE. Why is this important? With Fortran modules, a source file that USEs a module is dependent on the entire module; if any change is made to the module, every source that USEs it must be recompiled even if the changes have no effect on the interface. If you have nested modules, as many applications do, this can lead to a “recompilation cascade” that dramatically increases build time for even the most minor change.
Ada’s IS SEPARATE let you separate the implementation of module procedures from the declaration so that changes in the implementation simply required a recompilation of that implementation and not all the users of the modules. It’s been a long time, but separate implementation has finally come to Fortran with the Fortran 2008 submodule feature, and submodules are now in Intel® Fortran Compiler version 16.0. Let’s see how this works…
Consider the following module:
module colors … contains subroutine sub1 (arg) integer, intent(in) :: arg … end subroutine sub1 function func2 (arg) real :: func2 real, intent(in) :: arg … end function func2 … subroutine sub47 (arg) character(*), intent(inout) :: arg … end subroutine sub47 end module colors
The first thing we do is pull out the contains section and add interface blocks for the module procedures, with the addition of the keyword module before each function or subroutine. Like so:
module colors … interface module subroutine sub1 (arg) integer, intent(in) :: arg end subroutine sub1 module function func2 (arg) real :: func2 real, intent(in) :: arg end function func2 … module subroutine sub47 (arg) character(*), intent(inout) :: arg end subroutine sub47 end interface end module colors
The use of the module prefix tells the compiler that the implementation is separate. Now let’s supply that. In a new source file, I’ll call it colors_yellow.f90; we start out with:
submodule (colors) yellow
The name in parentheses is the parent of this submodule. Submodules can have other submodules as parents, so you can have a tree structure of them. Siblings can’t see each other’s declarations, but they can see their parent’s.
Next would be any declarations that should be local to the submodule (or its children). use statements can be here too if needed – the submodule will automatically inherit all declarations from its parent. (And this leads to a cute trick – Module A can use module B, and a submodule of B can use module A! This doesn’t create a circular dependency because module A can’t see into the submodule of B.) So let’s add some submodule-only declarations…
use, intrinsic :: ISO_FORTRAN_ENV real(REAL64) :: localvar contains
Now comes the fun part – the implementation of the procedures declared in the parent. Here you have a choice; you can repeat the subroutine or function statement and all the declarations from the interface, or you can use module procedure and inherit the interface. Repeating the declarations is perhaps easier for maintainers, as you don’t have to reference two files to see how dummy arguments are declared. But the downside is that you have to type the declarations twice, introducing the possibility of a mismatch. (The compiler will complain, though, if they don’t match.) module procedure is cleaner, with the declarations appearing only once, but may be a bit harder for maintainers to follow. Choose whichever model most appeals to you, and yes, you can mix and match. So let’s do that…
module subroutine sub1 (arg) integer, intent(in) :: arg localvar = REAL(arg,REAL64) end subroutine sub1 ! module procedure func2 func2 = arg + localvar end procedure func2 ! module procedure sub47 arg = TRIM(arg) // "***" end procedure sub47 ! end submodule yellow
You build a submodule just like any other Fortran source, and include its object output in the link of your application. When constructing your makefile or other build script that cares about build dependencies, the only new thing is that a submodule depends on its parent; nothing (except a child submodule) depends on the submodule. If you are using Intel® Visual Fortran in Microsoft Visual Studio*, this is handled for you automatically. The compilation of a submodule creates a file with a .smod file type, so you would create a rule for that in a makefile and have any child submodule depend on the .smod of its parent. The filename has the form firstname.lastname@example.org, for example, email@example.com. Remember that the link is dependent on all the object files from all the submodules.
Now that we have the submodule created we can build the application. Oh, we need to change a line of code in sub47. When we do that, only the submodule gets recompiled and then the application is relinked. If we were doing this without submodules, any source that referenced module colors would also get recompiled. But what if we want to add or change the interface? Then, everything does need to be recompiled.
Submodules don’t make your code any faster (nor any slower), but they can lead to a dramatic improvement in programmer productivity through better partitioning and reduced build times. Intel Fortran 16.0, part of Intel® Parallel Studio XE 2016, is the first Intel version to support submodules. Support in other Fortran compilers is limited as I write this, but I expect to see broader implementation as time goes on.
(Originally posted at Intel Developer Zone, copied with permission)