Fortran 90 difference between compaq visual fortra

2019-02-21 03:09发布

This may be a specific question, but I think it pertains to how memory is handled with these two compilers (Compaq visual Fortran Optimizing Compiler Version 6.5 and minGW). I am trying to get an idea of best practices with using pointers in Fortran 90 (which I must use). Here is an example code, which should work "out of the box" with one warning from a gfortran compiler: "POINTER valued function appears on RHS of assignment", and no warnings from the other compiler.

   module vectorField_mod
   implicit none

   type vecField1D
     private
     real(8),dimension(:),pointer :: x
     logical :: TFx = .false.
   end type

   contains

   subroutine setX(this,x)
     implicit none
     type(vecField1D),intent(inout) :: this
     real(8),dimension(:),target :: x
     logical,save :: first_entry = .true.
     if (first_entry) nullify(this%x); first_entry = .false.
     if (associated(this%x)) deallocate(this%x)
     allocate(this%x(size(x)))
     this%x = x
     this%TFx = .true.
   end subroutine

   function getX(this) result(res)
     implicit none
     real(8),dimension(:),pointer :: res
     type(vecField1D),intent(in) :: this
     logical,save :: first_entry = .true.
     if (first_entry) nullify(res); first_entry = .false.
     if (associated(res)) deallocate(res)
     allocate(res(size(this%x)))
     if (this%TFx) then
       res = this%x
     endif
   end function

   end module

   program test
   use vectorField_mod
   implicit none

   integer,parameter :: Nx = 15000
   integer :: i
   real(8),dimension(Nx) :: f
   type(vecField1D) :: f1

   do i=1,10**4
     f = i
     call setX(f1,f)
     f = getX(f1)
     call setX(f1,f)
     if (mod(i,5000).eq.1) then
       write(*,*) 'i = ',i,f(1)
     endif
   enddo
   end program

This program runs in both compilers. However, changing the loop from 10**4 to 10**5 causes a serious memory problem with gfortran.

Using CTR-ALT-DLT, and opening "performance", the physical memory increases rapidly when running in gfortran, and doesn't seem to move for the compaq compiler. I usually cancel before my computer crashes, so I'm not sure of the behavior after it reaches the maximum.

This doesn't seem to be the appropriate way to use pointers (which I need in derived data types). So my question is: how can I safely use pointers while maintaining the same sort of interface and functionality?

p.s. I know that the main program does not seem to do anything constructive, but the point is that I don't think that the loop should be limited by the memory, but rather it should be a function of run-time.

Any help is greatly appreciated.

1条回答
Fickle 薄情
2楼-- · 2019-02-21 03:41

This code has a few problems, perhaps caused by misunderstandings around the language. These problems have nothing to do with the specific compiler - the code itself is broken.

Conceptually, note that:

  • There is one and only one instance of a saved variable in a procedure per program in Fortran 90.
  • The variable representing the function result always starts off undefined each time a function is called.
  • If you want a pointer in a calling scope to point at the result of a function with a pointer result, then you must use pointer assignment.
  • If a pointer is allocated, you need to have a matching deallocate.

There is a latent logic error, in that the saved first_entry variables in the getX and setX procedures are conflated with object specific state in the setX procedure and procedure instance specific state in the getX procedure.

The first time setX is ever called the x pointer component of the particular this object will be nullified due to the if statement (there's an issue of poor style there too - be careful having multiple statements after an if statement - it is only the first one that is subject to the conditional!). If setX is then called again with a different this, first_entry will have been set to false and the this object will not be correctly set-up. I suspect you are supposed to be testing this%TFX instead.

Similarly, the first time getX is called the otherwise undefined function result variable res will be nullified. However, in all subsequent calls the function result will not be nullified (the function result starts off undefined each execution of the function) and will then be erroneously used in an associated test and also perhaps erroneously in a deallocate statement. (It is illegal to call associated (or deallocate for that matter) on a pointer with an undefined association status - noting that an undefined association status is not the same thing as dissociated.)

getX returns a pointer result - one that is created by the pointer being allocated. This pointer is then lost because "normal" assignment is used to access the value that results from evaluating the function. Because this pointer is lost there can't be (and so there isn't...) a matching deallocate statement to reverse the pointer allocation. The program therefore leaks memory. What almost certainly should be happening is that the thing that captures the value of the getX function in the main program (f in this case, but f is used for multiple things, so I'll call it f_ptr...) itself should be a pointer, and it should be pointer assigned - f_ptr => getX(f1). After the value of f_ptr has been used in the subsequent setX call and write statement, it can then be explicitly deallocated.

The potential for accidental use of normal assignment when pointer assignment is intended is one of the reasons that use of functions with pointer results is discouraged. If you need to return a pointer - then use a subroutine.

Fortran 95 simplifies management of pointer components by allowing default initialization of those components to NULL. (Note that you are using default initialization in your type definition - so your code isn't Fortran 90 anyway!)

Fortran 2003 (or Fortran 95 + the allocatable TR - which is a language level supported by most maintained compilers) introduces allocatable function results - which remove many of the potential errors that can otherwise be made using pointer functions.

Fortran 95 + allocatable TR support is so ubiquitous these days and the language improvements and fixes made to that point are so useful that (unless you are operating on some sort of obscure platform) limiting the language level to Fortran 90 is frankly ridiculous.

查看更多
登录 后发表回答