Community
cancel
Showing results for 
Search instead for 
Did you mean: 
Highlighted
Beginner
43 Views

Dynamically allocated array address changes after constructor

I have been updating some fortran modeling software for streamflow simulation. Until recently I had primarily used gfortran for compiling the code. Having obtained a license for the Intel compilers on Macos I have started testing my code with ifort.

I have run into a problem with using pointers to dynamically allocated arrays. We have an object which handles the output of summary information from other objects' arrays. For efficiency all the other objects use the summary object to set pointers to the required dynamic arrays within those objects. This happens during the initialization/constructor phase of the program. With gfortran this has worked quite well but with ifort I have been getting segmentation faults. What appears to be happening is the address for a dynamically allocated array is different after returning from an object's constructor compared to what is after allocation within the constructor. Because the summary object's pointer is set during the constructor phase for each object (after an array has been allocated) this is causing all manner of mischief. After a lot of digging I have created what is hopefully a simple, reproducible test case.

I'm not sure if I'm just missing something incorrect in my approach or if this is some bug. When I run the test with gfortran the address for an array stays constant during program execution, however, with ifort the address for an array changes after returning from the constructor. I would appreciate any help/insight you could provide.

-parker

0 Kudos
12 Replies
Highlighted
Beginner
43 Views

Hello Parker

 

just a quick thing. Have you tried to remove the call within the constructor to outside of the contructer.

So call the constructor and use its result to make the other call were you set the pointer

I suspect there could be an issue, since I faced similar problem before and that workaround worked for me. 

If that is the problem that it could be a bug?

 

 

0 Kudos
Highlighted
Beginner
43 Views

I am reasonably certain that your constructor is building and returning a new "anonymous" ModC object.  Then, a deep-copy of that object is being done on line 103:

module_c = ModC()

During this assignment, the 'module_c%first_arr' component is (re)allocated to match the size of the anonymous return value ModC%first_arr - then all of the data is copied from the anonymous result %first_arr into module_c%first_arr.  I believe the pointer component is just being copied (hence your problem).  So, after this assignment, module_c%first_arr is a newly allocated array that contains the same data as the object returned from the ModC() constructor - so it has a different address.  Since the pointer component is (probably) copied, it is still pointing to the %first_arr component of the ModC object being returned by the constructor.

This is standard Fortran behavior to my knowledge - perhaps gfortran is just being "smart" by recognizing that the return value from the ModC() constructor is not otherwise used so it is just "moving" all of the components from the returned object into the module_c instance.

 

0 Kudos
Highlighted
Honored Contributor I
43 Views

nathan, parker wrote:

.. I'm not sure if I'm just missing something incorrect in my approach or if this is some bug ..

You are employing intrinsic assignment with your derived type and in your particular situation, the behavior is effectively processor-dependent.  You need to either refine your derived type design to work as you want given the rules in the standard with intrinsic assignment.  Or you need to implement "defined assignment" to gain control over deep/shallow copy.  Per the standard rules for intrinsic assignment, you can kinda view the component with the ALLOCATABLE attribute as getting a "deep copy" whereas that with POINTER attribute with a "shallow" one, note though the language in the standard is different.  It just so happens the gfortran copy of the ALLOCATABLE component is an "efficient" one, a move!  But you can't quite rely on that as portable behavior.  You can try the somewhat shorter code below to compare and contrast the behavior with the 2 compilers:

module m
   type :: t
      integer, allocatable :: i
      integer, pointer :: ptr_i => null()
      character(len=10) :: id = ""
   end type
   interface t
      module procedure construct_t
   end interface
contains
   function construct_t() result( new_t )
      type(t), target :: new_t
      new_t%id = "new_t"
      new_t%i = 42
      new_t%ptr_i => new_t%i
      call print_addr( new_t )
   end function
   subroutine print_addr( this )
      type(t), intent(in) :: this
      print *, "object id:", trim(this%id)
      print *, "loc(this%i) = ", loc(this%i)
      print *, "loc(this%ptr_i) = ", loc(this%ptr_i)
   end subroutine
end module
   use m
   type(t) :: foo
   foo = t()
   foo%id = "foo"
   call print_addr( foo )
end

Upon execution with Intel Fortran,

 object id:new_t
 loc(this%i) =  2418441855344
 loc(this%ptr_i) =  2418441855344
 object id:foo
 loc(this%i) =  2418441855200
 loc(this%ptr_i) =  2418441855344

 

