I’ve been hesitant to abstract some array operations with the fear that an unnecessary copy would be made when passing array expressions as argument. A sample case for the operation:
subroutine mid_point(n, x, x_mid)
integer, intent(in) :: n
real, intent(in) :: x(n)
real, intent(out) :: x_mid(n - 1)
integer :: i
do i = 1, n - 1
x_mid(i) = 0.5 * (x(i) + x(i + 1))
end do
end subroutine
And for a potential call:
call mid_point(n, a + b/c, result)
To my understanding, if mid_point is not inlined, a + b/c is evaluated before the subroutine execution, requiring a transient array. Is that correct?
Your potential call lacks the first argument, n, but otherwise I guess you’re right, I don’t see any way (other than inlining) to avoid creating temporary array to be passed to the subroutine with the expression result.
[Fortran view] No. There is no Fortran requirement to store an actual argument, because such a thing cannot be named and consequently cannot ever be referred to ever again. That sequence of values exists for the duration of the CALL statement and consequently the compiler can lay down any code that achieves the same result.
[Compiler view] But it probably won’t.
Have you considered using intrinsic array transformations CSHIFT/EOSHIFT?
I’ve tried some experiments, and the result from gfortran-15 -fdump-tree-optimized (on MacM1) seems like the following (according to the .optimized file, though not very sure about the details of this file…):
If mid_point() is defined in a separate module from the call statement, then a temporary array is created and passed to mid_point() explicitly (even if I attach -O3 -flto etc);
If mid_point() and the routine calling it are defined in the same module, then the former is inlined with -O3 (so no explicit call for mid_point());
Similarly, if mid_point() is defined as an internal routine and the call statement is in the host scope, then the former is inlined again with -O3.
So at least for gfortran, defining small library routines in the same module or scope redundantly via include etc might help…? (if really necessary)
Apart from that, I am wondering if there are other languages that can send such a math expression directly to another routine without making a temporary array…? (possibly an expression template + “auto” argument can do that kind of thing…?)
To clarify, the dummy is known to be non-pointer, non-allocatable (either by explicit interface or implicit interface) and so cannot be pointer associated with anything. One can go through the entire list of other possible associations and determine that none apply here. Therefore, the entire actual argument array is “ethereal” (“made up”) and does not need to be stored, and the entire calculation can be done one element at a time, or 16 elements at a time, and in any order (the compiler can see the loop body and determine that), and on whatever resources the compiler can summon. If the compiler can determine that a violation of Fortran conformance happens just AFTER the CALL, or that RESULT is never referenced, it can even avoid doing any work altogether.
Thanks, I will check that page! (BTW I still do not understand why it is called “thunk”, though the above page says “The term originated as a whimsical irregular form of the verb think”…) It seems to be related to various other things (listed at the bottom of the above page), so I will check them also.
I’ve just tried changing all the test codes from explicit-shape to assumed-shape, but the results were essentially the same… (for Gfortran). Other compilers may show different behaviors, though
The term dates from the early 1960s - when folks were implementing the first ALGOL-60 compilers. Check out the Jan 1961 ACM paper referenced in the wikipedia page.
Yes, I am a bit more familiar with closures, and it seems common to pass expressions via closures (e.g. in C++?). But I was wondering if some languages might support passing & receiving a math expression directly as an argument (as in the OP’s code). I remember D or Chapel were able to do that, but trying a bit with Chapel right now, the code became an error (because of some lifetime errors…) The “range” in D might be closer to what I imagined but need to check again. RE functional programming languages, I’ve never tried them up to now, so it seems interesting to learn (for this kind of things).
I think in this situation, the input array expression argument would need to be evaluated and stored in a temp array and also the output array would need to be allocated. The former would probably be done by the caller, and the latter by the callee, so they would not typically be done together with a single allocation operation. Of course, if inlining occurs, then that would be a different situation.
I have seen languages where expressions could be used and passed as arguments. These are usually interpreted languages, such as Mathematica, which I think has this capability but I don’t remember the syntax offhand. Scripting languages, such as bash, have the eval() command to evaluate an expression with its current values (typically for scalar expressions, not arrays). PL1, a compiled language, is another possibility that might have had this feature.