Discussion:
(Re)allocating an already allocated array
(too old to reply)
spectrum
2017-08-25 20:29:20 UTC
Permalink
Hello,

If we try to reallocate an already allocated ALLOCATABLE
array with allocate() again, it ends up with an error. For example,
the following code

program main
implicit none
integer, allocatable :: a(:), b(:)
b = [1,2,3]

allocate( a(0) )
allocate( a(3), source=b ) !! (*)

print *, "a = ", a
end

gives the error (with gfortran-7.1):

Fortran runtime error: Attempting to allocate already allocated variable 'a'

If we replace the line (*) by

a = b

then the program runs fine with the result (as expected).

a = 1 2 3

Given that automatic reallocation of an allocatable array in the left-hand side
is now standard, I am wondering why allocate() does not perform
automatic deallocation of the old memory on the heap
(if any). Is there any reason why the user is forced to
call deallocate() explicitly for 'a' before calling allocate() (in current Fortran)?
Wolfgang Kilian
2017-08-25 20:47:20 UTC
Permalink
Post by spectrum
Hello,
If we try to reallocate an already allocated ALLOCATABLE
array with allocate() again, it ends up with an error. For example,
the following code
program main
implicit none
integer, allocatable :: a(:), b(:)
b = [1,2,3]
allocate( a(0) )
allocate( a(3), source=b ) !! (*)
print *, "a = ", a
end
Fortran runtime error: Attempting to allocate already allocated variable 'a'
If we replace the line (*) by
a = b
then the program runs fine with the result (as expected).
a = 1 2 3
Given that automatic reallocation of an allocatable array in the left-hand side
is now standard, I am wondering why allocate() does not perform
automatic deallocation of the old memory on the heap
(if any). Is there any reason why the user is forced to
call deallocate() explicitly for 'a' before calling allocate() (in current Fortran)?
It's not a bug, it's a feature.

If I see that error, it likely points to an actual programming error,
such as initialization performed twice. It like it when the compiler
can tell me about logic errors in my code.

-- Wolfgang
spectrum
2017-08-25 21:43:59 UTC
Permalink
Post by Wolfgang Kilian
It's not a bug, it's a feature.
If I see that error, it likely points to an actual programming error,
such as initialization performed twice. It like it when the compiler
can tell me about logic errors in my code.
I see, it seems helpful to be able to detect possible,
unintentional "double allocation" (i.e., a possible bug on the user's side).

Then..., if we wish some re-allocation facility (with automatic deallocation),
it may be more reasonable to request a new statement like reallocate()
or resize() rather than requesting an update or change in the semantic
of existing allocate().

The reason why I got interested in the above code is actually not to request
a new syntax like reallocate(). But rather, I was interested how to detect
the error in this another thread automatically.

accessing an element in a non-allocated array
https://groups.google.com/forum/#!topic/comp.lang.fortran/adK7w1ALqYY

If I allocate the array 'a' once to a 0-sized array,
then at least gfortran-7 reports an out-of-bounds error (with -fcheck=all).
(Still, other compilers may or may not report the error.)

module testM
implicit none
contains
subroutine sub( x )
integer :: x(:)
print *, "size(x) =", size(x)
print *, "x(1) =", x(1)
end
end

program Prog1
use testM
implicit none
! integer, pointer :: x(:) ! just for comparison
integer, allocatable :: x(:)

allocate( x( 0 ) ) !! (*) to be included or excluded below

call sub( x )
end

Results with Line (*) excluded:

$ gfortran-7 -fcheck=all test.f90
size(x) = -95090367
x(1) = 0

$ gfortran-7 -fcheck=all -fsanitize=address -g test.f90
size(x) = 313829054
x(1) = 0

$ pgfortran -Mbounds test.f90
size(x) = 1
Segmentation fault: 11

$ pgfortran -Mbounds -Mchkptr test.f90
0: Null pointer for x (test.f90: 20)

Results with (*) included:

$ gfortran-7 -fcheck=all test.f90
$ gfortran-7 -fcheck=all -fsanitize=address -g test.f90
size(x) = 0
At line 8 of file test.f90
Fortran runtime error: Index '1' of dimension 1 of array 'x' above upper bound of 0

$ pgfortran -Mbounds test.f90
size(x) = 0
x(1) = 0

$ pgfortran -Mbounds -Mchkptr test.f90
size(x) = 0
x(1) = 1996292176

Because many other languages seem to initialize a dynamically sized array
to 0-sized (when the user does not specify its length upon allocation) and
reports such unsafe use at runtime, I thought it might be useful if
all allocatable arrays are automatically given a descriptor for 0-sized array
by default (as initialization). But... this interferes with the above semantic of
allocate() (that a sequential use of allocate() is not allowed...)
Ian Harvey
2017-08-26 00:05:21 UTC
Permalink
On 2017-08-26 07:13, spectrum wrote:
...
Post by spectrum
Because many other languages seem to initialize a dynamically sized array
to 0-sized (when the user does not specify its length upon allocation) and
reports such unsafe use at runtime, I thought it might be useful if
all allocatable arrays are automatically given a descriptor for 0-sized array
by default (as initialization). But... this interferes with the above semantic of
allocate() (that a sequential use of allocate() is not allowed...)
Other Fortran compilers, with appropriate runtime debugging options
enabled, can report an unallocated actual argument being associated with
a non-allocatable, non-optional dummy argument.

There's no need for a language change (which might be materially
incompatible with the current language and break current conforming
programs) here - all this is is just an oversight in the runtime checks
of one compiler.
FortranFan
2017-08-27 16:17:15 UTC
Permalink
..
There's no need for a language change ..
There has not been a *need* for any language change since FORTRAN I. It's all about the *wants* of the language practitioners!

The canonical sequence of coding instructions suggested by the current Fortran standard to allocate an object appears to be:

--- begin code snippet ---
..
if ( allocated(foo) ) then
deallocate(foo)
! or some other action, say error stop?
..
end if
allocate(foo)
..
--- end snippet ---

There are coders who do find this needlessly verbose and I think they will have a point if they *want* a change in the language that can yield code brevity while also making it easier to understand and maintain the code.

I think the language would do well to consider an additional optional argument, say NEW, in the ALLOCATE statement that if given the value .TRUE. informs the processor to attempt to deallocate the object if its status is ALLOCATED; if this argument is .FALSE. or otherwise omitted, regular service resumes.

--- begin pseudo code snippet ---
..
! same as current standard
allocate( foo )

..
! this informs the processor to deallocate foo if its status is allocated
allocate( foo, new=.true. )
..
--- end snippet ---

A regular coder will be able to understand the above optional dummy argument of NEW as being somewhat analogous to STATUS='REPLACE' in file OPEN statements.

