SOURCEd allocation for dynamic array handling

I’m trying to find the most compact/clear way to reallocate an array to a different size, keeping both boilerplate and allocations to a minimum and am looking at SOURCEd allocation.

In the example, with gfortran:

  • if the new array is longer, all data is copied from a, and the trailing elements are not initialized (nice)
  • if the new array is shorter, sourcing it to a longer array leads to a segfault.
program compact_resize
   implicit none

   integer :: i,j
   integer, allocatable :: a(:),longer(:),shorter(:)

   a = [(i,i=1,10)]
   print *, 'a=',a

   ! works
   allocate(longer(20), source = a)
   print *, 'b=',longer

   ! segfault
   allocate(shorter(5), source = a)
   print *, 'c=',shorter

end program compact_resize

Is what I’m doing violating any standards? the Intel documentation says " If object is an array, s-spec must appear and the number of s-specs must equal the rank of object, or source-expr must appear and have the same rank as object and the shape of object is that of source-expr". In other words, it seems like the option of BOTH specifying the size of the array and an array source-expr is not considered.

2 Likes

On Metcalf/Reid/Cohen it states that “if allocation is for an array, source-expr may be an array of the same rank, otherwise source-expr must be scalar”. also “because the bounds and shape of the allocated item are not taken from the source, making a clone of an array has to be done [by specifying its bounds]”, like

allocate(a(lbound(b,1):ubound(b,1)), source = b)

So apparently there’s a constraint on the rank, but not on their size, gfortran is doing it right, but of course it’s overflowing the copy when the new array is shorter than the former…

If you know they are shorter, you could apply bounds to the source:

allocate(shorter(5), source = a[:5])

Or more general:

allocate(shorter(new_size), source = a[:min(new_size, size(a))])
1 Like

This program demonstrates an array constructor with an implied do loop and also reshape with pad to grow an array:

program main
implicit none
real, allocatable :: x(:), y(:), z(:)
integer :: i
x = [10.0, 20.0]
y = [x, (0.0,i=1,3)]
z = reshape(x, shape=[5], pad=[0.0])
print "(*(f4.1,1x))", y,z
end program main
! output: 10.0 20.0  0.0  0.0  0.0 10.0 20.0  0.0  0.0  0.0

I have tweeted about using move_alloc to do so more efficiently.

1 Like

