【发布时间】:2017-07-26 15:33:07
【问题描述】:
在 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 文件,为什么这么难?为什么我们需要明确地求助于语言不可知的、链接时间(困难的)内联的大枪来做像内联类型绑定访问器函数这样简单的事情。编译器不能在 .mod 文件中存储 GIMPLE 代码,甚至函数文本并进行编译时内联吗?也许我遗漏了一些微妙之处。
【问题讨论】:
-
您使用了哪些编译器选项?
-flto确实是完全必要的,它编译在不同的文件中,在不同的 gfortran 调用中。 -
您可能需要查看与
-fltohere 相关的文档,以注意一些可能有助于您调查的相关选项(例如-fno-fat-lto-objects)。请注意,-flto必须在编译和链接阶段都提供。 -
它默认为
-fno-fat-lto-objects。添加-ffat-lto-objects将GIMPLE 字节码和 常规程序集放入最终可执行文件的.s 文件中。不过,call仍然存在。 -
我想我属于后一类,如果有意义的话,希望编译器会这样做。我发现自己处于一种情况,它使世界上所有的东西都有意义,而编译器却没有这样做。我写了大量的 C++ 代码,并且不时检查编译器是否内联了这样的简单函数,而且几乎总是这样。无需踢腿。