Fortran是否在编译时解析可选参数并显示语句?

时间:2014-04-08 17:56:48

标签: runtime fortran fortran90 compile-time optional

在没有任何结果的搜索之后,我想问你一个关于包含可选参数的子例程以及编译器如何处理它们的问题(运行时/编译时)。请考虑以下示例程序。

  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 。当然,有两个子程序只能编写两个子程序,但是,这会产生很多额外的行,这些行实际上会做同样的事情。

提前感谢大家的帮助!

2 个答案:

答案 0 :(得分:3)

除了语法和语义之外,Fortran标准对语言实现的几乎每个方面都保持沉默。例如,人们通常认为Fortran通过引用传递参数,但标准只要求程序的行为就像参数通过引用传递一样。如果他们愿意,实施者可以自由地使用 pass-by-pixies ,如果他们可以通过引用产生令人信服的模拟传递。

这只是一个冗长的介绍,告诉您特定编译器如何实现语言功能,通常是特定于编译器的。还知道实现细节因同一编译器的版本而异。

我认为你认为在编译时检查是否存在可选参数是错误的,即使可以证明它是可行的 - 我只是不认为那是当前作物的东西编译器。我希望编译器使用可选参数生成子例程的单个实现。毕竟,它确实与程序员一起确保程序不会尝试处理缺少可选参数。

如果你,程序员,知道一个程序将被执行多次,如果你怀疑不同的实现,一个参数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中可以清楚地看到它。