Как оптимизировать Fortran IF-ELSE в цикле DO? - PullRequest
1 голос
/ 13 марта 2019

Функция ниже используется в моей программе чаще всего.

    CONTAINS
     SUBROUTINE Delta4(R,Del)
      REAL(DP), DIMENSION(:), INTENT(IN) :: R
      REAL(DP), DIMENSION(:), INTENT(OUT) ::  Del
      INTEGER(I4B)  :: i, j, r_n, c_n
      REAL(DP) :: ar
      !r_n=size(R,1);
      Del=0.0_dp
      do i=1,4; ar=ABS(R(i))
          if (ar <= 1.0_dp) then
            Del(i)=(3.0_dp-2.0_dp*ar+  &
              sqrt(1.0_dp+4.0_dp*ar-4.0_dp*ar*ar))*0.125_dp
          else !! if (1.0_dp < ar .and. ar <= 2.0_dp)  then
            Del(i)=(5.0_dp-2.0_dp*ar-  &
              sqrt(-7.0_dp+12.0_dp*ar-4.0_dp*ar*ar))*0.125_dp
          end if
      end do

R, Del: длина 4 вектора

Поэтому я хочу улучшить скорость этой функции.
Насколько я знаю, ветвь if-else работает медленно.Кроме того, он находится в цикле do.Как я могу оптимизировать это?

1 Ответ

3 голосов
/ 13 марта 2019

IMO, вы действительно мало что можете получить от этой функции, которая, по сути, является набором арифметических операций.

Вы можете поглотить *0.125_dp в других константах.

ЛучшеВы можете переписать вычисление как (псевдокод)

ar= 1 + 2 * ((ar > 1) - ar)
Del= (2 + ar + sqrt(1 - ar * ar)) * 0.125

Это предполагает неявное преобразование .false. в 0 и .true. в 1, что может не выполняться вашим компилятором.Надеюсь, он скомпилирован как безветвленный.

Поскольку длина вектора равна четырем, вы можете полностью развернуть цикл (но, возможно, компилятор уже делает это).

Моя ставка в том, что выиграл 'не имеет видимого значения.

Чтобы повысить производительность, вы должны рассмотреть код с более глобальной точки зрения и, возможно, рассмотреть возможность распараллеливания.


Обновление:

Как указал @kvantour, я пропустил смену знака.Мы можем исправить с помощью

i= 2 * (ar > 1) - 1
ar= i + 2 * (1 - ar)
Del= (2 + ar + i * sqrt(1 - ar * ar)) * 0.125

В качестве альтернативы,

i= SIGN(1, ar - 1)

, если это окажется эффективным.

...