By the way, if I'm not mistaken, in your set_ptr procedure, the pointer association will be undefined once the procedure returns per the Fortran standard i.e., unless the actual argument has the TARGET (or POINTER) attribute.

Separately, you may want to reconsider your derived type design: if you can provide a more descriptive example of how exactly you plan to work with your "class" data, readers can perhaps offer other suggestions. 

0 Kudos
Highlighted
Beginner
43 Views

@FortranFan

 

I tried  taking the call routine within the constructor and moved it out, you then obtain the expected results. 

I still don’t understand it though. I had similar problem with constructors within constructors. At some point in execution the address simply just changed

 

is it generelly wise to have any calls whatsoever within a Fortran function?

 

0 Kudos
Highlighted
Black Belt Retired Employee
43 Views

Calls within a function are generally fine, as long as there are no side-effects on other things referenced in the expression that calls the function.

0 Kudos
Highlighted
Beginner
43 Views

Thank you for the quick replies from everyone. My actual implementation is slightly more complicated than the example I provided. I wrote the example to get at the meat of the problem I am experiencing without going into more detail. Below is a fairly brief explanation of how I am using this in my code.

The model code has multiple objects/classes for the different physics required for a simulation. Each object has a mix of allocatable scalars and arrays that are used during computation; arrays only contain a single timestep of values over the model domain. There is a configuration file which contains (among other things) a list of variables are output to a netCDF summary file. These output variables can come from any of the requested physics objects. All summary -related operations are handled by a summary object. The summary object maintains an array of pointers to the output variables.

During the constructor phase the summary object is initialized first and then is passed to each physics object so they can set pointers to any of the scalars/arrays that are selected for output. When the model runs, the summary object writes the values to a file at the end of each timestep by using those pointers back to the original data.  

I've attached another somewhat simple example that hopefully will give you an idea of what I'm doing. If the address of a given array changes after the constructor (at least with ifort) then what is the best way to handle this type of situation? I've been experimenting with defined assignment but so far I haven't seen any change in behavior. It's entirely possible that I'm just not understanding how to do this correctly. Granted there is room for interpretation in the standard on this, but the current approach by gfortran seems more intuitive, at least to me.

Let me know if you need more information or something I've said just doesn't make sense.

 

-parker

 

 

0 Kudos
Highlighted
Honored Contributor I
43 Views

norton, parker wrote:

.. Let me know if you need more information or something I've said just doesn't make sense. ..

If the main program you show in your latest file is your most likely use case of your derived types, your easiest option might be to follow the simple-minded approach which you will know of i.e., to transform your "constructor" function into type-bound subroutine "setter"/"initialization" procedures.  And give your 'class' instance the TARGET attribute; toward this, you may want to investigate any performance impact in terms of missed optimization and check with Intel Sppport toward this as well.  To illustrate this with the simpler example, so you can follow the KISS principle as follows:

module m
   type :: t
      integer, allocatable :: i
      integer, pointer :: ptr_i => null()
      character(len=10) :: id = ""
   contains
      private
      procedure, pass(this) :: init_t
      generic, public :: init => init_t
   end type
contains
   subroutine init_t( this, i, id )
      class(t), intent(inout), target :: this
      integer, intent(in)             :: i
      character(len=*), intent(in)    :: id
      this%id = id
      this%i = i
      this%ptr_i => this%i
   end subroutine
   subroutine print_addr( this )
      type(t), intent(in) :: this
      print *, "object id: ", trim(this%id)
      print *, "loc(this%i) = ", loc(this%i)
      print *, "loc(this%ptr_i) = ", loc(this%ptr_i)
   end subroutine
end module
   use m
   type(t), target :: foo
   call foo%init( i=42, id="foo" )
   call print_addr( foo )
end

Upon execution with gfortran, you should find like htis:

 object id: foo
 loc(this%i) =              18882432
 loc(this%ptr_i) =              18882432

And with Intel Fortran,

 object id: foo
 loc(this%i) =  2181581445808
 loc(this%ptr_i) =  2181581445808

Given what the Fortran standard includes in terms of structure construction, which is rather limiting and nothing like NEW keyword in several widely-used OO languages, and how your "construction" instruction such as 'module_c = ModC()' translates effectively into a hodge-podge of a 'copy' operation, the use of a constructor can be more trouble than be beneficial.

0 Kudos
Highlighted
Honored Contributor I
43 Views

AT90 wrote:

@FortranFan

 

I tried  taking the call routine within the constructor and moved it out, you then obtain the expected results. 

I still don’t understand it though. I had similar problem with constructors within constructors. At some point in execution the address simply just changed

 

