Комментарии 21
Фортран это всегда круто, как стимпанк и альтернативная история...
Выбросы энергии в ноосферу говорят: кто-то опять применил тёмное искусство некромантии!
То есть у языка ФОРмул до 2024 года не было условных выражений?
Здорово, да :)
Было merge(a, 0.0, a>0.0)
, а теперь в качестве альтернативы добавили привычный по другим языкам синтаксис (a>0.0 ? a : 0.0)
.
Не совсем так, merge – обычная функция, а ? - специальная форма. В первом случае вычисляются значения всех трёх аргументов, поэтому такая, например, штука не пройдёт:
merge(a(i), 0.0, i >= 1)
Если функция a
объявлена как pure
, т.е. не имеет побочных эффектов, то уже в режиме -O1
компилятор gfortran не будет вызывать ее без необходимости:
https://godbolt.org/z/cKcdMrdxP
А иначе, да, стандарт требует вычисления всех аргументов функции.
Но в большинстве случаев gfortran (в отличие от ifort и ifx, как ни парадоксально) очень хорошо оптимизирует merge
.
Имелся в виду массив.
А, тогда тем более будет оптимизировано.
program m
implicit none
real :: a(2) = 0.
integer :: i = 0
print *, merge(a(i), 0.0, i>=1)
end program m
gfortran merge.f90 -omerge -O3 -Wl,-ld_classic -ftree-vectorize -march=native -flto -fcheck=bounds
./merge
At line 8 of file merge.f90
Fortran runtime error: Index '0' of dimension 1 of array 'a' below lower bound of 1
Error termination. Backtrace:
#0 0x10b38aaae
#1 0x10b38b7a5
#2 0x10b38bd15
#3 0x10aa72eb9
#4 0x10aa72ede
#5 0x7ff80bba9385
Ну и вообще нельзя полагаться на оптимизацию в принципиальном вопросе.
Ну так вы же специально указали -fcheck=bounds
. Без него (просто -O3
) у меня gfortran 13.2 выводит
0.00000000E+00
без каких-либо ошибок.
Да и не понимаю я, о чем мы спорим. Я просто привел пример, каким был аналог тернарного условного оператора в предыдущих версиях Fortran.
Без check программа просто тихо лезет в чужую память, что не очень хорошо и не всегда возможно.
Да и не понимаю я, о чем мы спорим. Я просто привел пример, каким был аналог тернарного условного оператора в предыдущих версиях Fortran.
А я уточнил, что это не совсем аналог.
Там по ассемблерному коду видно, что компилятор оптимизирует до просто print *, 0.0
и никуда не лезет. И даже в более сложных случаях, если нет побочных эффектов, merge
заменяется на сравнение и условный переход, т.е. полностью аналогично тернарному условному оператору.
Условная операция, будучи специальной формой, не вычисляет неактивную ветку, это написано в её определении. Любая функция по своей семантике вычисляет все свои входные аргументы, а merge формально является обычной функцией. Иногда оптимизатор может убрать лишнюю работу, но это не гарантированное поведение, и программа не должна от него зависеть. Это важная семантическая разница.
В SICP прямо даётся такой пример. Там бесконечную рекурсию так забабахали, заменив условную специальную форму на условную функцию.
А можно пользователям fortran 77 объяснить о чем речь? И куда делись мои .le. .lt. .eq. и прочие?
Условные выражения для конвейерной обработки массивов - это уже довольно древняя фича Фортрана, ей без малого лет 30, если не 40. ForAll, Where, Sum, и прочие спецфункции для массивов.
Первые в истории программирования и планеты Земля рабочие условные операторы выглядели так:
IF(a) 1, 2, 3
1 ...
GOTO 4
2 ...
GOTO 4
3 ...
GOTO 4
4 ...
IF(b>0) GOTO 666
...
666 STOP 'b>0!'
Если а<0, то переход происходит на метку 1, если a=0, то на метку 2, если a>0 - то на метку 3.
Разумеется, это был условный оператор доисторического ассемблероподобного Фортрана.
Но уже лет через 10 появились обычные ifы.
На этом фоне заявления "C++ умирает" смотрятся особенно забавно. :)
Принят новый стандарт Fortran 2023