fortranfortran77

Fortran SWITCH construct speed


I know that computer science generally frowns on GOTO statements, but is there a speed advantage to be gained by using

    INTEGER WHICH
    GOTO(100,200,300) WHICH
100 do something
    GOTO 400
200 do something else
    GOTO 400
300 do a third thing
    GOTO 400
400 CONTINUE

versus

    INTEGER WHICH
    IF(WHICH.EQ.1)THEN
      do something
    ELSEIF(WHICH.EQ.2)THEN
      do something else
    ELSEIF(WHICH.EQ.3)THEN
      do a third thing
    ENDIF

since Fortran lacks a SWITCH statement?


Solution

  • First of all, as a general comment, these type of micro-optimizations are not really worth your time. More important is the answer to the question: What is easier to debug, understand and migrate?

    Obligatory post: GOTO still considered harmful?

    enter image description here


    Having that said, I, unfortunately, have to inform you that the OP has a bug in his code-examples (here comes the raptor). The computed GOTO statement which you use has the following properties:

    Execution of a computed GOTO statement causes evaluation of the scalar integer expression. If this value is i such that 1 ≤ i ≤ n where n is the number of labels in label-list, a branch occurs to the branch target statement identified by the ith label in the list of labels. If i is less than 1 or greater than n, the execution sequence continues as though a CONTINUE statement were executed.

    source: Fortran 2008 Standard

    This implies that the correct version should read:

        INTEGER WHICH
        GOTO(100,200,300) WHICH
        GOTO 400                  << missing part
    100 do something
        GOTO 400
    200 do something else
        GOTO 400
    300 do a third thing
        GOTO 400
    400 CONTINUE
    

    When you generate the assembly code of the following 3 files (see below), you will actually notice that the assembly code is identical under optimization. You can quickly check this yourself with the following lines in a bash script:

    $ for i in f{1,2,3}.f90; do ifort -O3 -S $i; done
    $ meld f{1,2,3}.s
    

    You will also notice that, if you remove the goto 400 which was missing, the goto assembly code will be faster by a few instructions.


    source of f1.f90:

    subroutine g(a)
      integer a
      goto(100,200,300) a
      goto 400
    100 call f1()
      goto 400
    200 call f2()
      goto 400
    300 call f3()
      goto 400
    400 continue
    end subroutine g
    

    source of f2.f90:

    subroutine g(a)
      integer a
      if(a.eq.1)then
         call f1()
      elseif(a.eq.2)then
         call f2()
      elseif(a.eq.3)then
         call f3()
      endif
    end subroutine g
    

    source of f3.f90:

    subroutine g(a)
      integer a
      select case (a)
      case (1)
         call f1()
      case (2)
         call f2()
      case (3)
         call f3()
      end select
    end subroutine g