Since a couple of years ago or so I was completely new to Fortran, I overused SUBROUTINE
s with no arguments, together with shared data, so that these procedures have made calculations on the actual arguments, available through USE
statements. Now that I need to reuse some of these procedures (think of computing the divergence in a volume, a big DIMENSION(:,:,:)
array, from a vector field in that volume, 3 big DIMENSION(:,:,:)
arrays glued together in a derived type), I'd like either to
SUBROUTINE
s but remove USE
statements and use IN
/OUT
/INOUT
dummy arguments (easy), orFUNCTION
s (little harder since I've to study a bit)I imagine there could be a difference in performance in the two approaches, that I'd like to understand. In the following MWE I wrote 3 procedures to do the same computation, but I have no idea of how I should choose one or the others; neither I know whether some other approach would be preferable.
As a note, all rank-3 actual arrays in my program are ALLOCATABLE
and must be so.
PROGRAM mymod
IMPLICIT NONE
TYPE blk3d
REAL, DIMENSION(:,:,:), ALLOCATABLE :: values
END TYPE blk3d
TYPE(blk3d) :: A, B
INTEGER, PARAMETER :: n = 2
INTEGER :: i
ALLOCATE(A%values(n,n,n))
A%values = RESHAPE([(i/2.0, i = 1, PRODUCT(SHAPE(A%values)))], SHAPE(A%values))
print *, A%values
! 1st way
B = myfun(A)
print *, B%values
DEALLOCATE(B%values)
! 2nd way
ALLOCATE(B%values(n,n,n))
CALL mysub(A, B)
print *, B%values
DEALLOCATE(B%values)
! 3rd way
ALLOCATE(B%values(n,n,n))
CALL mysub2(A, B%values)
print *, B%values
CONTAINS
FUNCTION myfun(Adummy) RESULT(Bdummy)
IMPLICIT NONE
TYPE(blk3d), INTENT(IN) :: Adummy
TYPE(blk3d) :: Bdummy
ALLOCATE(Bdummy%values, mold = Adummy%values)
Bdummy%values(:,:,:) = 2*Adummy%values
END FUNCTION myfun
SUBROUTINE mysub(Adummy, Bdummy)
IMPLICIT NONE
TYPE(blk3d), INTENT(IN) :: Adummy
TYPE(blk3d), INTENT(INOUT) :: Bdummy
Bdummy%values(:,:,:) = 2*Adummy%values
END SUBROUTINE mysub
SUBROUTINE mysub2(Adummy, Bdummy)
IMPLICIT NONE
TYPE(blk3d), INTENT(IN) :: Adummy
REAL, DIMENSION(:,:,:), INTENT(OUT) :: Bdummy
Bdummy(:,:,:) = 2*Adummy%values
END SUBROUTINE mysub2
END PROGRAM mymod
EDIT
In my program to do CFD, I use several rank-3 big-sized arrays. These arrays interact with each other in that computations (not simply pointwise +
/-
/*
,...) is performed on some of them to obtain others of them. Think of B
which is computed based on A
through one of the 4 procedures in the example and then used to upgrade A
itself by A = A + B
. Am I wrong to think that the 4 options above could accomplish the same task? In this sense I could simply call A = A + myfun(A)
if I chose the function approach.
EDIT2
The actual derived type, along with that big rank-3 array, has half a dozen of other fields (scalars, and small static arrays); additionally, most of the variables of this type are arrays, e.g. TYPE(blk3d), DIMENSION(n) :: A
.
A function must return a single value, and can be invoked from within expressions, like a write statement, inside an if declaration if (function) then , etc. A subroutine does not return a value, but can return many values via its arguments and can only be used as a stand-alone command (using the keyword call ).
Functions and subroutines operate similarly but have one key difference. A function is used when a value is returned to the calling routine, while a subroutine is used when a desired task is needed, but no value is returned.
The subroutine call is an entire instruction. To call a function, use the function name (label or program member name) immediately followed by parentheses that can contain arguments. There can be no space between the function name and the left parentheses.
Fortran has two different types of subprograms, called functions and subroutines. Fortran functions are quite similar to mathematical functions: They both take a set of input arguments (parameters) and return a value of some type. In the preceding discussion we talked about user defined subprograms. Fortran also has some built-in functions.
The main program (s) can call (or invoke) such subprograms (functions or subroutines) to accomplish a task. Functions and subroutines are different in the following sense: Functions return a single object and - usually - don't alter the values of its arguments (i.e. they act just like a mathematical function!);
Unless the impure prefix (introduced in Fortran 2008) is specified an elemental function is also a pure function. The return statement can be used to exit function and subroutine. Unlike many other programming languages it is not used to set the return value. This function performs an iterative computation.
Unfortunately, Fortran is not really a polymorph language so in general you have to be careful to match the types of your variables and your functions! Now we turn to the user-written functions.
Choosing between subroutine or function should generally be based on how you will be using the result and how clear it is to the reader.
What you do want to be concerned with is how many times data gets unnecessarily copied. With big arrays, you would want to reduce that.
Ignoring the actual work in the procedures, myfun copies the data a second time and (potentially) does two allocations. First the function result variable gets allocated and the data copied to it. Then back in the caller, B%values gets reallocated if it isn't already the same shape as the result and the data copied again, then the function result is deallocated.
mysub and mysub2 don't have this extra allocate/copy and are pretty much equivalent, though the call to mysub2 may have some extra work setting up a descriptor on the stack. I expect that to be noise if the subroutine does any real work.
Choosing between mysub and mysub2 really depends on how clear it is in your real application. Having a derived type with only one component seems unrealistic unless you are looking to have an array of these.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With