'Order of Operations in Fortran

I'm a new Fortran 90 user and have a relatively simple question. Within a subroutine I'm running the following code:

y = (1-crra)*(f(llc(1)  , llc(2)  , llc(3)  , llc(4)  ))**(1/(1-crra))*(1-xin)*(1-yin)*(1-zin)*(1-qin) &
  + (1-crra)*(f(llc(1)+1, llc(2)  , llc(3)  , llc(4)  ))**(1/(1-crra))*xin    *(1-yin)*(1-zin)*(1-qin) &
  + (1-crra)*(f(llc(1)  , llc(2)+1, llc(3)  , llc(4)  ))**(1/(1-crra))*(1-xin)*yin    *(1-zin)*(1-qin) &
  + (1-crra)*(f(llc(1)  , llc(2)  , llc(3)+1, llc(4)  ))**(1/(1-crra))*(1-xin)*(1-yin)*zin    *(1-qin) &
  + (1-crra)*(f(llc(1)  , llc(2)+1, llc(3)+1, llc(4)  ))**(1/(1-crra))*(1-xin)*yin    *zin    *(1-qin) &
  + (1-crra)*(f(llc(1)+1, llc(2)  , llc(3)+1, llc(4)  ))**(1/(1-crra))*xin    *(1-yin)*zin    *(1-qin) &
  + (1-crra)*(f(llc(1)+1, llc(2)+1, llc(3)  , llc(4)  ))**(1/(1-crra))*xin    *yin    *(1-zin)*(1-qin) &
  + (1-crra)*(f(llc(1)+1, llc(2)+1, llc(3)+1, llc(4)  ))**(1/(1-crra))*xin    *yin    *zin    *(1-qin) &
  + (1-crra)*(f(llc(1)  , llc(2)  , llc(3)  , llc(4)+1))**(1/(1-crra))*(1-xin)*(1-yin)*(1-zin)*qin     &
  + (1-crra)*(f(llc(1)+1, llc(2)  , llc(3)  , llc(4)+1))**(1/(1-crra))*xin    *(1-yin)*(1-zin)*qin     &
  + (1-crra)*(f(llc(1)  , llc(2)+1, llc(3)  , llc(4)+1))**(1/(1-crra))*(1-xin)*yin    *(1-zin)*qin     &
  + (1-crra)*(f(llc(1)  , llc(2)  , llc(3)+1, llc(4)+1))**(1/(1-crra))*(1-xin)*(1-yin)*zin    *qin     &
  + (1-crra)*(f(llc(1)  , llc(2)+1, llc(3)+1, llc(4)+1))**(1/(1-crra))*(1-xin)*yin    *zin    *qin     &
  + (1-crra)*(f(llc(1)+1, llc(2)  , llc(3)+1, llc(4)+1))**(1/(1-crra))*xin    *(1-yin)*zin    *qin     &
  + (1-crra)*(f(llc(1)+1, llc(2)+1, llc(3)  , llc(4)+1))**(1/(1-crra))*xin    *yin    *(1-zin)*qin     &
  + (1-crra)*(f(llc(1)+1, llc(2)+1, llc(3)+1, llc(4)+1))**(1/(1-crra))*xin    *yin    *zin    *qin

When I replace that chunk of code with the following code I get around a 2X speed-up:

y = (((1-crra)*f(llc(1)  , llc(2)  , llc(3)  , llc(4)  ))**(1/(1-crra)))*(1-xin)*(1-yin)*(1-zin)*(1-qin) &
  + (((1-crra)*f(llc(1)+1, llc(2)  , llc(3)  , llc(4)  ))**(1/(1-crra)))*xin    *(1-yin)*(1-zin)*(1-qin) &
  + (((1-crra)*f(llc(1)  , llc(2)+1, llc(3)  , llc(4)  ))**(1/(1-crra)))*(1-xin)*yin    *(1-zin)*(1-qin) &
  + (((1-crra)*f(llc(1)  , llc(2)  , llc(3)+1, llc(4)  ))**(1/(1-crra)))*(1-xin)*(1-yin)*zin    *(1-qin) &
  + (((1-crra)*f(llc(1)  , llc(2)+1, llc(3)+1, llc(4)  ))**(1/(1-crra)))*(1-xin)*yin    *zin    *(1-qin) &
  + (((1-crra)*f(llc(1)+1, llc(2)  , llc(3)+1, llc(4)  ))**(1/(1-crra)))*xin    *(1-yin)*zin    *(1-qin) &
  + (((1-crra)*f(llc(1)+1, llc(2)+1, llc(3)  , llc(4)  ))**(1/(1-crra)))*xin    *yin    *(1-zin)*(1-qin) &
  + (((1-crra)*f(llc(1)+1, llc(2)+1, llc(3)+1, llc(4)  ))**(1/(1-crra)))*xin    *yin    *zin    *(1-qin) &
  + (((1-crra)*f(llc(1)  , llc(2)  , llc(3)  , llc(4)+1))**(1/(1-crra)))*(1-xin)*(1-yin)*(1-zin)*qin     &
  + (((1-crra)*f(llc(1)+1, llc(2)  , llc(3)  , llc(4)+1))**(1/(1-crra)))*xin    *(1-yin)*(1-zin)*qin     &
  + (((1-crra)*f(llc(1)  , llc(2)+1, llc(3)  , llc(4)+1))**(1/(1-crra)))*(1-xin)*yin    *(1-zin)*qin     &
  + (((1-crra)*f(llc(1)  , llc(2)  , llc(3)+1, llc(4)+1))**(1/(1-crra)))*(1-xin)*(1-yin)*zin    *qin     &
  + (((1-crra)*f(llc(1)  , llc(2)+1, llc(3)+1, llc(4)+1))**(1/(1-crra)))*(1-xin)*yin    *zin    *qin     &
  + (((1-crra)*f(llc(1)+1, llc(2)  , llc(3)+1, llc(4)+1))**(1/(1-crra)))*xin    *(1-yin)*zin    *qin     &
  + (((1-crra)*f(llc(1)+1, llc(2)+1, llc(3)  , llc(4)+1))**(1/(1-crra)))*xin    *yin    *(1-zin)*qin     &
  + (((1-crra)*f(llc(1)+1, llc(2)+1, llc(3)+1, llc(4)+1))**(1/(1-crra)))*xin    *yin    *zin    *qin

Why am I getting a speed-up here? Is there a general Fortran lesson here or is this specific to my application?



Solution 1:[1]

I can't comment about speed without knowing more about your variables and the function f. But can I recommend generating y algorithmically rather than with a single huge expression?

Something like

! I'm guessing at the types of these.
integer :: arguments(2,4) 
integer :: factors(2,4)

arguments(1,:) = llc
arguments(2,:) = llc+1

factors(1,:) = 1-[xin, yin, zin, quin]
factors(2,:) = [xin, yin, zin, quin]

! Initialise `y` to `0`.
y = 0

! Loop over four indices, one each for x, y, z and q.
do i=1,2
  do j=1,2
    do k=1,2
      do l=1,2
        ! Calculate the contribution to `y`.
        y = y &
          + f(arguments(i,1), arguments(j,2), arguments(k,3), arguments(l,4)) &
          ** (1/(1-crra)) &
          * factors(i,1)*factors(j,2)*factors(k,3)*factors(l,4)
      enddo
    enddo
  enddo
enddo

! Multiply `y` by `(1-crra)`.
y = y*(1-crra)

This might run slower than your code, but with a bit of refactoring it should be possible to make it faster.

The advantages of this kind of approach are that it's much more readable and maintainable.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1