Как оптимизировать Fortran IF-ELSE в цикле DO?

#performance #if-statement #optimization #fortran #do-loops

#Производительность #if-оператор #оптимизация #fortran #циклы выполнения

Вопрос:

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

     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   amp;
              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-  amp;
              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. Я добавил интересное улучшение.

2. Вы говорите, что ветвление if-else происходит медленно: какое измерение вы сделали? Оптимизация без измерения не имеет смысла!

3. Извините, Франсуа, вы правы. Я думаю, что if-else работает медленно, потому что это условный оператор.

Ответ №1:

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

Вы можете использовать *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)
  

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

Комментарии:

1. Имейте в виду, что в одном из sqrt есть знак минус.

2. @kvantour: ой, да. Исправление.