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
.