fortran操作符重载:函数或子例程 [英] fortran operator overloading: function or subroutine

查看:196
本文介绍了fortran操作符重载:函数或子例程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

最近我将.f90代码更新为.f03,而且我期待看到加速,因为我的旧版本在do循环(总共4000次)的每次迭代中涉及许多分配和释放(7个3D数组 - 45x45x45) 。使用派生类型,我在模拟开始时分配这些数组,并在最后解除分配它们。我认为我会看到加速,但实际上运行速度明显较慢(30分钟,而不是23分钟)。



我运行了一个profiler,看起来像add /减/乘/除运算符需要相对较长的时间。除了标准变化的变化之外,据我所知,操作人员是唯一的区别。我想知道这是否是由于函数在每次操作中都返回了字段数量的新副本。



所以这里是我的问题:它可能运行得更快如果我将函数更改为子例程,以便这些字段通过引用传递(我认为?)?另外,如果这更快,更优选,那么为什么所有这些示例都显示了运算符重载而不是使用子例程的函数?

运算符重载函数的参考:

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 -88f2-6e478ccba9aa / entry / object_oriented_fortran_does_fortran_support_operator_overloading55?lang = en



以下是我的一个操作员的示例:

 函数vectorVectorDivide(f,g)结果(q)
隐式无
类型(vectorField),intent(in):: f,g
类型(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
最终功能

赞赏!

解决方案

这里有两个问题:


  1. 在某些情况下,我可以通过函数方法使用子程序方法获得更好的性能吗?

  2. 为什么如果性能更差,我想使用函数吗?

对于第一个问题要说的重要一点是,您可能最适合自己测试:有很多特定的方面这个。



然而,我很快就会敲起一些可能会引导你的东西。

 模块测试

隐式无

类型t1
实数,可分配:: x(:)
结束类型t1

包含

函数div_fun(f,g)结果(q)
类型(t1),意图(in):: f,g
类型(t1) q
q%x = f%x / g%x
结束函数div_fun

subro utine div_sub1(f,g,q)
type(t1),intent(in):: f,g
type(t1),intent(out):: q
q%x = f%x / g%x
结束子程序div_sub1

子程序div_sub2(f,g,q)
类型(t1),intent(in):: f,g
类型(t1),意图(inout):: q
q%x(:) = f%x / g%x
结束子程序div_sub2

结束模块测试

有了这个,我发现有时使用函数和子例程之间没有明显的区别,有时候会有。也就是说,它取决于编译器,标志等。



然而,重要的是要注意发生了什么。



对于该函数,结果需要分配,对于子程序 div_sub1 intent(out)论点需要分配。 [赋值函数结果增加了一些东西 - 见后面。]



div_sub2 中,重新使用了分配结果参数是 intent(inout)),我们使用 q%x(:)。后面的部分很重要:编译器经常会因为检查是否需要调整大小而遭受开销。后一部分可以通过将 div_sub1 中的 q 改为 inout code $。

