Difference between revisions of "Fortran"
m |
(Replace equivalence statement with associate statement documentation) |
||
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 16: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.