在没有任何结果的搜索之后,我想问你一个关于包含可选参数的子例程以及编译器如何处理它们的问题(运行时/编译时)。请考虑以下示例程序。
module CONTAINS_ABC
contains
subroutine ABC( a, b, c)
implicit none
real, intent(in) :: a
real, intent(in), optional :: b, c
if( present(b) )then ! PATH_B
write(*,*) 'Provide c'
else if( present(c) )then "! PATH_C
write(*,*) 'Provide b'
end if
! Do all the computations with a, b and c
end subroutine ABC
end module CONTAINS_ABC
program CALL_ABC
use CONTAINS_ABC
real :: aa, bb, cc
call ABC( a = aa, b=bb )
call ABC( a = aa, c=cc )
end program CALL_ABC
我想知道编译器如何使用optimization
方面的可选参数处理子例程。编译器是否为子程序 FCN 隐式生成两个接口,然后在主程序的编译期间选择正确的接口?此外,是在运行时还是在编译期间评估的 present(b)/ present(c)语句?
如果我理解正确,编译器可能知道第一次调用ABC导致路径 B ,而第二次调用 ABC 必须导致路径 ç即可。
我问这个问题,因为我有一个被称为百万次的子程序。
我想避免在运行期间做出决定,以便继续路径B 或路径C 。当然,有两个子程序只能编写两个子程序,但是,这会产生很多额外的行,这些行实际上会做同样的事情。
提前感谢大家的帮助!
答案 0 :(得分:3)
这只是一个冗长的介绍,告诉您特定编译器如何实现语言功能,通常是特定于编译器的。还知道实现细节因同一编译器的版本而异。
我认为你认为在编译时检查是否存在可选参数是错误的,即使可以证明它是可行的 - 我只是不认为那是当前作物的东西编译器。我希望编译器使用可选参数生成子例程的单个实现。毕竟,它确实与程序员一起确保程序不会尝试处理缺少可选参数。
如果你,程序员,知道一个程序将被执行多次,如果你怀疑不同的实现,一个参数b
而没有c
,一个反之亦然< / em>,那么你可以确定单独的实现是否比具有可选参数的一个实现更快。如果您担心代码重复,您可能总是有第三个程序来实现公共代码,并从两个变体中调用它。
同样,您可以检查编译器生成的汇编程序,以查看编译器(版本)对您编写的代码变体的影响。
答案 1 :(得分:3)
我将添加一个例子。这是gfortran-4.8
使用-Ofast
(-fdump-tree-optimized
)对您的代码执行的操作。子例程只有一个副本,if (present())
个调用更改为if (b_1(D) != 0B)
。也许会有一些较小的子程序内联。然后我希望这样的优化可以启动,但不会在这里。
;; Function abc (__contains_abc_MOD_abc, funcdef_no=0, decl_uid=1875, cgraph_uid=0)
abc (real(kind=4) & restrict a, real(kind=4) * b, real(kind=4) * c)
{
struct __st_parameter_dt dt_parm.1;
struct __st_parameter_dt dt_parm.0;
<bb 2>:
if (b_1(D) != 0B)
goto <bb 3>;
else
goto <bb 4>;
<bb 3>:
dt_parm.0.common.filename = &"opt.f90"[1]{lb: 1 sz: 1};
dt_parm.0.common.line = 13;
dt_parm.0.common.flags = 128;
dt_parm.0.common.unit = 6;
_gfortran_st_write (&dt_parm.0);
_gfortran_transfer_character_write (&dt_parm.0, &"Provide c"[1]{lb: 1 sz: 1}, 9);
_gfortran_st_write_done (&dt_parm.0);
dt_parm.0 ={v} {CLOBBER};
goto <bb 6>;
<bb 4>:
if (c_11(D) != 0B)
goto <bb 5>;
else
goto <bb 6>;
<bb 5>:
dt_parm.1.common.filename = &"opt.f90"[1]{lb: 1 sz: 1};
dt_parm.1.common.line = 15;
dt_parm.1.common.flags = 128;
dt_parm.1.common.unit = 6;
_gfortran_st_write (&dt_parm.1);
_gfortran_transfer_character_write (&dt_parm.1, &"Provide b"[1]{lb: 1 sz: 1}, 9);
_gfortran_st_write_done (&dt_parm.1);
dt_parm.1 ={v} {CLOBBER};
<bb 6>:
return;
}
;; Function main (main, funcdef_no=2, decl_uid=1889, cgraph_uid=2) (executed once)
main (integer(kind=4) argc, character(kind=1) * * argv)
{
real(kind=4) aa;
real(kind=4) bb;
real(kind=4) cc;
static integer(kind=4) options.2[7] = {68, 1023, 0, 0, 1, 1, 0};
<bb 2>:
_gfortran_set_args (argc_2(D), argv_3(D));
_gfortran_set_options (7, &options.2[0]);
abc (&aa, &bb, 0B);
abc (&aa, 0B, &cc);
aa ={v} {CLOBBER};
bb ={v} {CLOBBER};
cc ={v} {CLOBBER};
return 0;
}
我检查过这也是最终装配的情况,但在GIMPLE中可以清楚地看到它。