- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Hi There,
I have a global allocatable vector, say n(:), defined in a module. It is big, in MB or even GB, with values either in the range -218~217, or -32768~32767, or -2147483648~ 2147483647, which can be represented by a 1-byte, 2-byte and 4 byte integer variable, respectively. The problem is, the value range can only be determined at runtime. I know I can define 3 vectors (say n1(:), n2(:), and n4(:)) and only allocate and use the right integer type of n vector at runtime. However, this makes the code quite cumbersome and also a bit slow (?), using a lot of if-else-then to determine which vector to allocate and use. In Fortran 2003, the polymorphic data type was introduced. However, it seems to be not applicable to my case, as the n(:) vector has to be repeatedly reset values, which are then used in the computation.
Anyone can kindly help, please?
Thanks a lot!
- Tags:
- myCode
Link Copied
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
If you can provide a specific description of what you seek and preferably with a code example (or two), some readers here may be able to help.
But otherwise, the responses are likely going to be the Fortran language does NOT include a convenient facility toward your interest. I personally don't think polymorphism will help you either, at least based on your original post.
But a workaround for you may be to think along the lines of object-oriented (OO) code design and encapsulate the instructions of your data type in a MODULE (or even a derived type in a MODULE, perhaps even a parameterized derived type a la a 'class' per OO terminology). And bring to bear the limited generic facilities in Fortran in this MODULE/'class' using the 'KIND' facility in Fortran.
Once you do this, then on the caller side you may be able to simplify matters to some extent by only requiring, say, a single IF..THEN..ELSE.. or better yet, a single SELECT CASE construct.
You may not find this to be "super great", but maybe you will see this as better than your other alternatives.
For an illustration using REAL(KIND) - as opposed to your situation which is with INTEGER kind - take a look at this link: https://community.intel.com/t5/Intel-Fortran-Compiler/REAL-KIND/m-p/1137892/highlight/true#M136254
You may get the idea(s) from this link.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
The use of a polymorphic data type will not make the code any faster. While it will (may) hide the type, type selection dispatch, it will still be there.
Note, because of the convenience of having the runtime code make this type selection, the tendency is to then pass the polymorphic type throughout the application. IOW every step along the way will incorporate the dispatch (non-optimal coding).
The better route (from performance perspective) is to use three or four different sets of subroutines, each only differ in type, then select the type at the outer most call level. Elimination of redundant algorithms can be eliminated with use of INCLUDE.
Jim Dempsey
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Thank you both for the reply!
Maybe I am not quite clear what I want to do by NOT providing an example. Here it is
Module MyVar
IMPLICIT NONE
INTEGER, PARAMETER :: I4B = SELECTED_INT_KIND(9)
INTEGER(I4B), Allocatable :: N4(:)
END MODULE MyVar
Program Main
USE MyVar
INTEGER(I4B) :: NLen, NRange
! get NLen and NRange from an input file
Allocate(N4(NLen))
DO
!Set values in N4(i), for i =1~NLen
!Use N4 in computation
!If the computational result satisfies certain conditions, exit DO,
! and the job is done
END DO
END Program Main
The problem is that NLen (say in millions) can be large, resulting in a huge N4 vector with possibly in several GBs. On the other hand, the range of values of N4 might be -128~127, -32768~32767, or -2147483648~2147483647. If it is the first case, using a 1-byte integer vector would save 3/4 of RAM. So to save RAM, and possibly to make the program run faster, I could do the following.
Module MyVar
IMPLICIT NONE
INTEGER, PARAMETER :: I4B = SELECTED_INT_KIND(9)
INTEGER, PARAMETER :: I2B = SELECTED_INT_KIND(4)
INTEGER, PARAMETER :: I1B = SELECTED_INT_KIND(2)
INTEGER(I4B), Allocatable :: N4(:)
INTEGER(I2B), Allocatable :: N2(:)
INTEGER(I1B), Allocatable :: N1(:)
END MODULE MyVar
Program Main
USE MyVar
INTEGER(I4B) :: NLen, NRange
! get NLen and NRange from an input file
IF(NRange < 128) THEN
Allocate(N1(NLen))
ELSE IF(NRange < 32768) THEN
Allocate(N2(NLen))
ELSE
Allocate(N4(NLen))
END IF
DO
!Set values in N4(i), N2(i) or N1(i) depending on NRange, for i =1~NLen
!Use N4(i), N2(i) or N1(i) depending on NRange in computation
!If the computational result satisfies certain conditions, exit DO,
! and the job is done
END DO
END Program Main
The first code is more elegant, but can waste 3/4 RAM and as a result can be slower. The 2ed uses the smallest possible RAM and as a result can run faster (less likely to miss cache?). However, the code becomes longer, especially considering that the N vector is used many times (places) within the do loop, and each time one has to use the if-else-then or select case to choose the right one.
Between short, concise code and long but efficient (in RAM and CPU use), I would prefer the latter. So between the above two options, I would like the 2nd one. However, I would like to check if there are better solutions in terms of both. I am not quite familiar with Fortran 2003 and later extensions.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
In case you are willing to consider current Fortran standard, you can review an example such as the following: this is a variant of what I show above but adapted somewhat to the example you listed:
module kinds_m
use, intrinsic :: iso_fortran_env, only : I1 => int8, I2 => int16, &
I4 => int32, I8 => int64
end module
module dat_m
use kinds_m, only : I1, I2, I4, I8
type :: dat_t(K, L)
integer, kind :: K = I1
integer, len :: L
integer(K) :: n(L)
contains
procedure, pass(this) :: do_work_i1
procedure, pass(this) :: do_work_i2
procedure, pass(this) :: do_work_i4
procedure, pass(this) :: do_work_i8
generic :: do_work => do_work_i1, do_work_i2, do_work_i4, do_work_i8
end type
contains
subroutine do_work_i1( this ) !<-- add arguments as needed
class(dat_t(K=I1,L=*)), intent(inout) :: this
integer :: i
do i = 1, this%l
this%n(I) = I
end do
print *, "do_work_i1: this%n(1) = ", this%n(1)
end subroutine
subroutine do_work_i2( this ) !<-- add arguments as needed
class(dat_t(K=I2,L=*)), intent(inout) :: this
integer :: i
do i = 1, this%l
this%n(I) = I
end do
print *, "do_work_i2: this%n(1) = ", this%n(1)
end subroutine
subroutine do_work_i4( this ) !<-- add arguments as needed
class(dat_t(K=I4,L=*)), intent(inout) :: this
integer :: i
do i = 1, this%l
this%n(I) = I
end do
print *, "do_work_i4: this%n(1) = ", this%n(1)
end subroutine
subroutine do_work_i8( this ) !<-- add arguments as needed
class(dat_t(K=I8,L=*)), intent(inout) :: this
integer :: i
do i = 1, this%l
this%n(I) = I
end do
print *, "do_work_i8: this%n(1) = ", this%n(1)
end subroutine
end module
program p
use kinds_m, only : I1, I2, I4, I8
use dat_m, only : dat_t
integer :: nlen, nrange
print *, "Enter nlen:"
read *, nlen
print "(*(g0,1x))", "Enter nrange: one of ", range(1_i1), range(1_i2), range(1_i4), range(1_i8)
read *, nrange
select case ( nrange )
case ( range(1_i1) )
blk_i1: block
type(dat_t(K=I1,L=nlen)) :: dat
call dat%do_work()
end block blk_i1
case ( range(1_i2) )
blk_i2: block
type(dat_t(K=I2,L=nlen)) :: dat
call dat%do_work()
end block blk_i2
case ( range(1_i4) )
blk_i4: block
type(dat_t(K=I4,L=nlen)) :: dat
call dat%do_work()
end block blk_i4
case ( range(1_i8) )
blk_i8: block
type(dat_t(K=I8,L=nlen)) :: dat
call dat%do_work()
end block blk_i8
case default
print *, "Invalid nrange"
end select
end program
You can try executing the above code as follows:
C:\Temp>ifort /standard-semantics /warn:all /stand:f18 p.f90
Intel(R) Visual Fortran Intel(R) 64 Compiler for applications running on Intel(R) 64, Version 19.1.3.311 Build 20201010_000000
Copyright (C) 1985-2020 Intel Corporation. All rights reserved.
Microsoft (R) Incremental Linker Version 14.26.28806.0
Copyright (C) Microsoft Corporation. All rights reserved.
-out:p.exe
-subsystem:console
p.obj
C:\Temp>p.exe
Enter nlen:
3
Enter nrange: one of 2 4 9 18
2
do_work_i1: this%n(1) = 1
C:\Temp>p.exe
Enter nlen:
5
Enter nrange: one of 2 4 9 18
18
do_work_i8: this%n(1) = 1
C:\Temp>
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
Note since your question is somewhat general in nature for Fortran, you can also consider inquiring at https://fortran-lang.discourse.group/ and comp.lang.fortran for broader feedback from other Fortran users.
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
- Subscribe to RSS Feed
- Mark Topic as New
- Mark Topic as Read
- Float this Topic for Current User
- Bookmark
- Subscribe
- Printer Friendly Page