1
$\begingroup$

Frame of the question

I am currently editing an add-on module to an ocean/circulation model, which is written in Fortran. The code of the main model is quite optimized with respect to short run time (not with respect to readability of the code). There are some code parts in the add-on module that do not seem to be optimized - at least from my point of view.

Problem

At three occasion a temporary variable is introduced to substitute some calculations as follows:

! comment
a2 = a * a
b2 = b * b

! comment
c = a / (a2 + b2)

! comment
f = c * d

This seems reasonable to me if (1) I want to make the code better human-readable or (2) the a * a is to be substituted several times in the code. However, (1) and (2) are not the case: a2 and b2 are only needed once. In order to reduce the run time this formulation seems to be better:

f = d * a / (a * a + b * b)

The number of calculation steps remains the same. The declaration of three variables is removed. We loose some explanations in the code.

I think that in one previous version of the code a * a was present several times which is why it was substituted by a2. However, it was not removed.

Questions

  • Is the a good reason with respect to "optimal" run time to keep the code as it is posted in the first code snipped?
  • Does a state-of-the-art compiler (i.e. ifort) detect it and remove the unnecessary variables? Thus, both formulations do not make a difference?
$\endgroup$
5
  • 3
    $\begingroup$ I would argue it is feasible that (1) is being attempted with this approach, though the math is simple enough that it is (in my opinion) not needed. I would not be surprised if the compiler inlines some of these variables and makes the two approaches identical after compilation. I am not a Fortran person though, so maybe someone else can comment on the Fortran compilers. $\endgroup$
    – spektr
    Commented Jan 25, 2017 at 18:23
  • 7
    $\begingroup$ The question of readability is highly subjective. But as far as performance, the only reasonable way to decide where and how to speed up your code is run it with a profiler. It is almost a truism in software development that programmers will often be surprised at where a profiler shows the bottlenecks to be. $\endgroup$ Commented Jan 25, 2017 at 18:58
  • 1
    $\begingroup$ Common subexpression elimination and inlining code are like the bread and butter of compiler optimizations, the first and easiest optimizations that a compiler can do. There is every reason to think the performance of the code will be exactly the same (while losing the comments), and there is no particular reason to think that a compiler would have trouble performing this kind of code transformation if it made sense. This is exactly the sort of thing that should be left to the compiler to decide. This is even more true if the comments were meaningful. $\endgroup$
    – Kirill
    Commented Jan 25, 2017 at 22:38
  • 1
    $\begingroup$ "The declaration of three variables is removed." This is misguided: since the code will be compiled to assembly, and both variables and subexpressions will be stored in whatever registers the compiler wants to use, there shouldn't be a reason to think that this will affect performance. There are sites like godbolt.org (for C++; example), where you can see just how code looks after compilation: the variables-subexpressions distinction is basically meaningless performance-wise. $\endgroup$
    – Kirill
    Commented Jan 25, 2017 at 22:42
  • $\begingroup$ @Kirill I you create an answer from your comment I will accept it as correct answer. Thanks for all the comments! $\endgroup$ Commented Jan 25, 2017 at 23:43

1 Answer 1

3
$\begingroup$

Common subexpression elimination and inlining code are like the bread and butter of compiler optimizations, the first and easiest optimizations that a compiler can do. There is every reason to think the performance of the code will be exactly the same (while losing the comments), and there is no particular reason to think that a compiler would have trouble performing this kind of code transformation if it made sense. This is exactly the sort of thing that should be left to the compiler to decide. This is even more true if the comments were meaningful.

The declaration of three variables is removed.

This is misguided: since the code will be compiled to assembly, and both variables and subexpressions will be stored in whatever registers the compiler wants to use, there shouldn't be a reason to think that this will affect performance. There are sites like godbolt.org (for C++; example), where you can see just how code looks after compilation: the variables-subexpressions distinction is basically meaningless performance-wise.

In the end, unfortunately, optimization tends to be a little bit of an art, especially now that optimizing compilers are powerful and widely available—I don't know what reading to recommend. Based on my understanding, the exact thing you are describing is never going to do anything helpful. So since you want to optimize the code anyway, the first thing you are going to need is not a list of optimizations to try, but a method to check if a given optimization does something. I don't know specifically about Fortran, but the first step is always to get a good profiler that can tell you which parts of your code are actually expensive, and you can use it to measure performance of modified code. It is much too easy to spend a lot of time for no gain.

The second major observation is that famous principle that the most helpful optimizations are those that avoid work, rather than those that do the same work but faster.

If you really do end up doing these micro-optimizations, one always starts with eliminating memory accesses and ensuring that memory access patterns are cache-friendly. These types of things compilers generally can't handle on their own.

$\endgroup$
2
  • $\begingroup$ As suggested in the answer and the comments to my question I took a profiler (Intel VTune Amplifier). Most optimizations I thought about did have a negligible impact on the run time. In contrast, accessing one very large array at two locations in the code (which is repeated quite often) seems to cost nearly 60% of the run time in the module that I optimize. Fascinating ... :-) $\endgroup$ Commented Jan 27, 2017 at 11:59
  • $\begingroup$ Correction: it does not cost 60% of the run time but something around 10%. I mixed two numbers. $\endgroup$ Commented Jan 27, 2017 at 15:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.