I think the above can be introduced with no adverse impact on existing language semantics.
Jos Bergervoet
2017-08-27 22:07:37 UTC
Permalink
Post by FortranFan
..
There's no need for a language change ..
There has not been a *need* for any language change since FORTRAN I. It's all about the *wants* of the language practitioners!
--- begin code snippet ---
..
if ( allocated(foo) ) then
deallocate(foo)
! or some other action, say error stop?
..
end if
allocate(foo)
..
--- end snippet ---
But for the case of an array that will sacrifice performance!
What you need is:
if ( allocated(foo) ) then
if (.not. size(foo) == wanted_size ) then
deallocate( foo )
allocate( foo(wanted_size) )
! or some other action, say error stop?
endif
else
allocate( foo(wanted_size) )
end if
Post by FortranFan
There are coders who do find this needlessly verbose
More importantly, there are readers who will have a
hard time understanding the code if 8 out of every 9
lines are just this book-keeping clutter..
--
Jos
Ev. Drikos
2017-08-26 14:55:06 UTC
Permalink
Post by spectrum
Post by Wolfgang Kilian
It's not a bug, it's a feature.
If I see that error, it likely points to an actual programming error,
such as initialization performed twice. It like it when the compiler
can tell me about logic errors in my code.
In C/C++ and Java is always legal to call again "malloc or/and new" and
"new" respectively. In C/C++ the penalty is just a memory leak whereas
in Java the garbage collector might do the clean up. Memory leaks aren't
always bad (ie just before a program exit).
Post by spectrum
...
module testM
implicit none
contains
subroutine sub( x )
integer :: x(:)
print *, "size(x) =", size(x)
print *, "x(1) =", x(1)
end
end
program Prog1
use testM
implicit none
! integer, pointer :: x(:) ! just for comparison
integer, allocatable :: x(:)
allocate( x( 0 ) ) !! (*) to be included or excluded below
call sub( x )
end
...
$ pgfortran -Mbounds test.f90
size(x) = 1
Segmentation fault: 11
$ pgfortran -Mbounds -Mchkptr test.f90
0: Null pointer for x (test.f90: 20)
Assuming this last message is a runtime error, it seems to be the most
perceived result. The Segmentation fault which is also the C/C++
approach in such cases isn't always guaranteed, not sure about Fortran.
Post by spectrum
$ gfortran-7 -fcheck=all test.f90
...
size(x) = 0
At line 8 of file test.f90
Fortran runtime error: Index '1' of dimension 1 of array 'x' above upper bound of 0
IMHO again, it seems to be the most perceived result.
Thomas Koenig
2017-08-27 08:17:45 UTC
Permalink
Post by Ev. Drikos
In C/C++ and Java is always legal to call again "malloc or/and new" and
"new" respectively. In C/C++ the penalty is just a memory leak whereas
in Java the garbage collector might do the clean up. Memory leaks aren't
always bad (ie just before a program exit).
If you want to have this kind of semantics, use Fortran pointers.
There, you can also generate memory leaks, and allocating
a pointer that is pointing to something that has been
allocated previously is no error.

This is the main reason why I personally prefer allocatable
variables - the compiler does the bookkeeping for you.
Ev. Drikos
2017-08-27 16:22:35 UTC
Permalink
Post by Thomas Koenig
Post by Ev. Drikos
In C/C++ and Java is always legal to call again "malloc or/and new" and
"new" respectively. In C/C++ the penalty is just a memory leak whereas
in Java the garbage collector might do the clean up. Memory leaks aren't
always bad (ie just before a program exit).
If you want to have this kind of semantics, use Fortran pointers.
There, you can also generate memory leaks, and allocating
a pointer that is pointing to something that has been
allocated previously is no error.
This is the main reason why I personally prefer allocatable
variables - the compiler does the bookkeeping for you.
IMHO, not worrying about memory leaks would be great but I'm not aware
of a language/tool that makes the cleanup perfectly!

BTW, some times the program below produces weird results and I can't
figure out if this error is gnu specific or a programmatic one. I've
reproduced it with versions 4.8 & 7.1 in Mac & Linux.

Ev. Drikos



$ gcc -lgfortran pointer.f90 -o pointer; ./pointer
----------------------------------------------------
a = 1
----------------------------------------------------
----------------------------------------------------
a = 2 3
p_old = 2
----------------------------------------------------
$ cat pointer.9f0
program main
implicit none

integer, parameter :: wp=8
integer(kind=wp), target, allocatable :: a(:), b(:)
integer(kind=wp), pointer :: p_old(:)
b = [2,3]

allocate( a(1) ) ; a(1)=1
p_old=>a !
print *, "----------------------------------------------------"
print *, " a = ", a
print *, "----------------------------------------------------"

a = b
print *, "----------------------------------------------------"
print *, " a = ", a
print *, " p_old = ", p_old
print *, "----------------------------------------------------"

end
FortranFan
2017-08-27 16:43:20 UTC
Permalink
..
BTW, some times the program below produces weird results ..
That is only as instructed by the standard. Following 'a = b' assignment, the target of pointer p_old can be "undefined" depending to what processor does with "reallocation of assignment".

The canonical steps for such code would appear to nullify the pointer associaton and associate it again with the target following its reallocation:

--- begin code snippet --
..
allocate( a(1) ) ; a(1)=1
p_old => a

..
p_old => null()
a = b
p_old => a
..

--- end snippet --
Ron Shepard
2017-08-27 18:17:23 UTC
Permalink
Post by spectrum
program main
    implicit none
    integer, parameter :: wp=8
    integer(kind=wp),  target, allocatable :: a(:), b(:)
    integer(kind=wp),  pointer    :: p_old(:)
    b = [2,3]
    allocate( a(1) ) ; a(1)=1
    p_old=>a         !
    print *, "----------------------------------------------------"
    print *, "     a = ", a
    print *, "----------------------------------------------------"
    a = b
If this statement were replaced with the more explicit

