Разрешает ли Фортран необязательные аргументы и представляет операторы во время компиляции?

После безрезультатного поиска я хочу задать вам вопрос о подпрограммах, содержащих необязательные аргументы, и о том, как они обрабатываются компилятором (время выполнения / время компиляции). Рассмотрим следующий пример программы.

  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 должен вести к пути C. Я задаю этот вопрос, поскольку у меня есть подпрограмма, которая вызывается миллион-миллион раз.

Я хочу избежать того, чтобы во время выполнения было принято решение идти по пути B или пути C. Конечно, можно было бы написать просто две подпрограммы, однако это привело бы к появлению множества дополнительных строк, которые фактически делали бы то же самое.

Заранее всем спасибо за помощь!


person user3512099    schedule 08.04.2014    source источник
comment
Мне это кажется микрооптимизацией, которая вряд ли сэкономит значительное время работы ЦП и не принесет компромисса в менее обслуживаемый код. Просто догадываюсь. Вы профилировали свой код, чтобы узнать, где потребление ЦП?   -  person M. S. B.    schedule 08.04.2014
comment
Я еще не профилировал код, однако, возможно, вы правы в своем предположении. Просто думала заранее спросить…   -  person user3512099    schedule 09.04.2014


Ответы (2)


Стандарты Fortran умалчивают практически обо всех аспектах реализации языка, кроме синтаксиса и семантики. Например, люди часто думают, что Фортран передает аргументы по ссылке, но стандарт требует только, чтобы программы вели себя так, как будто аргументы передаются по ссылке. Разработчики могут свободно использовать переход по пикселям, если они хотят, и если они могут таким образом произвести убедительную симуляцию передачи по ссылке.

Это просто длинное введение, чтобы рассказать вам, что то, как конкретный компилятор реализует языковую функцию, в целом зависит от компилятора. Также известно, что детали реализации варьируются от версии к версии одного и того же компилятора.

Я думаю, что вы ошибаетесь, полагая, что наличие необязательного аргумента будет проверяться во время компиляции, даже если доказано, что это могло быть - я просто не думаю, что это то, что делают нынешние компиляторы. . Я ожидаю, что компилятор сгенерирует единственную реализацию подпрограммы с необязательными аргументами. В конце концов, это зависит от программиста, чтобы гарантировать, что процедура не пытается обработать отсутствующие необязательные аргументы.

Если вы, программист, знаете, что процедура будет выполняться много раз, и если вы подозреваете, что разные реализации, одна с аргументом b и без c, другая наоборот, тогда вам решать, если отдельные реализации будут быстрее, чем одна реализация с необязательными аргументами. Если вас беспокоит дублирование кода, вы всегда можете иметь еще третью процедуру для реализации общего кода и вызывать ее из обоих вариантов.

Точно так же вы должны проверить ассемблер, созданный вашим компилятором, чтобы увидеть, что ваш компилятор (версия) делает с вариациями в коде, который вы пишете.

person High Performance Mark    schedule 08.04.2014
comment
Большое спасибо за это объяснение. Ваш комментарий и особенно пример кода очень помогли мне понять, как обрабатываются текущие и необязательные операторы. Вы правы в том, что я должен принять правильное решение, более того, я подумаю о «третьей процедуре», которая могла бы решить проблему. - person user3512099; 09.04.2014

Добавлю пример. Это то, что 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, это заметно более четко.

person Vladimir F    schedule 08.04.2014