mac osx catalina (latest)
gfortran 9.2
fortran standard "gnu"
Through something like the code below, I came to understand that, at least in this case, that within the function get_number the result value is constructed, and then results are copied over to the receiving variable. Probably I've also done a no-no by having a pointer target a local variable inside that function, but perhaps the compiler was being nice to me by not deallocating a local variable so my pointer was still okay.
My question is, is this how all function calls behave, namely that the value, whether a simple type or a complex type, is constructed in memory, and then the values are copied over and the original result is thrown away? I was under the impression that the variable receiving the value of the function call was just given the memory of the constructed result.
Suppose I felt that lots of memory copying was wasteful, or suppose as in the code below that it breaks my code, and I wanted to avoid it when doing many many function calls. Is the solution to replace them with subroutine calls? But I like function calls. If memory copying is what happens normally, is there a way to avoid memory copying when doing function calls?
module nums
type number
real :: n
end type number
type number_parent
type(number), pointer :: p
contains
procedure, pass(this) :: get_number
end type number_parent
contains
function get_number(this) result(n)
class(number_parent) :: this
type(number), allocatable, target :: n
allocate(n)
n%n = 1.0
this%p => n
end function get_number
end module nums
program main
use nums
implicit none
type(number) :: n
type(number_parent) :: np
n = np%get_number()
print *, n%n
print *, np%p%n
n%n = n%n + 1.0
print *, n%n
print *, np%p%n
end program main
program output
> ./a.out
1.00000000
1.00000000
2.00000000
1.00000000
I understood your question is about a thing called return value optimization and you mainly were thinking about simple variables, non-pointer, non-allocatable. My answer will only touch these. I will also assume no advanced derived type features like polymorphism and finalization.
I will also assume you use the function only in a simple assignment. Of course, functions can appear in many complicated expressions in general.
When you return a simple variable from a function
integer :: ext
ext = func(1)
contains
integer function func(loc) result(res)
res = loc + 1
end function
end
then what conceptually happens is that a local variable is used for the return value res and its value will be copied to the outer variable after the assignment.
In practice, if a separate variable will indeed be used, it will be allocated on the stack (that costs nothing, not just almost nothing, really nothing). Heap may be used for large objects (mostly arrays) and compilers often have settings for that, but normal variables will be on the stack. Then, when the function returns, the value is copied to the location of the outer variable ext and the stack pointer is reset (that just means some other function or subroutine will use that memory later).
The copying can indeed cost something and it may or may not be negligible. compilers will very often be able to optimize this and make the function to use the outer variable ext directly for its result variable res so that no copying is necessary. However, to see whether it is or is not happening you will have to check the machine code/assembly (or, if available, any intermediate representation) generated by the compiler. It may vary depending on the optimization level (-O1, -O2, -O3) and on the exact context of the function call in the source code.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With