[请注意,这个 div_sub2 方法的一个假设是size'不断变化;这似乎是支持你的文本。]



总结第一个问题:检查你自己,但不知道你是否仅仅通过派生类型隐藏分配而不是删除它们。你可能会得到非常不同的答案,使用参数化派生类型。



回到第二个问题,为什么常用函数?你会注意到我已经看过非常具体的情况:

  q = div_fun(f,g)
调用div_sub2(f,g,q)!可以更快

从问题文本和链接(以及您问过的问题)假设你有一些重载 / 运算符的东西

 接口运算符(/)
模块过程div_fun
结束接口

允许

  q = f / g!可能会更慢,但看起来不错。 
调用div_sub2(f,g,q)

我们注意到要用作一个二元运算符(参见Fortran 2008 7.1.5,7.1.6),该过程必须是一个函数。为回应您对此答案的以前版本的评论,您可以使用


不是div_sub1和div_sub2二元运算符,就像div_fun?

p>

答案是否,至少就Fortran定义的二元运算符而言(链接如上)。 [另外, div_fun 本身不是一个二元运算符,它是组合操作的函数和通用接口。]
$

  q = q 

使函数方法更具吸引力是二进制操作可以是表达式的一部分: + alpha *(f / g)!非常整齐的
调用div_sub2(f,g,temp1)
调用mult_sub(alpha,temp1,temp2)
调用add_sub(q,temp2,temp3)
调用assign_sub(q, temp3)

使用子程序会变得有点麻烦。上面的例子可以通过处理in-place方面(或专业子程序)稍微整理一下,但是这使我想到了最后一点。因为函数结果在其后使用(包括赋值)之前被完全评估,所以我们有如下的情况:

  f = f / g!或f = div_fun(f,g)
调用div_sub2(f,g,f)!谨防锯齿

总结第二个问题:表现并非全部。



[最后,如果你的意思是你使用 .f90 .f03 文件后缀表示/管理标准符合性,那么你可能想看看人们怎么想。]


I recently updated my .f90 code to .f03, and I was expecting to see speedup because my older version involved many allocating and deallocating (7 3D arrays--45x45x45) at each iteration inside a do loop (4000 in total). Using derived types, I allocate these arrays at the beginning of the simulation and deallocate them at the end. I thought that I would see speedup, but it's actually running significantly slower (30 min as opposed to 23 min).

I ran a profiler, and it looks like the add/subtract/multiply/divide operators are taking a relatively long time. Aside from the change in the standard change, the operators are the only difference as far as I can tell. I'm wondering if this is due to the fact that functions are returning new copies of the field quantities during every operation.

So here is my question: might it run faster if I change the functions to subroutines so that these fields are passed by reference (I think?)? Also, if this is faster, and more preferred, then why are all of these examples showing functions for operator overloading instead of using subroutines? I feel like I'm missing something.

References 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's 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 info on this is greatly appreciated!

解决方案

There are two questions here:

  1. In some situations, can I get better performance using a subroutine approach over a function approach?
  2. Why, if performance is worse, would I want to use a function?

An important thing to say about the first question is, you may be best testing things for yourself: there are a lot of specific aspects to this.

However, I have quickly knocked something up which may guide 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

With this, I observed that sometimes there was no significant difference between using a function and a subroutine, and sometimes there was. That is, it depends on compilers, flags, and so on.

However, it is important to note what is happening.

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

In div_sub2 the allocation is re-used (the "result" argument is intent(inout)) and we suppress an automatic re-allocation by using q%x(:). It's this latter part that is important: compilers often suffer overheads for doing the checking about whether resizing is required. This latter part can be tested by changing the intent of q in div_sub1 to inout.

[Note that, there is an assumption for this div_sub2 approach that sizes aren't changing; this seems supported by your text.]

To conclude for the first question: check for yourself, but wonder whether you are merely "hiding" allocations by using derived types rather than removing them. And you may get very different answers using parameterized derived types.

Coming to the second question, why are functions commonly used? You'll note that I've looked at very specific cases:

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

From the question text and the links (and previous questions you've asked) I'll assume that you have something which overloads the / operator

interface operator (/)
  module procedure div_fun
end interface

allowing

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

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

aren't the div_sub1 and div_sub2 binary operators just like the div_fun?

the answer is "no", at least in terms of what Fortran defines as being binary operators (link as above). [Also, div_fun isn't itself a binary operator, it's the combination of the function and the generic interface that form the operation.]

Making the function approach attractive 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 subroutines can get a little messy. The example above could be tidied slightly by handling "in-place" aspects (or specialist subroutines), but this brings me to a final point. Because a function result is evaluated entirely before its later use (including assignment) we have situations like

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

To conclude for the second question: performance isn't everything.

[Finally, if you mean you are using .f90 and .f03 file suffixes to denote/govern standard compliance, then you may want to see what people think about that.]

这篇关于fortran操作符重载:函数或子例程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