deallocate(a)
allocate(a(size(b))
a(:) = b(:)

then it should be clear that p_old is a dangling pointer that points to
the old memory, which might be overwritten by the time the print
statement is executed. In any case p_old is undefined according to the
language.
Post by spectrum
    print *, "----------------------------------------------------"
    print *, "     a = ", a
    print *, " p_old = ", p_old
    print *, "----------------------------------------------------"
end
In the context of the other part of this thread regarding safety and
clarity of code, this is another example of how allocate on assignment
sometimes complicates the debugging process. Without allocation on
assignment, the "a=b" expression would have been a nonconforming array
assignment, and with appropriate compiler options, it could have been
detected at run time.

$.02 -Ron Shepard
FortranFan
2017-08-27 21:16:40 UTC
Permalink
.. this is another example of how allocate on assignment
sometimes complicates the debugging process...
@Ron Shepard,

None of your statements in this thread are making any sense whatsoever. Whatever points you are making about the standard revision(s) making it more wrror-prone or difficult to debug (e.g., with reallocate-on-assignment) come across as vague and most subjective, even baseless, and for which counter-assertions can be made if one were so motivated. Moreover, the standard revisions are what they are, implementations have for a while started supporting them following some persistent requests:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=35810
https://groups.google.com/forum/#!topic/comp.lang.fortran/fV0ef0pIBxw.

Do note though the code snippet you are now trying to put up as an example also does not follow your vague arguments. For there are coders who are likely to overlook the fact a pointer points to old memory following the re-definement of 'a', whether it occurs in a single instruction or spanning multiple lines that you show and that is at the root of the problem with that example.

The question for readers is what to do going forward, if anything.
Ron Shepard
2017-08-27 22:32:57 UTC
Permalink
[...] For there are coders who are likely to overlook the fact a pointer points to old memory following the re-definement of 'a', whether it occurs in a single instruction or spanning multiple lines that you show and that is at the root of the problem with that example.
One code sequence is

pa => a
a = b

and the other sequence is

pa => a
deallocate(a)
allocate(a, source=b)

If you think the source of the dangling pointer is equally clear with
those two sequences, then I guess we just have to agree to disagree.

$.02 -Ron Shepard
FortranFan
2017-08-28 02:46:52 UTC
Permalink
Post by Ron Shepard
..
If you think the source of the dangling pointer is equally clear with
those two sequences, then I guess we just have to agree to disagree.
..
@Ron Shepard,

What I see is the difficulty many coders have with pointers generally and the particular misunderstandings and misimpressions they carry with the dual aspects in Fortran with variables of POINTER and TARGET attributes and the nature and state of pointer-target association.

Moreover, all but the simplest of code will have remoteness in the pointer-target association; unlike the contrived sequences you show, the association will be a page or more away from the reallocation and/or the original allocation that can lead the coder to overlook the possibility of a dangling pointer.

Under the circumstances, the argument is not whether one thinks the source of the issue "is equally clear" but if this particular aspect makes much of a difference to coders who are going to struggle with pointers and will have much bookkeeping to do to manage memory. Note it does not.

The 'allocate on assignment' feature introduced starting with Fortran 2003 appears to be a much broader aspect covering objects with length-type parameters. The pointer example, to me, is a matter aside from the larger semantics at work with this feature.

If you don't see it this way, then yes it's a disagreement.
Jos Bergervoet
2017-08-28 09:00:36 UTC
Permalink
Post by FortranFan
Post by Ron Shepard
..
If you think the source of the dangling pointer is equally clear with
those two sequences, then I guess we just have to agree to disagree.
..
Under the circumstances, the argument is not whether one thinks the source of the issue "is equally clear" but if this particular aspect makes much of a difference to coders who are going to struggle with pointers and will have much bookkeeping to do to manage memory. Note it does not.
The 'allocate on assignment' feature introduced starting with Fortran 2003 appears to be a much broader aspect covering objects with length-type parameters. The pointer example, to me, is a matter aside from the larger semantics at work with this feature.
If you don't see it this way, then yes it's a disagreement.
Still, you fail to disagree on the same subject! The 'allocate
on assignment' feature applies to allocatables, not pointers.

So any disagreement about pointers is not relevant and cannot be
accepted as proof that you disagree on the topic of the thread.

(And apart from that, you don't even disagree on whether you
disagree! You both claim that you do. But I don't believe it.)
--
Jos
Ev. Drikos
2017-08-28 12:57:25 UTC
Permalink
Post by Ron Shepard
Post by spectrum
program main
implicit none
integer, parameter :: wp=8
integer(kind=wp), target, allocatable :: a(:), b(:)
integer(kind=wp), pointer :: p_old(:)
b = [2,3]
allocate( a(1) ) ; a(1)=1
p_old=>a !
print *, "----------------------------------------------------"
print *, " a = ", a
print *, "----------------------------------------------------"
a = b
If this statement were replaced with the more explicit
deallocate(a)
allocate(a(size(b))
a(:) = b(:)
then it should be clear that p_old is a dangling pointer that points to
the old memory, which might be overwritten by the time the print
statement is executed. In any case p_old is undefined according to the
language.
It's my impression that a dangling pointer is an implementation choice.

The allocation of assignment doesn't seem be at a disadvantage. Indeed,
each of the above two sequences may produce weird results (confirmed).

To my understanding, as soon as "a" is freed the pointer "pa" is found
to be in an inconsistent state which I think is implementation specific.

Conceptually, the Fortran processor on pointer assignment likely copies
some values which are administrative data (ie size), from the structure
that represents "a" to the structure that represents "qa". One of them
is obviously the memory address where the allocated area begins.

It's my impression that with some extra indirection when the target is
allocatable, the pointer would remain in a consistent state as long as
the target is alive, or possibly there are other implications?

Ev. Drikos
Ev. Drikos
2017-08-29 05:55:04 UTC
Permalink
Post by Ev. Drikos
...
...
...
Conceptually, the Fortran processor on pointer assignment likely copies
some values which are administrative data (ie size), from the structure
that represents "a" to the structure that represents "qa". One of them
is obviously the memory address where the allocated area begins.
The word "Fortran" is redundant here, just "processor"!

I debugged the program below (pointer.f90) to figure out what values are
kept once the pointer has been assigned (p_old=>a).

LLDB reported that the array "a" and the pointer "p_old" have the
following structures & values (perhaps the information is partial):

[1] The array "a":
a array1_integer(kind=8)
data void * 0x1004010f0 0x00000001004010f0
offset long -1
dtype long 521
dim descriptor_dimension []

[2] The pointer "p_old":
p_old array1_integer(kind=8)
data void * 0x1004010f0 0x00000001004010f0
offset long -1
dtype long 521
dim descriptor_dimension []

Once the array "a" was deallocated, I used the command line:

(lldb) frame var -L a
0x00007fff5fbff750: (array1_integer(kind=8)) a = {
0x00007fff5fbff750: data = 0x0000000000000000
0x00007fff5fbff758: offset = -1
0x00007fff5fbff760: dtype = 521
0x00007fff5fbff768: dim = {}
}
(lldb) frame var a.dim[0]
(descriptor_dimension) a.dim[0] = (stride = 1, lbound = 1, ubound = 2)

(lldb) frame var -L p_old
0x00007fff5fbff6f0: (array1_integer(kind=8)) p_old = {
0x00007fff5fbff6f0: data = 0x00000001004010a0
0x00007fff5fbff6f8: offset = -1
0x00007fff5fbff700: dtype = 521
0x00007fff5fbff708: dim = {}
}

Below there are also exact copies of the program & the output.

Ev.Drikos

--------------------------------------------------------------

$gfortran pointer.f90; ./a.out
----------------------------------------------------
a = 1 2
----------------------------------------------------
----------------------------------------------------
a = 3
p_old = 3 2
----------------------------------------------------

$cat pointer.f90
program main
implicit none
integer, parameter :: wp=8
integer(kind=wp), target, allocatable :: a(:), b(:)
integer(kind=wp), pointer :: p_old(:)

b = [3]

allocate( a(2) ) ; a(1)=1 ; a(2)=2
p_old=>a
print *, "----------------------------------------------------"
print *, " a = ", a
print *, "----------------------------------------------------"

!a = b
deallocate(a)
allocate(a(size(b)))
a(:) = b(:)

print *, "----------------------------------------------------"
print *, " a = ", a
print *, " p_old = ", p_old
print *, "----------------------------------------------------"

end
Ev. Drikos
2017-08-29 17:13:24 UTC
Permalink
Post by Ev. Drikos
...
It's my impression that a dangling pointer is an implementation choice.
An excerpt from an older standard:
The association status of a pointer becomes undefined when ...
(1) ...
(2) ...
(3) the target of the pointer is deallocated other than through the pointer,
(4) ...


Another excerpt from the same standard:
Allocatable variables
1 The allocation status of an allocatable variable is either allocated
or unallocated.

Once I read the above two, I tried to deallocate the target "a" in the
program "pointer.f90" through the pointer "p_old" and I also faced a
runtime error (Attempting to allocate already allocated variable 'a').

So, I tried to deallocate twice, one through the pointer and one through
the target and then I faced a SIGABRT signal (malloc: *** error for
object 0x7fd8b3403470: pointer being freed was not allocated) So, the
allocatable target "a" was found to be in an inconsistent state.

I don't see how the implemented solution complies with the standard; I
cannot also figure out if the standard specification is consistent here
or just I don't get it.


Ev. Drikos
Wolfgang Kilian
2017-08-29 22:21:45 UTC
Permalink
Post by Ev. Drikos
...
It's my impression that a dangling pointer is an implementation choice.
 The association status of a pointer becomes undefined when ...
(1) ...
(2) ...
(3) the target of the pointer is deallocated other than through the pointer,
(4) ...
Allocatable variables
1 The allocation status of an allocatable variable is either allocated
or unallocated.
Once I read the above two, I tried to deallocate the target "a" in the
program "pointer.f90" through the pointer "p_old" and I also faced a
runtime error (Attempting to allocate already allocated variable 'a').
So, I tried to deallocate twice, one through the pointer and one through
the target and then I faced a SIGABRT signal (malloc: *** error for
object 0x7fd8b3403470: pointer being freed was not allocated) So, the
allocatable target "a" was found to be in an inconsistent state.
I don't see how the implemented solution complies with the standard; I
cannot also figure out if the standard specification is consistent here
or just I don't get it.
Ev. Drikos
Nothing wrong with that. Both operations are illegal in this context.
I don't have the standard quotations ready but:

A variable with the allocatable attribute can be decallocated only via
its own name. If the variable happens to be also a target of a pointer,
this is fine but that pointer is not allowed to change the allocation
status. Just as a pointer is not allowed to change the allocation
status of a target without the allocatable attribute.

Allocating a pointer actually means allocating an anonymous object and
establishing a pointer to it. Such an object can be deallocated by any
pointer pointing to it. Of course, it can be deallocated only once.

In other words, a Fortran pointer can point to a named object (variable)
or to an anonymous object. It can change the value in either case, but
only in the latter case it can change the allocation status.

-- Wolfgang
FortranFan
2017-08-27 21:38:07 UTC
Permalink
Post by Ev. Drikos
..
IMHO, not worrying about memory leaks would be great but I'm not aware
of a language/tool that makes the cleanup perfectly! ..
It's not entirely up to a language or a tool, but humans too and the use of the RAII programming idiom helps considerably with leaks and locks and losses:
https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization

In the context of the original post of using an ALLOCATE statement on an already 'allocated' object and your point about 'tolerating' memory leaks and Thomas Koenig's suggestion of using Fortran pointers, the example you were attempting is probably:

--- begin code ---
program p

integer, pointer :: a(:)
integer, allocatable :: b(:)

b = [ 2, 3 ]

allocate( a(1) ); a(1) = 1
print * ,"a = ", a

allocate( a, source=b )
print * ,"a = ", a

end program p
--- end code ---

which you should find will lead to:

a = 1
a = 2 3

and memory leaks! For the instructions are analogous to "call again malloc or/and new in those other languages that OP mentioned as allowing things that Fortran doesn't!
Thomas Koenig
2017-08-28 10:32:28 UTC
Permalink
Post by Ev. Drikos
Post by Thomas Koenig
Post by Ev. Drikos
In C/C++ and Java is always legal to call again "malloc or/and new" and
"new" respectively. In C/C++ the penalty is just a memory leak whereas
in Java the garbage collector might do the clean up. Memory leaks aren't
always bad (ie just before a program exit).
If you want to have this kind of semantics, use Fortran pointers.
There, you can also generate memory leaks, and allocating
a pointer that is pointing to something that has been
allocated previously is no error.
This is the main reason why I personally prefer allocatable
variables - the compiler does the bookkeeping for you.
IMHO, not worrying about memory leaks would be great but I'm not aware
of a language/tool that makes the cleanup perfectly!
Allocatables in Fortran are supposed to. If they don't, then this
is a compiler bug.
l***@gmail.com
2017-08-26 01:13:30 UTC
Permalink
I despair at the modern Fortran community almost as much as I despair at Fortran (chuckle).

Simply use the deallocate() subroutine before re-allocating an array.

The program:

program Exp
implicit none

integer, allocatable :: a(:), b(:)


b = [1, 2, 3]

allocate(a(1))
a(1) = -1
print *, "a = ", a

deallocate(a)
allocate(a(3), source=b)

print *, "a = ", a
end

Results in :

a = -1
a = 1 2 3
Llelan D.
2017-08-26 01:23:51 UTC
Permalink
I despair at the modern Fortran community almost as much as I despair at Fortran (chuckle).

Simply use the deallocate() statement before re-allocating an array.

The program:

program Exp
implicit none

integer, allocatable :: a(:), b(:)


b = [1, 2, 3]

allocate(a(1))
a(1) = -1
print *, "a = ", a

deallocate(a)
allocate(a(3), source=b)

print *, "a = ", a
end

Results in:
a = -1
a = 1 2 3
Llelan D.
2017-08-26 01:36:41 UTC
Permalink
Post by spectrum
Given that automatic reallocation of an allocatable array in the left-hand side
is now standard, I am wondering why allocate() does not perform
automatic deallocation of the old memory on the heap
(if any). Is there any reason why the user is forced to
call deallocate() explicitly for 'a' before calling allocate() (in current Fortran)?
Fortran is designed to be much more explicit than that. The allocate and deallocate statements explicitly indicate what they do without nonobvious additional functions, like allocate also performing a combined deallocate and allocate on an already allocated variable. Accidentally using allocate a second time is a common coding error for inexperienced coders. If such a statement were to be included in the standard, it would probably be consigned to a reallocate statement to make it more explicit rather than added to allocate.

Fortran is designed to be easily usable and comprehensible by those without programming as their major area of study, not to be quick to type.
Jos Bergervoet
2017-08-26 11:18:07 UTC
Permalink
Post by Llelan D.
Post by spectrum
Given that automatic reallocation of an allocatable array in the left-hand side
is now standard, I am wondering why allocate() does not perform
automatic deallocation of the old memory on the heap
(if any). Is there any reason why the user is forced to
call deallocate() explicitly for 'a' before calling allocate() (in current Fortran)?
Fortran is designed to be much more explicit than that. The allocate and deallocate statements explicitly indicate what they do without nonobvious additional functions, like allocate also performing a combined deallocate and allocate on an already allocated variable. Accidentally using allocate a second time is a common coding error for inexperienced coders. If such a statement were to be included in the standard, it would probably be consigned to a reallocate statement to make it more explicit rather than added to allocate.
Fortran is designed to be easily usable and comprehensible by those without programming as their major area of study,
The revisions, however, have to some extent broken that
initial design objective, some might argue..
Post by Llelan D.
not to be quick to type.
An important objective should be "easy to read and
understand". One can assume that the code is more
often read than typed in. (Even if it is typed in
multiple times with small changes, each time you will
read it at least a few times before retyping the line
with an update.

Nevertheless, none of the objectives mentioned here
stands in the way of automatic deallocate before
allocate. The fact that this would allow unnecessary
repeated allocation is similar to re-initializing x:
x = 3
print *, "Hello, where am I, is it still 1954?"
x = 5
print *, 3*x

and I don't see a big advantage in disallowing that
and instead require
x = 3
unassign(x)
print *, "OK, I see, it is 2017.."
x = 5
print *, 3*x

(But of course the choice for allocate() should now
remain fixed, for compatibility reasons.)
--
Jos
Wolfgang Kilian
2017-08-28 12:36:35 UTC
Permalink
Post by Jos Bergervoet
Nevertheless, none of the objectives mentioned here
stands in the way of automatic deallocate before
allocate. The fact that this would allow unnecessary
  x = 3
  print *, "Hello, where am I, is it still 1954?"
  x = 5
  print *, 3*x
and I don't see a big advantage in disallowing that
and instead require
  x = 3
  unassign(x)
  print *, "OK, I see, it is 2017.."
  x = 5
  print *, 3*x
Disallowing reassignment is actually the default or mandatory behavior
in many programming languages. If available, it makes a lot of sense to
make maximal use of one-time assignment / immutable values.

Fortran also has such a property, hidden in the intent(in) attribute
(argument association plays the role of the first assignment), and also
in the 'associate' statement. In both cases, the 'variable' becomes
read-only after given a value. There is no 'unassign' functionality in
either case.

-- Wolfgang
Ian Harvey
2017-08-29 08:23:55 UTC
Permalink
On 28/08/2017 10:06 PM, Wolfgang Kilian wrote:
...
Post by Wolfgang Kilian
Fortran also has such a property, hidden in the intent(in) attribute
(argument association plays the role of the first assignment), and also
in the 'associate' statement.  In both cases, the 'variable' becomes
read-only after given a value.
what_do_you_mean: ASSOCIATE(some_name => variable)
some_name = can_be_defined
END ASSOCIATE what_do_you_mean
Wolfgang Kilian
2017-08-29 13:29:38 UTC
Permalink
Post by Ian Harvey
...
Post by Wolfgang Kilian
Fortran also has such a property, hidden in the intent(in) attribute
(argument association plays the role of the first assignment), and
also in the 'associate' statement.  In both cases, the 'variable'
becomes read-only after given a value.
what_do_you_mean: ASSOCIATE(some_name => variable)
  some_name = can_be_defined
END ASSOCIATE what_do_you_mean
Oh, yes,

i_mean: ASSOCIATE(some_name => (variable))
some_name = cannot_be_defined ! this assignment is 'illegal'
END ASSOCIATE i_mean

Agreed?

-- Wolfgang
r***@gmail.com
2017-08-26 08:09:33 UTC
Permalink
Post by spectrum
Hello,
If we try to reallocate an already allocated ALLOCATABLE
array with allocate() again, it ends up with an error. For example,
the following code
program main
implicit none
integer, allocatable :: a(:), b(:)
b = [1,2,3]
allocate( a(0) )
allocate( a(3), source=b ) !! (*)
print *, "a = ", a
end
Fortran runtime error: Attempting to allocate already allocated variable 'a'
If we replace the line (*) by
a = b
then the program runs fine with the result (as expected).
a = 1 2 3
Given that automatic reallocation of an allocatable array in the left-hand side
is now standard, I am wondering why allocate() does not perform
automatic deallocation of the old memory on the heap
(if any).
It is provided to guard against a programming error.
Post by spectrum
Is there any reason why the user is forced to
call deallocate() explicitly for 'a' before calling allocate() (in current Fortran)?
It is to guard against a programmign error.
Ron Shepard
2017-08-26 15:55:54 UTC
Permalink
Post by r***@gmail.com
Post by spectrum
Given that automatic reallocation of an allocatable array in the left-hand side
is now standard, I am wondering why allocate() does not perform
automatic deallocation of the old memory on the heap
(if any).
It is provided to guard against a programming error.
Post by spectrum
Is there any reason why the user is forced to
call deallocate() explicitly for 'a' before calling allocate() (in current Fortran)?
It is to guard against a programmign error.
No one has mentioned this yet, but many fortran programmers objected to
the reallocate-on-assignment feature when it was added to the language
(in f2003, I think) for this same reason. It makes it difficult or
impossible for the compiler to help locate size mismatch program errors.
Before this feature was added, the compiler could test and warn at
runtime when a size mismatch occurred, but adding this feature negated
that desirable attribute. There was also a separate argument that each
assignment then required possible additional overhead to test for the
possibility of a mismatch and reallocation. The counter argument was
that the slice notation ((:), or (:,:), etc.) could be used on the lhs
of the assignment, and the compiler could then again legitimately test
for size mismatches upon request.

On the other hand, it is easy to imagine situations in which an
assignment is in a critical place in the code for efficiency, say in an
inner-loop, and the programmer would want to bypass all size mismatch
tests and reallocation tests and do just the memory copy as efficiently
as possible, consistent with code semantics before this feature was
added to the language. So this feature complicated both of the extreme
cases, debugging code for correctness and also writing efficient code
once correctness has been verified by the programmer.

Fortran is a compiled language, where the compiler is almost always
invoked on a saved source code file, so the number of characters
necessary to access some feature is not important. That characteristic
is important in interactive languages, especially those used in the
context where the program is not saved to a file but is typed directly
from the keyboard. For this reason, having a few allocate() and
deallocate() statements scattered around is not seen as a disadvantage
compared to the advantages of improved debugging and computational
efficiency.

$.02 -Ron Shepard
Jos Bergervoet
2017-08-26 17:53:15 UTC
Permalink
Post by Ron Shepard
Post by r***@gmail.com
Post by spectrum
Given that automatic reallocation of an allocatable array in the left-hand side
is now standard, I am wondering why allocate() does not perform
automatic deallocation of the old memory on the heap
(if any).
It is provided to guard against a programming error.
Post by spectrum
Is there any reason why the user is forced to
call deallocate() explicitly for 'a' before calling allocate() (in current Fortran)?
It is to guard against a programmign error.
No one has mentioned this yet, but many fortran programmers objected to
the reallocate-on-assignment feature when it was added to the language
Fortunately the wise committee members neglected this.
It is the same nonsense argument as for assigning an
integer to a real (or complex). Indeed everything that
gives you more possibilities to do things will also
make certain errors possible.
Post by Ron Shepard
(in f2003, I think) for this same reason. It makes it difficult or
impossible for the compiler to help locate size mismatch program errors.
Yes, so to avoid all errors we need special types, like:
integers that can only be 3
integers that can only be 7
reals that can only be 12.5
etc.
In that way you are certain that you never give those
variable the wrong value!
Post by Ron Shepard
.., the compiler could test and warn at
runtime when a size mismatch occurred,
Yes! And in my proposal the compiler can test and warn at
runtime when a wrong value is assigned!
Post by Ron Shepard
..., having a few allocate() and
deallocate() statements scattered around is not seen as a disadvantage
compared to the advantages of improved debugging and computational
efficiency.
Exactly my point! Having to use a dedicated kind of integer
(or real) for every value we use, is not a drawback, it gives
extra safety!
--
Jos
Gary Scott
2017-08-26 19:35:12 UTC
Permalink
Post by Jos Bergervoet
Post by Ron Shepard
Post by r***@gmail.com
Post by spectrum
Given that automatic reallocation of an allocatable array in the left-hand side
is now standard, I am wondering why allocate() does not perform
automatic deallocation of the old memory on the heap
(if any).
It is provided to guard against a programming error.
Post by spectrum
Is there any reason why the user is forced to
call deallocate() explicitly for 'a' before calling allocate() (in current Fortran)?
It is to guard against a programmign error.
No one has mentioned this yet, but many fortran programmers objected to
the reallocate-on-assignment feature when it was added to the language
Fortunately the wise committee members neglected this.
It is the same nonsense argument as for assigning an
integer to a real (or complex). Indeed everything that
gives you more possibilities to do things will also
make certain errors possible.
Post by Ron Shepard
(in f2003, I think) for this same reason. It makes it difficult or
impossible for the compiler to help locate size mismatch program errors.
  integers that can only be 3
  integers that can only be 7
  reals that can only be 12.5
  etc.
In that way you are certain that you never give those
variable the wrong value!
Post by Ron Shepard
    .., the compiler could test and warn at
runtime when a size mismatch occurred,
Yes! And in my proposal the compiler can test and warn at
runtime when a wrong value is assigned!
Post by Ron Shepard
      ..., having a few allocate() and
deallocate() statements scattered around is not seen as a disadvantage
compared to the advantages of improved debugging and computational
efficiency.
Exactly my point! Having to use a dedicated kind of integer
(or real) for every value we use, is not a drawback, it gives
extra safety!
These capabilities were desired as part of my bitstring extensions. You
build up types consisting of definitions of bit ranges (including
packing within an base integer type for example), MSB/LSB values,
maximum/minimum value ranges (and separate from the scaling of course),
ones complement/twos complement, decimal, BCD, etc. You can define all
of these things in a composite object definition. I would never want
the base types to be mucked up, this would be too inefficient for many
applications, but this concept would not be that difficult to implement.
I've built much of the lower level bit mapping procedures over the
years, it just isnt that difficult and it would be extremely useful for
many system programming paradigms, especially translation from one
processor type to another or translation of complex bus protocols
(careful design would not cause performance issues for many things).
Jos Bergervoet
2017-08-26 20:10:38 UTC
Permalink
Post by Gary Scott
Post by Jos Bergervoet
Post by Ron Shepard
Post by r***@gmail.com
Post by spectrum
Given that automatic reallocation of an allocatable array in the left-hand side
is now standard, I am wondering why allocate() does not perform
automatic deallocation of the old memory on the heap
(if any).
It is provided to guard against a programming error.
Post by spectrum
Is there any reason why the user is forced to
call deallocate() explicitly for 'a' before calling allocate() (in
current Fortran)?
It is to guard against a programmign error.
No one has mentioned this yet, but many fortran programmers objected to
the reallocate-on-assignment feature when it was added to the language
Fortunately the wise committee members neglected this.
It is the same nonsense argument as for assigning an
integer to a real (or complex). Indeed everything that
gives you more possibilities to do things will also
make certain errors possible.
Post by Ron Shepard
(in f2003, I think) for this same reason. It makes it difficult or
impossible for the compiler to help locate size mismatch program errors.
integers that can only be 3
integers that can only be 7
reals that can only be 12.5
etc.
In that way you are certain that you never give those
variable the wrong value!
Post by Ron Shepard
.., the compiler could test and warn at
runtime when a size mismatch occurred,
Yes! And in my proposal the compiler can test and warn at
runtime when a wrong value is assigned!
Post by Ron Shepard
..., having a few allocate() and
deallocate() statements scattered around is not seen as a disadvantage
compared to the advantages of improved debugging and computational
efficiency.
Exactly my point! Having to use a dedicated kind of integer
(or real) for every value we use, is not a drawback, it gives
extra safety!
These capabilities were desired as part of my bitstring extensions. You
build up types consisting of definitions of bit ranges (including
packing within an base integer type for example), MSB/LSB values,
maximum/minimum value ranges (and separate from the scaling of course),
ones complement/twos complement, decimal, BCD, etc. You can define all
of these things in a composite object definition. I would never want
the base types to be mucked up, this would be too inefficient for many
applications, but this concept would not be that difficult to implement.
I've built much of the lower level bit mapping procedures over the
years, it just isnt that difficult and it would be extremely useful for
many system programming paradigms, especially translation from one
processor type to another or translation of complex bus protocols
(careful design would not cause performance issues for many things).
But then you may and up with an awful lot of different lengths
and value ranges. And if you also want mixed-mode arithmetic
you get an astronomical amount of different routines for "+",
"*" and the like.. (You probably don't want mixed-mode in your
example, but this problem does often occur in extending the
numerical types to vectors, tensors, and other algebras. Some
better generic programming possibilities in Fortran would surely
be welcome!)
--
Jos
Ron Shepard
2017-08-26 23:53:29 UTC
Permalink
Post by Jos Bergervoet
Post by Ron Shepard
Post by r***@gmail.com
Post by spectrum
Given that automatic reallocation of an allocatable array in the left-hand side
is now standard, I am wondering why allocate() does not perform
automatic deallocation of the old memory on the heap
(if any).
It is provided to guard against a programming error.
Post by spectrum
Is there any reason why the user is forced to
call deallocate() explicitly for 'a' before calling allocate() (in current Fortran)?
It is to guard against a programmign error.
No one has mentioned this yet, but many fortran programmers objected to
the reallocate-on-assignment feature when it was added to the language
Fortunately the wise committee members neglected this.
It is the same nonsense argument as for assigning an
integer to a real (or complex).
I am not aware of this argument arising in any recent version of fortran.
Post by Jos Bergervoet
Indeed everything that
gives you more possibilities to do things will also
make certain errors possible.
Allocate on assignment did not allow any new possibilities to do things,
everything that it does can also be done with explicit allocate() and
deallocate() statements. But that feature did, and still does, have the
disadvantages that I mentioned.
Post by Jos Bergervoet
Post by Ron Shepard
(in f2003, I think) for this same reason. It makes it difficult or
impossible for the compiler to help locate size mismatch program errors.
  integers that can only be 3
  integers that can only be 7
  reals that can only be 12.5
  etc.
[...]

$.02 -Ron Shepard
Jos Bergervoet
2017-08-27 04:18:21 UTC
Permalink
Post by Ron Shepard
Post by Jos Bergervoet
Post by Ron Shepard
Post by r***@gmail.com
Post by spectrum
Given that automatic reallocation of an allocatable array in the left-hand side
is now standard, I am wondering why allocate() does not perform
automatic deallocation of the old memory on the heap
(if any).
It is provided to guard against a programming error.
Post by spectrum
Is there any reason why the user is forced to
call deallocate() explicitly for 'a' before calling allocate() (in
current Fortran)?
It is to guard against a programmign error.
No one has mentioned this yet, but many fortran programmers objected to
the reallocate-on-assignment feature when it was added to the language
Fortunately the wise committee members neglected this.
It is the same nonsense argument as for assigning an
integer to a real (or complex).
I am not aware of this argument arising in any recent version of fortran.
Fortunately also there, people finally got wiser! It
is an *old* argument, saying that you *should* write:
x = 42.0d0
z = (1.0d0,0.0d0)
when you mean
x = 42
z = 1
Post by Ron Shepard
Post by Jos Bergervoet
Indeed everything that
gives you more possibilities to do things will also
make certain errors possible.
Allocate on assignment did not allow any new possibilities to do things,
everything that it does can also be done with explicit allocate() and
deallocate() statements.
Yes it does. It allows you to remove a lot of clutter
from your code: allocate() and deallocate() statements.

In addition it avoids certain human errors in those
statements, like allocating not exactly the right size
that subsequently is needed for the assignement. Or
deallocating when the object isn't allocated
Post by Ron Shepard
But that feature did, and still does, have the
disadvantages that I mentioned.
You mentioned "It makes it difficult or impossible for
the compiler ..". Well it should! If it removes the
burden of doing difficult things to the compiler then
we have progress!

Perhaps you really believe that this code:
if(allocated(x) then
deallocate(x)
endif
allocate(x(4*i+3*j-4,2*ny,4-k)
x = v+u
is safer (for maintenance or initial coding) than this:
x = v+u

So then, (out of curiosity) would you also advise:
z = (1.0d0,0.0d0)
instead of
z = 1
for a complex variable z?
--
Jos
r***@gmail.com
2017-08-27 07:42:00 UTC
Permalink
Post by Jos Bergervoet
Post by Ron Shepard
Post by Jos Bergervoet
Post by Ron Shepard
No one has mentioned this yet, but many fortran programmers objected to
the reallocate-on-assignment feature when it was added to the language
Fortunately the wise committee members neglected this.
It is the same nonsense argument as for assigning an
integer to a real (or complex).
I am not aware of this argument arising in any recent version of fortran.
Fortunately also there, people finally got wiser! It
x = 42.0d0
z = (1.0d0,0.0d0)
when you mean
x = 42
z = 1
Post by Ron Shepard
Post by Jos Bergervoet
Indeed everything that
gives you more possibilities to do things will also
make certain errors possible.
Allocate on assignment did not allow any new possibilities to do things,
everything that it does can also be done with explicit allocate() and
deallocate() statements.
Yes it does. It allows you to remove a lot of clutter
from your code: allocate() and deallocate() statements.
In addition it avoids certain human errors in those
statements, like allocating not exactly the right size
that subsequently is needed for the assignement. Or
deallocating when the object isn't allocated
Post by Ron Shepard
But that feature did, and still does, have the
disadvantages that I mentioned.
You mentioned "It makes it difficult or impossible for
the compiler ..". Well it should! If it removes the
burden of doing difficult things to the compiler then
we have progress!
if(allocated(x) then
deallocate(x)
endif
Why not, simply, :

if (allocated(x)) deallocate(x)
Post by Jos Bergervoet
allocate(x(4*i+3*j-4,2*ny,4-k)
x = v+u
x = v+u
A test for being allocated (or not) is helpful (probably important)
when the programmer has made an error.
If he expects a variable to be allocated, and it is not,
or if he expects it to be not allocated, and it is.
Jos Bergervoet
2017-08-27 08:31:50 UTC
Permalink
...
Post by r***@gmail.com
Post by Jos Bergervoet
allocate(x(4*i+3*j-4,2*ny,4-k)
x = v+u
or simply
Post by r***@gmail.com
Post by Jos Bergervoet
x = v+u
A test for being allocated (or not) is helpful (probably important)
when the programmer has made an error.
And it is unacceptable if the programmer would have
to go many times again through very complicated
arithmetic to fill in the right bounds, which the
compiler can do without errors for free.
Post by r***@gmail.com
If he expects a variable to be allocated, and it is not,
But if both cases (allocated or not yet allocated)
are supposed to occur, your test would be completely
useless and the complicated arithmetic in the allocate
statements would give a much higher chance of errors.
Post by r***@gmail.com
or if he expects it to be not allocated, and it is.
There are hundreds of tests (or more) that might be
done before every statement to check all kind of
things. Why would you make them mandatory? Tbis
particular test is not in general very useful.
Having been allocated before usually just means
"having been used before", which is no problem.

It would be useful only if the array is supposed to
be allocated only once. But then make it a fixed
array or an automatic array!

Advocating the test as useful in general is coparable
to requiring a variable x not to be assigned multiple
times. (If you want that, make it a parameter!)

Perhaps an analogue to "parameter" for your special
case could be added as a "one time allocatable".
Something like:
real, once_allocatable :: x(:)
or
real, allocatable(times=1) :: x(:)

where the latter also gives you twice allocatable
arrays, etc. After all, there might be people who
think it is extremely important to have it checked
that an array is not allocated more than e.g. 7
times. (That could be the intention of the program,
couldn't it? So we want it to be checked! You
don't want people to die, do you?!)
--
Jos
r***@gmail.com
2017-08-27 13:30:21 UTC
Permalink
Post by Jos Bergervoet
...
Post by r***@gmail.com
Post by Jos Bergervoet
allocate(x(4*i+3*j-4,2*ny,4-k)
x = v+u
or simply
Post by r***@gmail.com
Post by Jos Bergervoet
x = v+u
A test for being allocated (or not) is helpful (probably important)
when the programmer has made an error.
And it is unacceptable if the programmer would have
to go many times again through very complicated
arithmetic to fill in the right bounds, which the
compiler can do without errors for free.
Rarely happens that the bound of an array is some
complicated expression (such as you suggested above)

Usually the bound is a simple variable name.
Post by Jos Bergervoet
Post by r***@gmail.com
If he expects a variable to be allocated, and it is not,
But if both cases (allocated or not yet allocated)
are supposed to occur, your test would be completely
useless
The test is never useless.
Post by Jos Bergervoet
and the complicated arithmetic in the allocate
statements would give a much higher chance of errors.
Rubbish.
Post by Jos Bergervoet
Post by r***@gmail.com
or if he expects it to be not allocated, and it is.
There are hundreds of tests (or more) that might be
done before every statement to check all kind of
things.
Nonsense.
Post by Jos Bergervoet
Why would you make them mandatory? Tbis
particular test is not in general very useful.
Having been allocated before usually just means
"having been used before", which is no problem.
It would be useful only if the array is supposed to
be allocated only once. But then make it a fixed
array or an automatic array!
Advocating the test as useful in general is coparable
to requiring a variable x not to be assigned multiple
times. (If you want that, make it a parameter!)
Perhaps an analogue to "parameter" for your special
case could be added as a "one time allocatable".
real, once_allocatable :: x(:)
or
real, allocatable(times=1) :: x(:)
where the latter also gives you twice allocatable
arrays, etc. After all, there might be people who
think it is extremely important to have it checked
that an array is not allocated more than e.g. 7
times. (That could be the intention of the program,
couldn't it? So we want it to be checked! You
don't want people to die, do you?!)
I see no need for that.
Jos Bergervoet
2017-08-27 21:56:54 UTC
Permalink
Post by r***@gmail.com
Post by Jos Bergervoet
...
There are hundreds of tests (or more) that might be
done before every statement to check all kind of
things.
Nonsense.
So how many tests are there then, according to you?
Post by r***@gmail.com
Post by Jos Bergervoet
Why would you make them mandatory? This
particular test is not in general very useful.
Having been allocated before usually just means
"having been used before", which is no problem.
It would be useful only if the array is supposed to
be allocated only once. But then make it a fixed
array or an automatic array!
Advocating the test as useful in general is comparable
to requiring a variable x not to be assigned multiple
times. (If you want that, make it a parameter!)
Perhaps an analogue to "parameter" for your special
case could be added as a "one time allocatable".
real, once_allocatable :: x(:)
or
real, allocatable(times=1) :: x(:)
where the latter also gives you twice allocatable
arrays, etc. After all, there might be people who
think it is extremely important to have it checked
that an array is not allocated more than e.g. 7
times. (That could be the intention of the program,
couldn't it? So we want it to be checked! You
don't want people to die, do you?!)
I see no need for that.
You mean that you can give them eternal life?! (They
then have to believe in you, I presume?)
--
Jos
Ron Shepard
2017-08-27 17:59:52 UTC
Permalink
Post by Jos Bergervoet
Advocating the test as useful in general is coparable
to requiring a variable x not to be assigned multiple
times. (If you want that, make it a parameter!)
I'm not following your argument here.

A parameter is a constant, it is not a variable and cannot be assigned
even a single time. Perhaps you are thinking of the PROTECTED attribute?
That allows a module variable to be defined within the module, either a
single time or multiple times, but it cannot be modified outside of the
module. This is useful in several circumstances, including avoiding
making unintended changes to global variables by mistaking them for
local variables. It is also useful for certain optimizations where the
compiler can determine that such a variable cannot be modified within a
block of code.

I still do not know what this has to do with reallocation on assignment
or reallocation within ALLOCATE().

$.02 -Ron Shepard
Ron Shepard
2017-08-27 17:44:04 UTC
Permalink
    if(allocated(x) then
      deallocate(x)
    endif
    allocate(x(4*i+3*j-4,2*ny,4-k)
    x = v+u
    x = v+u
Yes, I can imagine situations in which the former (without reallocation
upon assignment) is "safer" than the latter (with reallocation upon
assignment). For example, imagine a situation in which v(:,:,:) and
u(:,:,:) do not have the correct bounds; in that case, the compiler can
catch the size mismatch at runtime in the first case, whereas in the
latter case it cannot. This kind of error can be difficult to locate and
debug, since its effects may not be apparent until much later. I can
also imagine situations in which the former is more efficient than the
latter. An example might be if the assignment is inside of a loop in
which the sizes of x, v, and u are known to be conformable to the
programmer but not known to the compiler. In this case, the allocation
can be done once by the programmer in the first case, but in the latter
case it must be rechecked with every assignment by the compiler for
possible reallocation.

As I mentioned before, these situations can be addressed to some extent
by using stride notation for x(:,:,:) in the assignment. But as this
feature is added to new compiler versions, that means that old code that
did not use stride notation in these cases sometimes needed to be rewritten.

As far as z=(1.0d0,0.0d0), I have not recommended using "d" exponents in
fortran code for about 20 years now. The KIND notation, introduced in
f90, is almost always more portable, more flexible, and easier to
maintain. As for mixed-mode arithmetic, sometimes that feature
simplifies expressions and other times the quirky conversion rules
result in surprises and outright errors.

$.02 -Ron Shepard
e***@gmail.com
2017-08-30 10:11:56 UTC
Permalink
Dear everybody,
I have never been convinced that the reallocation on assignment has a big effect on performance, as basically it has to perform just one integer check on assigment (if it has to realloc the array, I think that the reallocation would take most of the time).

That said I made a small test, to be taken for whatever it is:

program testalloc
implicit none

real :: tstart, tend
integer, parameter :: NP = 100, NA = 1000000000

real, allocatable :: i_alloc(:)
real :: i_fixed(NP)

integer :: i

do i=1,NP
i_fixed(i) = i
enddo
allocate(i_alloc(NP))
i_alloc = i_fixed

call cpu_time(tstart)
do i=1, NA
i_fixed = i_fixed + i
enddo
call cpu_time(tend)

print *,tend-tstart, sum(i_fixed)

call cpu_time(tstart)
do i=1, NA
i_alloc = i_alloc + i
enddo
call cpu_time(tend)

print *,tend-tstart, sum(i_alloc)

end program

And I compiled with gfortran with and without the -std=f95.
The second loop is longer that the first of about 10% (on my laptop).
But I cannot see any difference on the cpu time when compiled with or whithout the -std=f95.

I should say that I compiled it with -O3 so I don't know which optimizations have been done by the compiler. By the way even without the -O3 there is no difference.

-- Edmondo
h***@gmail.com
2017-08-30 12:12:14 UTC
Permalink
Post by e***@gmail.com
Dear everybody,
I have never been convinced that the reallocation on assignment
has a big effect on performance, as basically it has to perform
just one integer check on assigment (if it has to realloc the
array, I think that the reallocation would take most of the time).
(snip)
Post by e***@gmail.com
do i=1, NA
i_fixed = i_fixed + i
enddo
(snip)
Post by e***@gmail.com
do i=1, NA
i_alloc = i_alloc + i
enddo
(snip)
Post by e***@gmail.com
And I compiled with gfortran with and without the -std=f95.
The second loop is longer that the first of about 10%
(on my laptop).
It is very hard to do benchmarks like this, but I suspect
that 10% is about right. It takes a little more work to access
an ALLOCATABLE array than a statically allocated array.

But processors now are good at overlapping operations that
it isn't easy to see the effect.
Post by e***@gmail.com
But I cannot see any difference on the cpu time when
compiled with or whithout the -std=f95.
You can also:

i_alloc(:) = i_alloc + i

which doesn't allow for reallocation, and so might avoid
the test.

In the actual case, there should be one test per execution
of the statement, but NP (that is, 100) assignments.

Note that a really good optimizing compiler would figure out
that the results are known at compile time, and evaluate
them at compile time.
e***@gmail.com
2017-08-30 13:53:19 UTC
Permalink
Post by h***@gmail.com
Post by e***@gmail.com
Dear everybody,
I have never been convinced that the reallocation on assignment
has a big effect on performance, as basically it has to perform
just one integer check on assigment (if it has to realloc the
array, I think that the reallocation would take most of the time).
(snip)
Post by e***@gmail.com
do i=1, NA
i_fixed = i_fixed + i
enddo
(snip)
Post by e***@gmail.com
do i=1, NA
i_alloc = i_alloc + i
enddo
(snip)
Post by e***@gmail.com
And I compiled with gfortran with and without the -std=f95.
The second loop is longer that the first of about 10%
(on my laptop).
It is very hard to do benchmarks like this, but I suspect
that 10% is about right. It takes a little more work to access
an ALLOCATABLE array than a statically allocated array.
But processors now are good at overlapping operations that
it isn't easy to see the effect.
Post by e***@gmail.com
But I cannot see any difference on the cpu time when
compiled with or whithout the -std=f95.
i_alloc(:) = i_alloc + i
which doesn't allow for reallocation, and so might avoid
the test.
In the actual case, there should be one test per execution
of the statement, but NP (that is, 100) assignments.
Note that a really good optimizing compiler would figure out
that the results are known at compile time, and evaluate
them at compile time.
Yes the compiler was figuring out a lot of thing.
I changed the program this way (just the relevant instructions):

...
real :: vtest_0(NP), vtest_1(NP+1), vtest_2(NP+2)
integer :: im, base
...

read(*,*) base
call cpu_time(tstart)
do i=1, NA
select case (im)
case(1)
i_alloc(:) = i + vtest_0
case(2)
i_alloc = i + vtest_1
case(0)
i_alloc = i + vtest_2
end select
im = modulo(im + 3, base)
enddo
call cpu_time(tend)

With and without the (:) after i_alloc.
I hoped that the compiler has no way to figure out which assignment to perform at every loop as it depends on a variable "base" that is read from the keyboard.

For NP = 100 (and NA=1000000000) the cpu time with "(:)" is longer (25 s) than that without "(:)" (17 s). While basically the same for NP = 10 (8.5 s).

But I agree with you it is just a toy test not much meaningful.
Cheers,
Edmondo

Continue reading on narkive:
Loading...