Fortran statement overload: function or routine

I recently upgraded my .f90 code to .f03, and I was expecting acceleration because my old version included a lot of allocation and deallocation (7 3D arrays - 45x45x45) at each iteration inside the do loop (total 4000), Using derived types , I allocate these arrays at the beginning of the simulation and free them at the end. I thought I would see the acceleration, but in fact it works much slower (30 min vs 23 min).

I ran the profiler, and the add / subtract / multiply / divide statements seem to take a relatively long time. As far as I can tell, apart from changing the standard change, operators are the only difference. I am wondering if this is due to the fact that functions return new copies of field values ​​during each operation.

So, here is my question: can it work faster if I change functions to routines so that these fields are passed by reference (I think?)? Also, if this is faster and more preferable, then why do all of these examples show operator overload functions instead of using routines? I feel something is missing.

Links for functions with operator overloading:

http://www.mathcs.emory.edu/~cheung/Courses/561/Syllabus/6-Fortran/operators.html

http://research.physics.illinois.edu/ElectronicStructure/498-s97/comp_info/overload.html

https://web.stanford.edu/class/me200c/tutorial_90/13_extra.html

https://www.ibm.com/developerworks/community/blogs/b10932b4-0edd-4e61-89f2-6e478ccba9aa/entry/object_oriented_fortran_does_fortran_support_operator_overloading55?lang=en

Here is an example of one of my operators:

function vectorVectorDivide(f,g) result(q) implicit none type(vectorField),intent(in) :: f,g type(vectorField) :: q q%x = f%x / g%x; q%y = f%y / g%y; q%z = f%z / g%z q%sx = f%sx; q%sy = f%sy; q%sz = f%sz end function 

Any help or information on this subject is welcome!

+4
source share
1 answer

There are two questions here:

  • In some situations, can I get better performance using a routine approach with a functional approach?
  • Why, if performance is worse, would I use a feature?

The most important thing to say about the first question is that you can best test yourself: there are many specific aspects to this.

However, I quickly knocked down something that might help you.

 module test implicit none type t1 real, allocatable :: x(:) end type t1 contains function div_fun(f,g) result(q) type(t1), intent(in) :: f, g type(t1) q q%x = f%x/g%x end function div_fun subroutine div_sub1(f, g, q) type(t1), intent(in) :: f, g type(t1), intent(out) :: q q%x = f%x/g%x end subroutine div_sub1 subroutine div_sub2(f, g, q) type(t1), intent(in) :: f, g type(t1), intent(inout) :: q q%x(:) = f%x/g%x end subroutine div_sub2 end module test 

At the same time, I noticed that sometimes there was no significant difference between the use of a function and a subprogram, and sometimes it was. That is, it depends on compilers, flags, etc.

However, it is important to note what is happening.

For a function, the result requires allocation, and for the subroutine div_sub1 the intent(out) argument requires allocation. [Assigning the result of a function adds to things - see below.]

In div_sub2 distribution is reused (the argument "result" is intent(inout) ), and we suppress the automatic redistribution with q%x(:) . This is the last important part: compilers often suffer from overhead to check if resizing is required. This last part can be verified by changing the q intent in div_sub1 to inout .

[Note that there is an assumption for this approach div_sub2 , whose dimensions are not div_sub2 ; it looks like your text.]

In conclusion, for the first question: check for yourself, but ask yourself if you are just β€œhiding” distributions using derived types rather than deleting them. And you can get very different answers using parameterized derived types.

Returning to the second question, why are functions commonly used? You will notice that I examined very specific cases:

 q = div_fun(f,g) call div_sub2(f,g,q) ! Could be much faster 

From the text of the question and the links (and the previous questions that you asked), I assume that you have something that overloads the / operator

 interface operator (/) module procedure div_fun end interface 

allows

 q = f/g ! Could be slower, but looks good. call div_sub2(f,g,q) 

since we note that for use as a binary operator (see Forran 2008 7.1.5, 7.1.6), the procedure must be a function. In response to your comment on a previous revision of this answer

are not binary div_sub1 and div_sub2 operators, like div_fun?

the answer is no, at least in terms of what Fortran defines as binary operators (referenced as above). [In addition, div_fun itself is not a binary operator, it is a combination of a function and a common interface that form the operation.]

The appeal of the function approach is that a binary operation can be part of an expression:

 q = q + alpha*(f/g) ! Very neat call div_sub2(f,g,temp1) call mult_sub(alpha, temp1, temp2) call add_sub(q, temp2, temp3) call assign_sub(q, temp3) 

Using routines can be a bit confusing. The above example can be tidied up a bit by processing the in-place aspects (or specialized routines), but that leads me to the end point. Since the result of the function is evaluated in its entirety until its subsequent use (including purpose), we have situations such as

 f = f/g ! or f=div_fun(f,g) call div_sub2(f,g,f) ! Beware aliasing 

In conclusion, for the second question: performance is not everything.

[Finally, if you mean that you use the suffixes of the .f90 and .f03 files to denote / adjust the standard match, then you can see what people think about it.]

+7
source

Source: https://habr.com/ru/post/985595/


All Articles