簡體   English   中英

fortran運算符重載:函數或子例程

[英]fortran operator overloading: function or subroutine

我最近將我的.f90代碼更新為.f03,我期待看到加速,因為我的舊版本涉及許多分配和解除分配(7個3D陣列 - 45x45x45)在do循環內的每次迭代(總共4000個)。 使用派生類型,我在模擬開始時分配這些數組,並在結束時釋放它們。 我以為我會看到加速,但它實際上運行得慢得多(30分鍾而不是23分鍾)。

我運行了一個分析器,看起來加/減/乘/除運算符需要相對較長的時間。 除了標准變化的變化之外,就我所知,運營商是唯一的區別。 我想知道這是否是因為函數在每次操作期間都返回了字段數量的新副本。

所以這是我的問題:如果我將函數更改為子例程以便這些字段通過引用傳遞(我認為?),它可能運行得更快嗎? 此外,如果這更快,更優選,那么為什么所有這些示例都顯示運算符重載的函數而不是使用子例程? 我覺得我錯過了什么。

具有運算符重載的函數的引用:

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

這是我的一個運營商的例子:

    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

任何幫助或信息都非常感謝!

這里有兩個問題:

  1. 在某些情況下,使用子程序方法比函數方法可以獲得更好的性能嗎?
  2. 為什么,如果性能更差,我想使用一個函數嗎?

關於第一個問題,一個重要的事情是,你可能最好自己測試一些東西:這里有很多具體的方面。

但是,我很快就敲了一些可以指導你的事情。

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

有了這個,我觀察到有時在使用函數和子例程之間沒有顯着差異,有時會有。 也就是說,它取決於編譯器,標志等。

但是,重要的是要注意發生了什么。

對於函數,結果需要分配,對於子例程div_sub1intent(out)參數需要分配。 [分配功能結果會增加內容 - 請參閱后面的內容。]

div_sub2中重新使用分配(“result”參數是intent(inout) )並且我們通過使用q%x(:)來抑制自動重新分配。 后一部分很重要:編譯器經常會遇到檢查是否需要調整大小的開銷。 可以通過將div_sub1q的意圖更改為inout來測試后一部分。

[注意,這種div_sub2方法的假設是大小不變; 這似乎得到你的文字支持。]

總結第一個問題:檢查自己,但想知道你是否只是通過使用派生類型“隱藏”分配而不是刪除它們。 使用參數化派生類型可能會得到非常不同的答案。

談到第二個問題,為什么常用函數? 你會注意到我已經看過非常具體的案例:

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

從問題文本和鏈接(以及之前提出的問題)我會假設你有一些超載/運算符的東西

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),該過程必須是一個函數。 回應您對此答案的先前修訂版的評論

不像div_fun那樣div_sub1和div_sub2二元運算符?

答案是“不”,至少就Fortran定義為二元運算符而言(鏈接如上)。 [此外, div_fun本身不是二元運算符,它是函數和形成操作的通用接口的組合。

使函數方法具有吸引力的是二元運算可以是表達式的一部分:

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)

使用子程序可能會有點亂。 上面的例子可以通過處理“就地”方面(或專家子程序)來略微整理,但這讓我想到了最后一點。 因為函數結果在之后的使用(包括賦值)之前完全被評估,所以我們有類似的情況

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

總結第二個問題:表現並非一切。

[最后,如果您的意思是使用.f90.f03文件后綴來表示/管理標准合規性,那么您可能希望了解人們對此的看法。]

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM