Beliavsky / FortranTip

Short instructional Fortran codes associated with Twitter @FortranTip
https://zmoon.github.io/FortranTipBrowser/
The Unlicense
64 stars 14 forks source link

Using the minimal memory for large logical arrays #48

Open urbanjost opened 2 years ago

urbanjost commented 2 years ago

According to the Fortran standard, a nonpointer scalar object that is default integer, default real, or default logical occupies a single numeric storage unit, This often means a default logical value occupies 32 bits even though it is often thought of as a 1-bit boolean.

Many compilers support logical kinds that require less storage. If you use large arrays of logical type you can see a significant reduction in your memory footprint by using kinds that use less bits.

The default kind is sometimes faster, but because less bits are required for smaller kinds the opposite may be true. Timing tests will tell you, but you may see a performance gain as well (and you want to test to make sure any change in kind is acceptable at all levels).

list available kinds

program whatkinds
use ISO_FORTRAN_ENV, only : logical_kinds
implicit none
   write(*,*)'! logical kinds are ',logical_kinds
end program whatkinds

You will typically find a KIND=1 is supported that takes 8 bits, and that the default will be 32 bits.

list storage for a specified KIND

program whatsizes
implicit none
character(len=*),parameter :: g='(*(g0))'
integer,parameter :: VALUE=1 ! use KIND values you found
logical(kind=VALUE) :: boolean
   write(*,g)"kind=",kind(boolean),",bits=",storage_size(boolean)
   write(*,g)"kind=",kind(.true.),",bits=",storage_size(.true.),"(default)"
end program whatsizes

typical output ...

kind=1,bits=8
kind=4,bits=32(default)

So in that case you can have the same functionality using 1/4 the memory by using a non-default kind.

tkoenig1 commented 2 years ago

Another possibility would be to use integer(kind=c_bool). While it is not guaranteed by the standard that this uses the smallest possible kind, in practice it is so (and it does not require source code changes).

urbanjost commented 2 years ago

I like that in that you do not have to query your compiler to find the LOGICAL kinds; and the variables with be C types which would allow calling C procedures. At least with the three I tried c_bool was one byte, the same as the smallest INTEGER and LOGICAL kinds available with those compilers. Perhaps putting it into a little module so it could be easily changed would be good too.

module m_small
use iso_c_binding, only : c_bool
implicit none
private
integer, parameter, public :: i_small=selected_int_kind(0) ! if using integers
integer, parameter, public :: l_small=c_bool
end module m_small

program whatsizes
use m_small,  only : l_small
implicit none
character(len=*), parameter :: g='(*(g0))'
logical(kind=l_small) :: boolean
   write ( *, g ) "kind=", kind(boolean), ", bits=", storage_size(boolean)
   write ( *, g ) "kind=", kind(.true.), ", bits=", storage_size(.true.), "(default)"
end program whatsizes