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

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

问题描述

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

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/电子结构/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_orient_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!

推荐答案

这里有两个问题:

  1. 在某些情况下,与函数方法相比,使用子程序方法可以获得更好的性能吗?
  2. 如果性能更差,我为什么要使用函数?

关于第一个问题要说的重要一点是,您最好自己测试一下:这有很多具体方面.

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.

对于函数,结果需要分配,对于子例程div_sub1intent(out) 参数需要分配.[分配函数结果会增加事情 - 见下文.]

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.]

div_sub2 中,分配被重新使用(result"参数是 intent(inout))并且我们通过使用 来抑制自动重新分配q%x(:).后一部分很重要:编译器经常在检查是否需要调整大小时承受开销.可以通过将 div_sub1q 的意图更改为 inout 来测试后一部分.

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.

[请注意,这种 div_sub2 方法的假设是尺寸不变;你的文字似乎支持这一点.]

[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

允许

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

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

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

div_sub1 和 div_sub2 二元运算符不就和 div_fun 一样吗?

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

答案是不",至少就 Fortran 定义的二元运算符而言(链接如上).[另外,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.

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

[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天全站免登陆