is it generelly wise to have any calls whatsoever within a Fortran function?

 

See Quote #8.

With functions especially, you can try to follow a rule which is to apply the PURE attribute always which will then require you to adhere to certain constraints to help you minimize or avoid side-effects.  So, meet the PURITY requirements of functions and you will find good value in their use, particularly in For(mula) Tran(slation) e.g., with expressions in code.

0 Kudos
Highlighted
Beginner
43 Views

So based on both Steve and FortranFan's answers, setting pointers in a function are not recommended? As  pointers will break the pure rules ?

0 Kudos
Highlighted
Valued Contributor I
43 Views

You can use function with pointer references, e.g. for data which you don't want to copy around, as copying those data structures might be costly. For elemental procedures, you have to use

impure elemental

then. But for the other purpose, we often use setter and getter functions, e.g.

function struct1_get_struct2_ptr (struct) result (struct2)
   type(struct2_t), pointer :: struct2
   type(struct1_t), intent(in), target :: struct2
   struct2 => struct1%struct2
end function struct1_get_struct2_ptr

for a hierarchy of data structures.

0 Kudos
Highlighted
Beginner
43 Views

@FortranFan

Thank for this! I hadn't thought to try using an init subroutine instead of a constructor. This does work for me with both gfortran and ifort. I ended up using a slight variation on what you posted. It works but I'm wondering if it's considered legal by the standard. I've modified your example to show in general the approach I used.

module m
   type :: t
      integer, allocatable :: i
      integer, pointer :: ptr_i => null()
      character(len=10) :: id = ""
   contains
      private
      procedure, pass(this) :: init_t
      generic, public :: init => init_t
      procedure, public :: set_ptr
   end type
contains
   subroutine init_t( this, i, id )
      class(t), intent(inout) :: this
      integer, intent(in)             :: i
      character(len=*), intent(in)    :: id
      this%id = id
      this%i = i
      call this%set_ptr(this%i)
      ! this%ptr_i => this%i
   end subroutine

   subroutine print_addr( this )
      type(t), intent(in) :: this
      print *, "object id: ", trim(this%id)
      print *, "loc(this%i) = ", loc(this%i)
      print *, "loc(this%ptr_i) = ", loc(this%ptr_i)
   end subroutine

   subroutine set_ptr(this, var)
    class(t), intent(inout) :: this
    integer, target, intent(in) :: var

    this%ptr_i => var
   end subroutine
end module

program test
   use m
   type(t) :: foo
   call foo%init( i=42, id="foo" )
   call print_addr( foo )
end program

So basically I removed the two target attributes and then created a new subroutine for the pointer assignment that does use the target attribute for the input variable. This works perfectly in my code for Intel and GNU. Is this a reasonable alternative approach in light of the standard?

-parker

0 Kudos
Highlighted
Honored Contributor I
43 Views

norton, parker wrote:

.. basically I removed the two target attributes and then created a new subroutine for the pointer assignment that does use the target attribute for the input variable. This works perfectly in my code for Intel and GNU. Is this a reasonable alternative approach in light of the standard?

Unfortunately, no.  As I alluded to earlier. there is a specific reason for the TARGET attribute in my example, particularly for the actual variable (such as 'mod_summary' variable in the main program in your 'summary.f90' file).  Without the TARGET attribute, the standard offers no guarantee the pointer remains defined e.g., "this%ptr_i => var" in subprogram 'set_ptr' once the procedure instance completes execution i.e., when the effective argument corresponding to the dummy argument 'var' does not have the TARGET attribute.  That you find Intel and gfortran/GCC compilers to work as you would like them to with your revised example in Quote #12 should be of no comfort, you cannot rely on such behavior until and unless the Fortran standard is updated to support such a situation which appears unlikely.  Note, unlike some other languages such as C where there is considerable flexibility with an object to point to another object that can then hinder optimization, the Fortran standard - starting with the 90 revision - extends a restricted facility with the 2 attributes (TARGET and POINTER) to programmers given performance considerations among other matters.

Nonetheless, you may take note pointers generally and the POINTER attribute in Fortran get very tricky and if you can do without them, all the better because you and anyone else working on your code will find it much cleaner.  By the way, in this situation, if your use case is really post-processing of simulation data from your "class" instances (derived type variables) based on certain criteria and/or logic (e.g., certain time intervals), you may want to consider user-defined derived-type IO (DTIO) procedures bound to your types, that way all the criteria/logic is collocated with the 'class' itself and you may find it easier to maintain and extend as your work progresses.

0 Kudos