Improving very repetitive code

Hello everyone,

I am trying to improve a module that has the following form:

module repetitive
  implicit none
  private 
  public :: preferred_array

  contains

  subroutine preferred_array(idx, arr)
    integer, intent(in) :: idx
    real(dp), dimension(idx), intent(out) :: arr

    select case(idx)
      case(2)
        call get_array_2(2, arr)
      case(5)
       call get_array_5(5, arr)
     ! About a dozen cases of the same form here
    end select
  end subroutine preferred_array

  subroutine get_array_2(idx, arr)
    integer, intent(in) :: idx
    real(dp), dimension(idx), intent(out) :: arr
    !  Returns a very large array arr, that is hard-coded inside this subroutine
  end subroutine get_array_2
  ! Many more subrotines basically identical to the above one
end module repetitive

There are many things that I do not like in this module, and I would like to know if there is any better solution:

  • First, I understand that for performance reasons it is good to limit the IO operations, does this justify using this large hard-coded arrays? Large means a dozen of ~1000 element arrays.
  • If hard-coding is the best solution, wouldn’t it be better to declare them as module-level variables, and have a single get_array subroutine instead of a dozen?

I thank you in advance for your help with these questions, and any other suggestions regarding this code.

The code really appears to be redundant. Why specifying a function for each size and then take the size as an argument?

Does the algorithm to construct the arrays depend on the size of the arrays?

Furthermore does hard-coded mean that the result is always the same.

If so, I think its only neccessary to call the functions once, and they might not have a large influence on the total performance of the code and increasing the readability of the code may be more important than increasing the performance by a negligible amount. Withot knowing the code I can, however, not judge wheter this is actually the case.

Furthermore you talk about dozens of arrays with ~1000 elements, which is -assuming double orecision numbers- still less than one Megabyte. Is the goal to keep the data within the cache of the CPU or is it sone antique code from the time where 1 MB of RAM had been an issue. I do not believe the latter is true as it uses Fortan 2003 code.

Have you talked with the person responsible for the current code about the motivation?

If it is possible to rewrite the array selector code? You might also just rewrite it and then compaare it with the currend code on your specific platform.

1 Like

@Panadestein ,

Welcome to the forum!

Please take a look at this page for many learning resources on Fortran:
https://fortran-lang.org/en/learn/

You may then want to look up what is known in Fortran parlance as assumed-shape arrays versus what you have in your original post which is explicit-shape arrays. Unless advised otherwise due to some specific circumstances, you can consider assumed-shape arrays as parameters (received arguments) in your procedures and you will find the “information” on the array that is known to the procedures via the so-called array descriptors to be beneficial. You can then avoid having to author multiple procedures for different shapes of the same array as you’re thinking of in your original post.

Separately if you have situations such as “a very large array arr, that is hard-coded inside this subroutine,” look into what is referred to as named constant array, you can make it a PUBLIC entity of a module and other modules and your program(s) can then “USE” such a constant safely without requiring “getter” procedures of the kind you show in the original post:

module m
   ..
   real, parameter, public :: some_arr(*) = [ xx, yy, zz, ... ]
   ..
end module
   ..
   use m, only : ..., .., some_arr, ..
   ..
! make use of some_arr in code
2 Likes

Thanks a lot for the replies. I was given the task to refactor someone else’s code, and given the current form of the module, I saw the process as an opportunity to get accustomed to modern Fortran practices.

@Jweber The result is always the same. Inside the get_array there is an array arr of explicit shape arr(m, n), and the output is one of the columns of this array, selected according to an energy range.

@FortranFan I see, so you are suggesting a dedicated module containing only the arrays, and using it inside all the other modules that might need it. I like this idea.

If the fuctions just select columns, I really do not think that each colum needs its own function
Furthermore the memory is occupied anyway.