Nice alternatives, thank you both. So I’m guessing the best option is still the sourced allocation, but unfortunately, more bound checking lines will always be necessary like

   pure subroutine resize(array,newsize)
        type(mytype), intent(inout), allocatable :: array(:)
        integer, intent(in) :: newsize

        integer :: copysize
        type(mytype), allocatable :: tmp(:)

        copysize = min(newsize,merge(ubound(array,1),0,allocated(array))
        allocate(tmp(newsize),source=array(:copysize))
        call move_alloc(from=tmp,to=array)

   end subroutine resize

Hi Federico, I was looking for how to resize an array, and I found this thread. At first I thought that using source was a neat solution, but then I started wondering: where will this memory go? Maybe at the beginning of the new array? At its end? Somewhere in the middle? I might be wrong, but this sounds like undefined behavior.

Then I found this StackOverlow answer, where the author says that it’s not allowed to have a source smaller than the new array.

Moving on, I checked the Intel documentation, where they only require that the two arrays have the same rank. And finally I checked the draft of Fortran 2023 standard, where in 9.7.1.1-3 it’s written that source-expr shall not depend on the bounds of any allocate-object in that statement.

What do you think about it?

I think it’s pretty clear that sourced allocation from a different-size array is non standard conforming. So, to achieve the one liner I was suggesting in the first place, @Beliavsky’s example is the way to go.
But I’m not sure about the overall safety/speed of this approach.

1 Like

This is where GitHub - PierUgit/enhanced-allocatables: Fortran allocatable arrays made resizable would be useful (the code is non-standard, it’s just there to illustrate the added value of resizable arrays).

1 Like

Unfortunately, the Fortran standard does not offer the much-desired flexibility with array resizing, refilling, rebinding, and rebinding + refilling (which by merging them, I call it rebilling). Frustrated with explaining user needs to the Fortran committee members (only to have the suggestions dismissed a day after with one stroke), a few years ago, we wrote four extensive multi-precision, multi-dimensional, fully generic modules specifically for the allocation tasks you described. These are:

Module Functionality
pm_arrayRebill This module contains procedures and generic interfaces for resizing allocatable arrays of various types, relocating their contents, and rebinding (re-indexing) their lower and upper bounds, and refilling the newly added elements.
pm_arrayRebind This module contains procedures and generic interfaces for resizing allocatable arrays of various types, relocating their contents and rebinding (re-indexing) their lower and upper bounds.
pm_arrayRefill This module contains procedures and generic interfaces for resizing allocatable arrays of various types, relocating their contents and filling the newly added elements with specific values.
pm_arrayResize This module contains procedures and generic interfaces for resizing allocatable arrays of various types and relocating their contents, without initializing or filling the newly added elements with specific values.

It’s hard to exaggerate how useful and time-saving these modules have been in subsequent developments. We paid careful attention to the design of interfaces to ensure flexibility and performance simultaneously. If you want to try the examples supplied with any of the above generic interfaces follow the QuickStart instructions here and the extra build flag exam to enable the example build and run for the specific module. For example,

./install.sh --exam pm_arrayResize

If you try any of the modules, we would appreciate your feedback and suggestions for improvement.

4 Likes

Just about everyone that has had to deal with Fortran allocatable arrays since F90 has wanted an intrinsic procedure to resize/reallocate and a dynamically varying array (like C++ vector classes) etc. Like most folks I ended up writing my own but they are limited to just my use cases and don’t cover the entire spectrum of potential use cases. I think that you have summed up the frustration of that many of us have with the Committee. We have legitimate needs that aren’t addressed by the current standard that potentially impacts the entire spectrum of Fortran users but the Committee appears to spend its time implementing things that experience has shown are only beneficial to a handful of users (even though the Committee appears to think otherwise). My personal opinion is that this “write it yourself” attitude is one of the major reasons people have migrated to other languages.

3 Likes

What is even more frustrating here is that implementing it both in the standard and in the compilers doesn’t look complicated: in my demo code, the C part that does the core of the job by manipulating the array descriptors is less than 100 lines. We could have the best of the Fortran arrays (multi-dimensions, arbitrary lower bounds…) and of the C++ vectors (resizeability…) at a little cost. It’s one or two orders below implementing a fully new type or a new data structure.

4 Likes

And anyway it can’t cover the the entire spectrum: resizing an allocatable array without moving the content is not possible at all with a standard-conforming code.

Yes. MOVE_ALLOC is a useful feature in the context of the current standard but if you want to keep the current contents of an array you are forced to use a temporary which can have a potential big impact on performance. People forget that one of the things that saved C++ and made it viable as a language for scientific computing is the introduction of expression templates that provided the compiler with more opportunities to optimize away some of the temporaries needed for things like operator overloading as well as a dynamic polymorphism etc. I read somewhere that a lot of the performance problems in the early days of C++ (more so than the OOP constructs) was due to the compiler creating a lot of temporaries when it tried to overload operators. I think an intrinsic facility for reallocation etc would also remove the need for the creation of temporaries you still have to use with MOVE_ALLOC. As I’ve stated in previous posts, if you don’t need save the contents of an array, the addition of an optional DEALLOCATE logical option to ALLOCATE that would tell the compiler to let ALLOCATE deallocate an existing array and resize it without throwing an error would go a long way to remove some of the frustration involved with just changing the size of an existing array. All current compilers will throw an error if you try to ALLOCATE an already allocated array without explicitly DEALLOCATEing it first.

ie

real, allocatable :: a(:)
allocate(a(10))
allocate(a(20), DEALLOCATE=.TRUE.)

would not throw an error.

A related thread is Do we need REALLOCATE? and issue is Add deallocate/reallocate option to ALLOCATE · Issue #61 · j3-fortran/fortran_proposals · GitHub.