Difference between revisions of "Fortran"
(Fixed formatting errors) |
(Replace equivalence statement with associate statement documentation) |
||
| (One intermediate revision by the same user not shown) | |||
| Line 7: | Line 7: | ||
== Miscellaneous Notes == | == Miscellaneous Notes == | ||
| + | === Associate Statement === | ||
| + | Associate is effectively the replacement for (and better version of) the equivalence statement (which was formerly documented here, removed to reduce confusion). | ||
| + | Effectively, it adds clarity to programming by allowing you to name "temporary variables" | ||
| − | + | The <code>associate</code> statement allows different variable names to reference the same data storage (ie. memory allocation). | |
| − | + | A common paradigm in PHASTA is the use of temporary arrays to store the partial result of calculations that maybe reused multiple times. | |
| − | + | For example, the function below calculates sum over i of [ (sqrt(a) * sqrt(b))^2_i + sqrt(a * b)_i ], where a and b are vectors: | |
| − | The <code> | ||
| − | A common paradigm in PHASTA is the use of temporary arrays to store the result of calculations that maybe reused multiple times. | ||
| − | For example, the function | ||
<nowiki> | <nowiki> | ||
| − | + | function example_func1(a, b) result(c) | |
implicit none | implicit none | ||
real, intent(in), dimension(4) :: a, b | real, intent(in), dimension(4) :: a, b | ||
| − | real, | + | real, intent(out) :: c |
| − | + | real, dimension(4) :: temp | |
temp = a*b | temp = a*b | ||
| − | + | c = sqrt(temp) | |
| − | temp = | + | |
| − | + | temp = sqrt(a)*sqrt(b) | |
| + | c = c + temp**2 | ||
return | return | ||
| − | end function | + | end function example_func1 |
</nowiki> | </nowiki> | ||
| − | While reusing the <code>temp</code> array for both a*b and | + | While reusing the <code>temp</code> array for both a*b and sqrt(a)*sqrt(b) is memory efficient (as we only need to have 2 1x4 arrays, <code>temp</code> and <code>c</code>) and computationally efficient (as we calculate a*b only once), it's not very easy to read as the ''definition'' of <code>temp</code> changes throughout the code. |
| − | To improve the legibility, we could set a*b and | + | To improve the legibility, we could set a*b and sqrt(a)*sqrt(b) to be different variables instead: |
<nowiki> | <nowiki> | ||
| − | + | function example_func2(a, b) result(c) | |
implicit none | implicit none | ||
real, intent(in), dimension(4) :: a, b | real, intent(in), dimension(4) :: a, b | ||
| − | real, dimension(4) :: ab, | + | real, intent(out) :: c |
| − | + | real, dimension(4) :: ab, ab_sqrt | |
| − | + | ||
ab = a*b | ab = a*b | ||
| − | + | c = sqrt(ab) | |
| − | + | ||
| − | + | ab_sqrt = sqrt(a)*sqrt(b) | |
| + | c = c + ab_sqrt**2 | ||
return | return | ||
| − | end function | + | end function example_func2 |
</nowiki> | </nowiki> | ||
| − | This is easier to read, as the contents of <code>ab</code> and <code> | + | This is easier to read, as the contents of <code>ab</code> and <code>ab_sqrt</code> are far less ambiguous. |
| + | However, note that we are less memory efficient than before; we now require 3 1x4 arrays (<code>ab</code>, <code>ab_sqrt</code>, and <code>c</code>) compared to the previous 2. | ||
| + | This may not seem like much, but memory speed is often the bottle-neck when it comes to HPC performance, so a 50% increase in required memory is significant. | ||
| + | This is especially true when you deal with arrays with millions of elements rather than just 4. | ||
| + | |||
| + | However, using the <code>associate</code> statement, we can get the best of both worlds: | ||
| − | |||
<nowiki> | <nowiki> | ||
| − | + | function example_func3(a, b) result(c) | |
implicit none | implicit none | ||
real, intent(in), dimension(4) :: a, b | real, intent(in), dimension(4) :: a, b | ||
| − | real, | + | real, intent(out) :: c |
| − | + | real, dimension(4) :: temp | |
| − | + | ||
| − | + | associate(ab => temp) | |
| − | + | ab = a*b | |
| − | + | c = sqrt(ab) | |
| − | + | end associate | |
| − | + | ||
| + | associate(ab_sqrt => temp) | ||
| + | ab_sqrt = sqrt(a)*sqrt(b) | ||
| + | c = c + ab_sqrt**2 | ||
| + | end associate | ||
return | return | ||
| − | end function | + | end function example_func3 |
</nowiki> | </nowiki> | ||
| − | + | Here, <code>ab</code> and <code>ab_sqrt</code> use the ''same memory'', thus we only require 2 1x4 arrays. <code>example_func3</code> is compiled as completely identical to <code>example_func1</code>, but it's more easily readable. | |
| − | Here, <code> | + | It also has the nice effect of separating the code into computation chunks; if you use temporary variables, each new associate block represents a new "task" being completed. |
| − | |||
| − | |||
[[Category:Software_Engineering]] | [[Category:Software_Engineering]] | ||
Latest revision as of 17:21, 4 February 2021
Fortran (short for Formula Translation) is a programming language geared towards numerical computation.
Documentation
Some documentation of the language format can be found at Intel's Developer Guide.
Fortran subroutines can also be documented using Doxygen. It is recommended that you document your code in a Doxygen format, as we're looking to move to Doxygen for PHASTA documentation.
Miscellaneous Notes
Associate Statement
Associate is effectively the replacement for (and better version of) the equivalence statement (which was formerly documented here, removed to reduce confusion). Effectively, it adds clarity to programming by allowing you to name "temporary variables"
The associate statement allows different variable names to reference the same data storage (ie. memory allocation).
A common paradigm in PHASTA is the use of temporary arrays to store the partial result of calculations that maybe reused multiple times.
For example, the function below calculates sum over i of [ (sqrt(a) * sqrt(b))^2_i + sqrt(a * b)_i ], where a and b are vectors:
function example_func1(a, b) result(c)
implicit none
real, intent(in), dimension(4) :: a, b
real, intent(out) :: c
real, dimension(4) :: temp
temp = a*b
c = sqrt(temp)
temp = sqrt(a)*sqrt(b)
c = c + temp**2
return
end function example_func1
While reusing the temp array for both a*b and sqrt(a)*sqrt(b) is memory efficient (as we only need to have 2 1x4 arrays, temp and c) and computationally efficient (as we calculate a*b only once), it's not very easy to read as the definition of temp changes throughout the code.
To improve the legibility, we could set a*b and sqrt(a)*sqrt(b) to be different variables instead:
function example_func2(a, b) result(c)
implicit none
real, intent(in), dimension(4) :: a, b
real, intent(out) :: c
real, dimension(4) :: ab, ab_sqrt
ab = a*b
c = sqrt(ab)
ab_sqrt = sqrt(a)*sqrt(b)
c = c + ab_sqrt**2
return
end function example_func2
This is easier to read, as the contents of ab and ab_sqrt are far less ambiguous.
However, note that we are less memory efficient than before; we now require 3 1x4 arrays (ab, ab_sqrt, and c) compared to the previous 2.
This may not seem like much, but memory speed is often the bottle-neck when it comes to HPC performance, so a 50% increase in required memory is significant.
This is especially true when you deal with arrays with millions of elements rather than just 4.
However, using the associate statement, we can get the best of both worlds:
function example_func3(a, b) result(c)
implicit none
real, intent(in), dimension(4) :: a, b
real, intent(out) :: c
real, dimension(4) :: temp
associate(ab => temp)
ab = a*b
c = sqrt(ab)
end associate
associate(ab_sqrt => temp)
ab_sqrt = sqrt(a)*sqrt(b)
c = c + ab_sqrt**2
end associate
return
end function example_func3
Here, ab and ab_sqrt use the same memory, thus we only require 2 1x4 arrays. example_func3 is compiled as completely identical to example_func1, but it's more easily readable.
It also has the nice effect of separating the code into computation chunks; if you use temporary variables, each new associate block represents a new "task" being completed.