Storage management

FEAT2 has an in-build storage management that identifies memory blocks via handles. Such handles are usually stored in structures instead of pointers to the actual memory. There are a couple of advantages in using this technique:

  • A handle is always of type integer. Therefore, structures containing handles to identify memory block simplify, the actual structure of the memory does not have to be declared.
  • A handle can be "shared" between different structures (just as a pointer as well). However, memory reallocation is more transparent. A reallocation does not change the handle, only the address that is associated to that handle.
  • The total memory of the program can be measured.
  • If memory allocation fails, the program can (theoretically) savely be aborted.
  • Memory can be accessed on external devices as well (e.g., on graphics cards) where no pointer exists.
  • Memory allocation can be tracked, memory holes can be found more easily.

The storage management is handled via the module storage.f90. The module has to be initialised at the beginning of the program via storage_init. At the end, storage_done has to be called which releases all allocated memory.

Allocation/use/deallocation of local memory

Allocation/use and deallocation of memory is always done in a very structured manner.

  • Allocation of memory is done via storage_new. This returns a handle to the memory.
  • With the handle, a pointer to the memory can be obtained via storage_getbase_XXXX (with XXXX depending on the type of the memory object).
  • If the memory is not needed anymore, it can be released via storage_free.

Here an example. We allocate a 1D array of 10 integers, initialise by zero, print the array and release it.

integer :: ihandle
integer, dimension(:), pointer :: p_Idata

...

! Allocate
call storage_new ("mysubroutine", "myarray", 10, &
    ST_INT, ihandle, ST_NEWBLOCK_ZERO)

! Get the memory block
call storage_getbase_int (ihandle, p_Idata)

! Print
write (*,*) p_Idata

! Release
call storage_free (ihandle)

A second example allocates a 2D double precision array with 2x20 entries, initialise by zero, print the array and release it.

integer :: ihandle
integer, dimension(:,:), pointer :: p_Ddata

...

! Allocate
call storage_new ("mysubroutine", "myarray", (/10,2/), &
    ST_DOUBLE, ihandle, ST_NEWBLOCK_ZERO)

! Get the memory block
call storage_getbase_double2d (ihandle, p_Ddata)

! Print
write (*,*) p_Ddata

! Release
call storage_free (ihandle)

Data types

The storage management supports a lot of different data/handle types, identified by a set of constants:

Constant Data type
ST_SINGLE Single precision array
ST_DOUBLE Double precision array
ST_QUAD Quadrupel precision array
ST_SINGLE_COMPLEX Single precision complex array
ST_DOUBLE_COMPLEX Double precision complex array
ST_QUAD_COMPLEX Quadrupel precision complex array
ST_LOGICAL Bool array
ST_CHAR Character array
ST_INT Standard integer array
ST_INT8 8-bit integer array
ST_INT16 16-bit integer array
ST_INT32 32-bit integer array
ST_INT64 64-bit integer array

These constants can be specified in the storage_new command as type for the array. For example, the following call allocates a double precision array of size isize and returns a handle ihandle to it.

call storage_new (..., ..., isize, ST_DOUBLE, ihandle, ...)

The additional constant ST_NOHANDLE is used to identify a non-associated handle. storage_new returns ST_NOHANDLE if the allocation fails.

Supported array formats

The storage management supports three types of data arrays: 1D, 2D and 3D arrays. For each type of data and dimension, there exist a separate storage_new routine for creation and a storage_getbase_XXXX routine to retrieve the pointer behind the handle.

The creation of the array is always done with storage_new, which has a different syntax depending on whether a 1D, 2D or 3D array is created:

Dim Creation of the array
1D call storage_new ( ..., isize, ST_DOUBLE, ihandle, ...)
2D call storage_new (..., (/ isize1, isize2 /), ST_DOUBLE, ihandle, ...)
2D call storage_new (..., (/ isize1, isize2, isize3 /), ST_DOUBLE, ihandle, ...)

For retrieving the pointer, one uses the appropriate storage_getbase_XXXXyD routine:

Dim Type Retrieving the pointer
1D integer integer, dimension(:), pointer :: p_Idata
call storage_getbase_int (ihandle,p_Idata)
2D integer integer, dimension(:,:), pointer :: p_Idata
call storage_getbase_int2D (ihandle,p_Idata)
3D integer integer, dimension(:,:,:), pointer :: p_Idata
call storage_getbase_int3D (ihandle,p_Idata)
1D double real(DP), dimension(:), pointer :: p_Ddata
call storage_getbase_double (ihandle,p_Ddata)
2D double real(DP), dimension(:,:), pointer :: p_Ddata
call storage_getbase_double2D (ihandle,p_Ddata)
3D double real(DP), dimension(:,:,:), pointer :: p_Ddata
call storage_getbase_double3D (ihandle,p_Ddata)
... ... ...

Destruction of an array is always done with storage_free.