2017-07-26 80 views
1

在C++中,通常會將可能內聯到頭文件中的小函數放在可能的內聯中,而無需藉助鏈接時優化或其他巫術。大多數情況下,對於類的訪問器方法(請參閱std::vector上的operator[])。我在現代Fortran中遇到了一些類似的行爲。派生類型訪問函數的跨模塊內聯

說我有一個定義派生類型與一些私有數據,用一個簡單的存取這樣的模塊:

module FooMod 
type :: FooType 
    integer,allocatable,private :: d(:) 

    contains 
     procedure,pass :: init 
     procedure,pass :: get 
     procedure,pass :: clear 
endtype 

contains 
    subroutine init(this,n) 
     class(FooType),intent(inout) :: this 
     integer,intent(in) :: n 
     integer :: i 

     allocate(this%d(n)) 
     do i=1,n 
      this%d(i)=i 
     enddo 
    endsubroutine 

    function get(this,i) result(val) 
     class(FooType),intent(in) :: this 
     integer,intent(in) :: i 
     integer :: val 

     val = this%d(i) 
    endfunction 

    subroutine clear(this) 
     class(FooType),intent(inout) :: this 

     deallocate(this%d) 
    endsubroutine 
endmodule 

現在我寫一個程序使用訪問:

program testtype 
use FooMod 

type(FooType) :: foo 
integer :: val 

call foo%init(10) 

val = foo%get(2) 

write(*,*)val 

endprogram 

用gfortran編譯5.4.0用-O3

gfortran -c -O3 foo.f90 
gfortran -O3 -S testfoo.f90 foo.o 

產生輸出像這樣:

call __foomod_MOD_get 
leaq 16(%rsp), %rdi 
movl %eax, 12(%rsp) 
movq $.LC2, 24(%rsp) 
movl $11, 32(%rsp) 
movl $128, 16(%rsp) 
movl $6, 20(%rsp) 
call _gfortran_st_write 

所以仍然有一個調用發生,沒有內聯。我知道,因爲get()例程的定義位於不同的翻譯單元中,因此與C++標題的文本相比可能有點困難,但我認爲這是.mod文件生成的目的的一部分編譯器。

有沒有什麼辦法讓這樣的小功能跨模塊內聯?如果沒有,在數據封裝的現代編程實踐中,這似乎是語言/實現方面的嚴重缺陷。

我試着-flto標誌來看看這是否會有所幫助,但只是吐出GIMPLE ascii文本字段到「程序集」輸出,所以很難說出它在做什麼。

謝謝!一些澄清:我知道C++中的inline,它的真正含義,它有點不恰當,並且非常熟悉內聯的概念;它是如何工作的,需要滿足哪些條件,爲什麼沒有標題很難,以及LTO適合於圖片。我的主要挫折是,由於Fortran有正式的模塊,編譯器允許編譯器生成實現特定的.mod文件,爲什麼這麼困難呢?爲什麼我們需要明確地使用語言不可知的鏈接時間(困難)內聯的大槍來做一些簡單的內聯類型綁定存取器函數。編譯器能否存儲GIMPLE代碼,甚至是.mod文件中的函數文本,並執行編譯時內聯?也許我錯過了一些微妙之處。

+0

你使用了哪些編譯器選項? '-flto'確實是完全必要的,它是在不同的文件中編譯的,使用不同的gfortran調用。 –

+1

您可能需要查看與'-flto' [此處](https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html)相關的文檔來記錄一些可能有助於您的相關選項調查(例如'-fno-fat-lto-objects')。請注意,'-flto'必須在編譯和鏈接階段提供。 –

+1

另外[相關問題](https://stackoverflow.com/a/42386798) –

回答

2

在C/C++中,您通常會#include包含訪問器函數定義的頭文件。因此,在預處理之後,定義與調用站點位於同一C/C++翻譯單元中,並且該函數可以內聯,即使編譯器的前端也是如此。

在Fortran中,程序,模塊,外部子程序,外部功能和塊數據都定義了單獨的編譯單元。即使它們出現在相同的源文件中,編譯器也可以將它們視爲出現在單獨的文件中。有些編譯器會這樣做,因此如果沒有鏈接時間優化(LTO),就不能進行內聯。如果definitoins與呼叫站點出現在相同的源文件中,則其他人嘗試內聯。我知道只有一個編譯器將模塊過程的定義嵌入到它們的.mod文件中,以便它們可以內聯在使用模塊的任何位置。

TL; DR如果要在Fortran中嵌入這些函數,則必須啓用LTO。

+0

你指的是哪個編譯器在.mod文件中存儲函數定義? – youngmit

+2

Cray編譯器有一個選項「-O modinline」以允許內聯模塊過程。 (我相信沒有LTO。) 請注意,Fortran標準郵件列表中多次出現此問題。例如[這裏](http://mailman.j3-fortran.org/pipermail/j3/2014-May/007435.html)。 聲明:我使用IBM編譯器。我們要求啓用LTO以將模塊過程內聯到其他編譯單元。 –