I get the fortran runtime warning "An array temporary was created" when running my code (compiled with gfortran) and I would like to know if there is a better way to solve this warning.
My original code is something like this:
allocate(flx_est(lsign,3))
allocate(flx_err(lsign,3))
do i=1,lsign
call combflx_calc(flx_est(i,:),flx_err(i,:))
enddo
Inside the subroutine I define the variables like this:
subroutine combflx_calc(flx_est,flx_err)
use,intrinsic :: ISO_Fortran_env, only: real64
implicit none
real(real64),intent(inout) :: flx_est(3),flx_err(3)
flux_est and flx_err vectors may change inside the subroutine depending on several conditions and I need to update their values accordingly.
Fortran does not seem to like this structure. I can solve it defining temporary variables:
tmp_flx_est=flx_est(i,:)
tmp_flx_err=flx_err(i,:)
call combflx_calc(tmp_flx_est,tmp_flx_err)
flx_est(i,:)=tmp_flx_est
flx_err(i,:)=tmp_flx_err
But it seems to me quite a silly way to fix it.
As you may see I'm not an expert with Fortran, so any help is more than welcome.
One way is to pass an assumed shape array
real(real64),intent(inout) :: flx_est(:),flx_err(:)
the other is to exchange the dimensions of your array, so that you can pass a contiguous section of the 2D array.
call combflx_calc(flx_est(:,i),flx_err(:,i))
The problem is that the explicit size dummy arguments of your procedure (var(n)) require contiguous arrays. The assumed shape arrays can have some stride.
Your array temporary is being created because you are passing a strided array to your subroutine. Fortran arrays are column major so the leftmost index varies fastest in an array, or better said, the leftmost index is contiguous in memory and each variable to the right is strided over those to the left.
When you call
call combflx_calc(flx_est(i,:),flx_err(i,:))
These slices are arrays of your 3-vector strided by the length of lsign. The subroutine expects variables of a single dimension contiguous in memory, which the variable you pass into it is not. Thus, a temporary must be made for the subroutine to operate on and then copied back into your array slice.
Your "fix" does not change this, it just not longer warns about a temporary because you are using an explicitly created variable rather than the runtime doing it for you.
Vladimir's answer gives you options to avoid the temporary, so I will not duplicate them here.
Related
I just begin learning Fortran, and I come across this issue. Consider the following simple code.
PROGRAM random
INTEGER, DIMENSION(12):: array
PRINT *, array
END PROGRAM random
The array is not assigned value, but can be printed, and it seems to have a few random elements and several zero elements. However, if I consider a shorter array, say I declare
INTEGER, DIMENSION(5):: array
then the printed array has all elements = 0. I wonder what is happening here?
When you define an array and try to look at the values it contains (i.e. by printing) before initialising it, the behaviour is undefined. It depends on compiler to compiler. While one compiler may automatically set all the values to zero (which many of us think that to be the default), another compiler may set it to completely random values. Which is why you are seeing the array values sometimes zero and sometimes not.
However, many of the compilers have options to initialise an unassigned array to zeros at compiler level. It is always advised that one should initialise an array always before using it!
If you do not initialize the variable you are seeing whatever happens to be in the memory that your variable occupies. To initialize your array to 0, use the statement:
integer, dimension(12) :: array
array = 0
If you do not initialize your variable before accessing it, your are using undefined behavior and your program is invalid.
When I run a Fortran code, I get this warning:
Fortran runtime warning: An array temporary was created for argument '_formal_11' of procedure 'zgemm'
related to this part of the code
do iw=w0,w1
!
do a=1,nmodes
Vw(a,:)=V(a,:)*w(iw,:)
end do
call zgemm('N', 'C',&
nmodes, nmodes, nbnd*nbnd, &
(1.d0,0.0d0),&
Vw, nmodes, &
V, nmodes, &
(0.d0,0.0d0), VwV(iw,:,:), nmodes)
end do
!
If I have understood well, the warning is related to passing non-continguous arrays which could affect the preformances. I would like to take care of this. However it is not clear to me what exactly is the problem here, and what I could do to solve it.
What is going on, is that you activated compiling flags that will warn you of temporary array creation at runtime.
Before getting to more explanation, we have to take a better look at what an array is. An array is an area in memory, together with the information needed to interpret it correctly. Those information include but
are not limited to the data type of the elements, number of dimensions, The start-index and end-index of each dimension, and most importantly, the gap between two successive element.
In very simplistic terms, Fortran 77 and below do not have a built-in mechanism to pass in the gap between successive elements. So when there is no explicit interface of the called subroutine, the compiler ensures that there is no gap between successive element by copying data to a temporary contiguous array. This is a safe mechanism to ensure the predictability of the behavior of the subroutine.
When using modules, Fortran 90 and above use a descriptor to pass those information to the called subroutine; that works hand-in-hand with assumed-shape declaration of arrays. This is also a simplistic description.
In summary, that is a warning that will be of importance only if the performance is affected as Vladimir said.
If I have a loop like
do i = 1,much
call computations(input(i,:),output(i,:))
enddo
and
subroutine computations(inn,outt)
real, intent(in) :: inn(:)
real, intent(out) :: outt(:)
real :: temp(size(inn))
...
end subroutine
will the array temp be allocated and deallocated on each call? We can assume that the size of input and output are not changing. If I did not have a subroutine but rather inline code in the loop, then this array would have to be defined higher up, and would not be reallocated on each loop iteration. Will the compiler realize this? Does it depend on optimization level/compiler?
Yes, the array will be allocated on each call. However, if it is allocated on the stack, the allocation is essentially free (just updating the stack pointer). One can never be sure about compiler optimizations unless you specify the compiler and the version, but I am not aware of any optimization like this, that would be very complicated. And also we would have to know the size of the array and whether the compiler allocates on the stack or on the heap.
If the subroutine is internal, you can allocate the array higher up. You can also allocate it higher up and pass it as an argument. But only do that if it really brings anything. If it is a rather small stack array, it would not achieve much.
is it possible to assign the size and values of a common array in a subroutine and then use it from other subroutines of the program?
The following program doesn't work, but I want to do something like this:
main.f
program main
integer n
integer, allocatable :: co(:)
common n, co
call assign
print *, co(1), co(2)
deallocate(co)
stop
end program main
assign.f
subroutine assign
integer n
integer, allocatable :: co(:)
common n, co
n = 2
allocate(co(n))
co(1) = 1
co(2) = 2
return
end subroutine assign
No. You can put pointers into common, but not allocatables.
The reason is that a concept fundamental to common is storage association, where you can make a contiguous sequence of all the things that are in the common and those sequences are then shared amongst scopes. Allocatables can have their size vary dynamically in a scope, which would make the tracking in the sequence of things in the common block that came after the allocatable rather difficult.
(Typical implementation of allocatables means that the storage directly associated with the allocatable is just a descriptor - the actual data is kept elsewhere. This practically breaks the concept of a contiguous sequence of storage units, given that the allocation status (as recorded in the descriptor) and the data are both part of the value of the allocatable. The implementation for pointers is similar, but then conceptually the data that is elsewhere in memory is not part of the value of the pointer, so it should not be expected to appear in the contiguous sequence that the common describes - the pointer is in the sequence, but not what it points at.)
Allocatables require F90. That means that you can use module variables - which are a far better solution than the use of common for global data. If you must do this using common, then use a data pointer.
I'm going through a Fortran code, and one bit has me a little puzzled.
There is a subroutine, say
SUBROUTINE SSUB(X,...)
REAL*8 X(0:N1,1:N2,0:N3-1),...
...
RETURN
END
Which is called in another subroutine by:
CALL SSUB(W(0,1,0,1),...)
where W is a 'working array'. It appears that a specific value from W is passed to the X, however, X is dimensioned as an array. What's going on?
This is non-uncommon idiom for getting the subroutine to work on a (rectangular in N-dimensions) subset of the original array.
All parameters in Fortran (at least before Fortran 90) are passed by reference, so the actual array argument is resolved as a location in memory. Choose a location inside the space allocated for the whole array, and the subroutine manipulates only part of the array.
Biggest issue: you have to be aware of how the array is laid out in memory and how Fortran's array indexing scheme works. Fortran uses column major array ordering which is the opposite convention from c. Consider an array that is 5x5 in size (and index both directions from 0 to make the comparison with c easier). In both languages 0,0 is the first element in memory. In c the next element in memory is [0][1] but in Fortran it is (1,0). This affects which indexes you drop when choosing a subspace: if the original array is A(i,j,k,l), and the subroutine works on a three dimensional subspace (as in your example), in c it works on Aprime[i=constant][j][k][l], but in Fortran in works on Aprime(i,j,k,l=constant).
The other risk is wrap around. The dimensions of the (sub)array in the subroutine have to match those in the calling routine, or strange, strange things will happen (think about it). So if A is declared of size (0:4,0:5,0:6,0:7), and we call with element A(0,1,0,1), the receiving routine is free to start the index of each dimension where ever it likes, but must make the sizes (4,5,6) or else; but that means that the last element in the j direction actually wraps around! The thing to do about this is not use the last element. Making sure that that happens is the programmers job, and is a pain in the butt. Take care. Lots of care.
in fortran variables are passed by address.
So W(0,1,0,1) is value and address. so basically you pass subarray starting at W(0,1,0,1).
This is called "sequence association". In this case, what appears to be a scaler, an element of an array (actual argument in caller) is associated with an array (implicitly the first element), the dummy argument in the subroutine . Thereafter the elements of the arrays are associated by storage order, known as "sequence". This was done in Fortran 77 and earlier for various reasons, here apparently for a workspace array -- perhaps the programmer was doing their own memory management. This is retained in Fortran >=90 for backwards compatibility, but IMO, doesn't belong in new code.