2

I have a subroutine which contains a simple if-statement right in the middle which I would like to get rid of. The reason being that I know in advance if that statement will be true or false based on where the call occurs and not based on the input data. This is even more attractive since it is a 1-liner if that really does not do too much.

Right now there is an extra logical input which is purely for that if statement and it is called twice - once with "false" and once with "true".

Essentially, I want to make the if-statement work out during compilation, but an #ifdef will not allow for two different usages/configurations of the same subroutine.

The dumb solution would be to copy the subroutine completely and have one instance with the "true" segment while the other with the "false" segment only.

However, besides this being far from elegant (and have 99.9% similarity), this is an opening for trouble, once the subroutine is changed in the future.

I will note that I do not mean overloading the subroutine (at least not in the "traditional" sense of it), although this is sort of the same general idea.

So my question is - is there a solution to this situation or I will have to leave it as a run-time if statement and have an additional logical true/false input?

I'm using Fortran 90/95.

  • 2
    Please try to show code next time, it is much easier and more accurate to read code than trying to interpret your words. See how the answer starts: "So my understanding is that the code is something like". The answerer had to recreate everything and can't even be sure he did it correctly. – Vladimir F Героям слава May 30 '18 at 06:30
  • 1
    Unless you're trying to vectorize, branches that are obvious are often 'predicted', minimizing the hit to performance. See here: https://stackoverflow.com/a/11227902/4872873 – Ross May 30 '18 at 14:01

1 Answers1

4

So my understanding is that the code is something like:

module mymod
    implicit none
contains
    subroutine mysub(a, l)
        integer, intent(inout) :: a
        logical, intent(in) :: l
        a = mod(a**3, 53)
        if (l) then
            a = a + 1
        else
            a = a - 1
        end if
    end subroutine mysub
end module mymod


program myprog
    use mymod
    implicit none
    integer :: b
    b = 10
    call mysub(b, .TRUE.)
    print *, b
    call mysub(b, .FALSE.)
    print *, b
end program myprog

And you are concerned that the mysub subroutine is not as efficient as it can be because it has an if statement, even though the compiler knows at compile time which of the paths it will take each time.

Before you investigate this further, I'd strongly suggest you evaluate whether that's necessary. Everything you can do will make the code less readable, less maintainable, and is likely to deliver marginal performance increase at best.

You could write two different subroutines, and have the parts that are identical in a separate file and INCLUDE it into both subroutines, like this:

same.inc:

        integer, intent(inout) :: n
        n = mod(n**3, 53)

sub.f90:

module mymod
    implicit none
contains
    subroutine mysub_true(n)
        include 'same.inc'
        n = n + 1
    end subroutine mysub_true
    subroutine mysub_false(n)
        include 'same.inc'
        n = n - 1
    end subroutine mysub_false
end module mymod

Together with the preprocessor, you can even go all-in:

mysub.inc:

#if PATH == 1
#define SUB_NAME mysub_true
#else
#define SUB_NAME mysub_false
#endif

    subroutine SUB_NAME(n)
        integer, intent(inout) :: n
        n = mod(n**3, 53)
#if PATH == 1
        n = n + 1
#else
        n = n - 1
#endif
    end subroutine SUB_NAME

#undef SUB_NAME
#undef PATH

mymod.F90:

module mymod
    implicit none
contains

#define PATH 1
#include mysub.inc

#define PATH 0
#include mysub.inc

end module mymod

myprog.F90:

program myprog
    use mymod
    implicit none
    integer :: b
    b = 10
    call mysub_true(b)
    print *, b
    call mysub_false(b)
    print *, b
end program myprog

and compile with

gfortran -c -o mymod.o -cpp mymod.F90
gfortran -o myprog myprog.F90 mymod.o

Whether that makes it easier or harder to read depends on the complexity of the parts that are the same.

chw21
  • 7,970
  • 1
  • 16
  • 31
  • didn't you mean ´INCLUDE´ instead of ´IMPORT´? – Rodrigo Rodrigues May 30 '18 at 00:39
  • Hi, Thanks for the reply. First to point out - I'm not worried about the performance, it is clearly not the main issue here. About the #include - I guess that can work. Any way that does not include using an extra code file? – TheWhitestOfFangs May 30 '18 at 14:30
  • If you're not worried about the performance, why don't you just wrap the common part of the routine in a separated routine (private to the module), and write 2 versions of the routine? You may use a generic interface for them, with a common name for the caller, if you want to. – Rodrigo Rodrigues May 30 '18 at 19:30
  • So that was something I thought about, but that would end up too messy. But thanks for the idea! – TheWhitestOfFangs May 30 '18 at 20:52