Abstract. Чуть-чуть ностальгии, лирики и теории + занудные практические инструкции: что выбрать, с чем развернуть, где настроить и как заставить себя в XXI веке писать на Fortran
.
Contents
Introduction
Инженер на любом языке программирования программирует на Fortran.
Это шутка, но, как обычно, только отчасти. :)
Fortran
– старейший язык высокого уровня, задуманный изначально как FORmula TRANslator для ученых и инженеров. Fortran
создавался в IBM начиная с 1954 года суровым чуваком Джоном Бэкусом.
Формально первым языком высокого уровня считается Планкалкюль (1945), компилятор которого, правда, был создан только в 2000. Fortran
в 1950-х был не просто изобретен (точнее – «интуитивно нащупан»), но сразу реализован. Он – первый «по-честноку».
Благодарные ученые и инженеры активно используют Fortran
до сих пор, несмотря на очевидную его моральную устарелость. (Сейчас XXI век, четвертая пятилетка). Мы тоже не исключение. На Fortran
меня подсадили в 2009 году седовласые профессора кафедры «Прикладная математика» петербургского Политеха, с которыми мы вместе создавали одну нетривиальную загогулину. С тех пор слезть с него не удается никак.
В рейтинге популярности языков программирования Fortran
находится на 28 месте, между F#
и Lua
. Современные мейнстримные программисты заливаются хохотом, когда им говоришь про Fortran
. (Боюсь, они представляют его весьма условно, в стандарте приблизительно 77). А зря. Среди куда более узкой аудитории – ученых и инженеров Fortran
занимает если уже и не первое, то точно одно из ведущих мест.
Причин продолжения использования Fortran
несколько:
Огромная, накопленная за пол века база инженерно-научного кода.
Скорость. Программы на
Fortran
– очень быстрые. Компиляторы Fortran чрезвычайно эффективны, а используемые математические библиотеки алгоритмически отточены как лезвия бритв. Кроме того, современныйFortran
очень эффективно позволяет распараллеливать вычисления.Удобство работы с матрицами и вообще с математикой. В Си-подобных языках этого можно добиться только с помощью ряда библиотек, здесь – все «из коробки».
Он простой как топор и императивный как танк. Инженеру/ученому не надо забивать голову программистскими премудростями (объектно-ориентированными или функциональными парадигмами, либо особенностями распределения памяти, указателями и т.д., и т.п.). Можно просто писать свои сладкие формулы.
Он компилируемый. Программы очень просто кому-нибудь отдавать безо всяких ваших виртуальных машин.
Как ни странно, он развивается и поддерживается. Стандарты регулярно обновляются (последний – 2015), компиляторы – совершенствуются.
На самом деле, Fortran
действительно хорош для задач вычислительной математики, требующих очень высокой скорости исполнения кода с одной стороны, а с другой стороны – не слишком сложных в плане организации данных. Большинство околонаучных задач таковы. Однако, если данных все-таки много (не по объему, а по запутанности), а проект – сложен, то Fortran
с удовольствием поспособствует его превращению в летающего макаронного монстра.
Если высокая скорость не требуется, лучше взять более современный язык «сверхвысокого» уровня: Python
с библиотеками NumPy
и SciPy
или специализированный математический язык Mathematica
или MatLAB
(или свободные аналоги; для последнего – Octave
или SciLAB
). На них разработка «научного» проекта будет в разы быстрей и прозрачней, правда все будет работать чертовки по-черепашьи.
Часто проекты так и приходится создавать в два этапа:
Прототипирование на «высоком и прозрачном» языке типа
Python
.Реализация на быстром
Fortran
(илиС/С++
).
В последние годы научным сообществом активно разрабатывается новый современный язык Julia
, претендующий на замену Fortran
. Он позволяет писать как «прозрачные» прототипы, так и быструю реализацию критичных участков. Julia
, родившаяся в 2012, – многообещающий и очень интересный язык, который лично я изучать пока только начал. О нем еще будут посты.
Компиляторы Fortran
Живых компиляторов Fortran
сегодня, как ни странно, не меньше десятка.
Лучший активно развивающийся коммерческий компилятор для Windows и Linux – вероятно, Intel Fortran
. С ним «из коробки» идет мощнейшая математическая библиотека MKL
, включающая известные пакеты линейной алгебры BLAS
и LAPACK
, а также супер-эффективный пакет для работы с разряженными матрицами PARDISO
и прочие вкусности. (За дополнительные деньги можно приобрести дополнительные или альтернативные библиотеки, такие как знаменитая IMSL
). С Intel Fortran
поставляется очень мощный профайлер, позволяющий смотреть, на каких участках кода как тратится время (и другие параметры) и оптимизировать код. В Windows Intel Fortran
ставится на MS Visual Studio
и работает с линкером от Майкрософт.
Вторым лидером рынка вроде бы считается PGI Fortran
(для Linux and Mac доступен во Free edition) – тоже монстр, на который, я правда особо и не смотрел. Еще есть Absoft Pro Fortran
, IBM XL Fortran
(для Linux и AIX), EKOPath
(Linux), Lahey/Fujitsu Fortran
, NAG Fortran
, Oracle Solaris Studio
(для Solaris и Linux, кажется недавно стала бесплатной) и другие.
Довольно интересный коммерческий компилятор – Silverfrost FTN95
, бесплатный для персонального использования или «в целях оценки». Единственное неудобство – каждый раз при запуске скомпилированной программы появляется баннер. С FTN95
поставляется приятная легкая Plato IDE
. Вообще, вся система производит впечатление очень легкой и быстрой.
Лучший свободный компилятор – GFortran
из набора компиляторов GCC
, разрабатываемых в рамках проекта GNU. GFortran
активно совершенствуется «свободным сообществом».
Еще неплохой свободный компилятор был G95
, но похоже загнулся в 2013.
Итого, наш выбор – GFortran
, поэтому о нем – поподробнее.
Установка GFortran в Windows
На январь 2017 последняя стабильная версия GCC
с GFortran
– 6.3, заканчивается работа над седьмой версией.
GFortran
– разумеется, кроссплатформенный. Для Windows его можно получить и установить разными способами:
Из родного «набора свободных средств разработки под Windows для настоящих минималистов» :) – проекта
MinGW
или отпочковавшегося от него проектаMinGW-w64
, реализующего преимущества 64-битной архитектуры. Или из проектаCygWin
. Все три варианта – не самые простые пути. Что надо скачать и как это настроить – все довольно запутанно.Поставить
GFortran
вместе с хорошей свободной кроссплатформенной IDECode::Blocs
. Скачиваем с их сайта и устанавливаем файлcodeblocks-16.01mingw_fortran-setup.exe
(версия 16.01 на сегодня последняя). Возможно, это наиболее простой путь. Недостаток –GFortran
будет несколько устаревшей (текущая версия в сборке 4.9.2) и только в разрядности 32. Зато поставил – и можно сразу переходить к сладким формулам в интуитивно понятном окружении IDE.От ребят, именующих себя
Equation Solution
, в лице китайца Джен-Чинга Ляо (www.equation.com). Это бывший профессор колумбийского университета, еженедельно собирающий самую актуальную версию дистрибутиваGCC
для разрядности как 32, так и 64. Сборка дополнена отладчиком и собственными библиотеками профессора Ляо, из которых особый интерес для нас представляютLAIPE2
(решение СЛАУ с распараллеливанием) иJCL
(перенумерация строк и столбцов разряженных матриц для приведения их к ленточной форме). Это хорошие библиотеки, оказавшиеся для меня очень кстати. Из дистрибутива профессора ЛяоGCC
устанавливается очень легко через инсталлятор. Необходимые пути автоматом прописываются вPATH
. У Ляо в принципе, все хорошо. С его дистрибутивами я жил какой-то время, единственное, там немножко все «собрано на коленке». Например, в комплекте нетdll
(предполагается только статическая линковка библиотекgcc
), получаемыеexe
в последних версиях стали странным образом разбухать, его собственные библиотеки – иногда глючат и т.п. :) Как обычно, Open Source – такой Open Source. :)Из специальной сборки компиляторов
GCC
для Windows с сайта TDM-GCC. Это очень хорошие и продуманные сборки, включающие полный набор нужных средств. К сожалению, здесьGCC
тоже несколько устаревший (текущая версия 5.1), но сейчас этот вариант – мой фаворит. Скачиваем с их странички файлtdm64-gcc-5.1.0-2.exe
для архитектуры 64 илиtdm-gcc-5.1.0-3.exe
для 32. На версии 64 можно (в том числе) компилировать свои программы и для 32-разрядных машин, т.е. две версии ставить не нужно. Устанавливается все элементарно через инсталлятор. Единственное, ему надо указать «Устанавливать все пакеты», т.к. по умолчанию компилятор фортран как «экзотика» пропускается. Инсталлятор прописывает путь к компиляторам вPATH
.
Последний вариант, TDM-GCC
, – очень хороший, за одним «но». В версиях 5.3 (и 4.9) допущен досадный баг, из-за которого не работает оператор фортрана Open
. :) (Open Source – такой Open Source). Об этом не написал в баг-листе только ленивый. Надо скачать с их сайта старую версию 4.8 (там надо найти gcc-4.8.1-tdm64-2-fortran.zip
) и заменить из нее файлы libgfortran_64-3.dll
и libgfortran-3.dll
, а также libgfortran.a
, libgfortran.dll.a
, libgfortran.spec
, libgfortranbegin.a
. Причем последние файлы имеются в 64 и 32-разрядных версиях, их надо аккуратно разложить по соответствующим папкам lib
или lib32
.
То же относится и к текущей версии 4.9 GFortran
в CodeBlocs
(он по сути там тот же самый из TDM-GCC
).
Надеюсь, это исправят. В остальном все работает как часы.
В дистрибутивах от Ляо этой проблемы нет.
Компиляция в GFortran
Из IDE
IDE для того и нужны, чтобы не думать, а нажимать кнопочки. :) Все интуитивно понятно без дополнительных пояснений.
Из командной строки
Допустим, мы хотим запустить «чистый компилятор», не пользуясь IDE. Пишем программу в текстовом файле, например Hello.f08
:
program Hello
print *, "Здавствуй, мир! Hello, Space Boy!"
call execute_command_line("Pause")
endprogram
Компилируем из командной строки:
gfortran Hello.f08 -o Hello.exe
Если пути в PATH
«подхватились», после этого получается файл Hello.exe
, запуск которого приведет к выводу приветствия на консоль (или «кракозябров» из-за несовпадения кодировки). Если пути в PATH
не прописаны, перед gfortran
надо указать к нему путь – все точно также будет работать.
Опция -o
задает выходной файл.
Чтобы статически сгенерировать exe
, не завязанный ни на какие внешние .dll
-библиотеки, нужно добавить опцию -static
:
gfortran Hello.f08 -static -o Hello.exe
О других опциях – см. ниже.
Далее. Допустим, проект состоит из нескольких модулей. Пусть, например, это три файла: Module1.f08
, Module2.f08
и MainProgram.f08
с соответствующими текстами:
module Module1
contains
function Square(x) ! Вычисление квадрата x
real :: x, Square
Square = x**2
endfunction
endmodule
module Module2
use Module1
contains
subroutine PrintSquare(y) ! Печать квадрата y
real :: y
print *, "Квадрат числа:", Square(y)
endsubroutine
endmodule
program MainProgram ! Супер-программа «Нахождение квадрата числа»
use Module1 ; use Module2
real :: z = 3. ! Наше число
call PrintSquare(z)
print *, "Проверка:", Square(z)
call execute_command_line("Pause")
endprogram
Пример составлен так, чтобы смоделировать зависимости модулей друг от друга, показанные на схеме:
Компилируется такой проект, чтобы не нарушался порядок зависимостей, следующим образом:
gfortran -c Module1.f08
gfortran -c Module2.f08
gfortran -c MainProgram.f08
gfortran Module1.o Module2.o MainProgram.o -static -o MyProgram.exe
Сначала исходные файлы компилируются в объектные .o
(опция -c
), а затем они линкуются в результирующий .exe
.
Компиляция с GNU Make
Работать с командной строкой, когда фалов проекта много, не слишком удобно (даже если все прописывать в *.bat
или *.cmd
). Задачу автоматизации сборки берет на себя IDE
и/или олдскульная технология составления Make
-файлов, которые можно легко вызывать на исполнение, например, из любимого текстового редактора. Преимущество Make
, в частности, в том, что каждый раз будут перекомпилироваться не все файлы проекта, а только те, которые изменились.
Если фортран был поставлен из дистрибутива TDM-GCC
или CodeBlocs
, утилита Make
находится в папке bin
под именем mingw32-make.exe
. Для простоты скопируем (продублируем) ее под именем просто make.exe
. В дистрибутиве от Ляо она сразу называется make.exe
.
Если на машине инсталлировано несколько сред программирования, могут стоять и различные мэйки, которые также могут вызываться по команде make
не совсем к месту. Убедимся, что так вызывается именно GNU Make, набрав make -v
. Это должно выдавать приблизительно следующее:
GNU Make 3.82.90
Built for i686-pc-mingw32
Copyright (C) 1988-2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
Инструкции компиляции пишутся в текстовом файле, который обычно называется Makefile
(без расширения) и располагается в папке с исходниками.
Make
-файл состоит из правил и переменных. Правила имеют следующий синтаксис:
цель1 цель2 ...: реквизит1 реквизит2 ...
команда1
команда2
...
Правило представляет собой набор команд, выполнение которых приведет к сборке файлов-целей из файлов-реквизитов. Правило сообщает make
, что файлы, получаемые в результате работы команд (цели), являются зависимыми от соответствующих файлов-реквизитов. Make
никак не проверяет и не использует содержимое файлов-реквизитов, однако, указание списка файлов-реквизитов требуется только для того, чтобы Make
убедился в наличии этих файлов перед началом выполнения команд и для отслеживания зависимостей между файлами.
NB! Строки, в которых записаны команды, должны начинаться с символа табуляции. Пробелы здесь ставить нельзя!!!
Простейший Makefile
для программы из одного модуля Hello.f08
(наш первый пример) может выглядеть так:
PRG_NAME = Hello
all:
gfortran $(PRG_NAME).f08 -o $(PRG_NAME).exe
$(PRG_NAME).exe
clean:
rm -rf *.o *.mod $(PRG_NAME).exe
Здесь вначале определена переменная PRG_NAME
, значение которой затем «дословно» подставляется в команды – везде, где написано $(PRG_NAME)
.
Далее определены две цели: all
и clean
, без реквизитов, но зато с командами.
Если из рабочего каталога вызвать
make all
то мейк отработает первую цель, а именно – скомпилирует и запустит нашу программу. При этом файл Makefile
подхватится по умолчанию. Первую цель, определенную в файле, можно вызывать еще проще, написав просто
make
Можно также указать требуемый мейк-файл явно, тогда он может называться как нам угодно:
make -f MyMakeFile all
Запуск Make
с другой целью, clean
, сотрет все промежуточные файлы проекта:
make clean
Теперь составим чуть более сложный Makefile
для нашего второго примера с тремя модулями. При этом предусмотрим возможность управления вариантами компиляции с разными опциями, путем их комментирования/раскомментирования. Пусть также exe
помещается в отдельную папку bin
, которая должна лежать рядом с папкой для исходного кода.
# 1. Флаги компилятора, меняемые оперативно (отладка, оптимизация):
# Быстрая компиляция без лишних вопросов:
# MY_FLAGS = -w -O0
# Компиляция для отладки с проверками времени выполнения и предупреждениями:
MY_FLAGS = -Og -Wall -Wno-tabs -fcheck=all
# Компиляция для релиза с оптимизацией:
# MY_FLAGS = -w -Ofast -mfpmath=sse -ftree-vectorize -funroll-all-loops
# 2. Флаги компилятора - общие для всех модулей и вариантов компиляции:
FLAGS = -ffree-line-length-512 -freal-4-real-8
# 3. Название программы и исходных файлов (без расш.) в порядке компиляции:
PRG_NAME = ..\\bin\\MyProgram
F1 = Module1
F2 = Module2
F3 = MainProgram
# 4. Компилятор фортран (здесь можно прописать путь):
CF = gfortran
# 5. Главная цель - сформировать программу и потом ее запустить:
all: $(PRG_NAME)
$(PRG_NAME).exe
# 6. Для этого нужно достичь другой цели - слинковать объектные файлы в exe:
$(PRG_NAME): $(F1).o $(F2).o $(F3).o
$(CF) $^ $(FLAGS) $(MY_FLAGS) -o $@.exe
# 7. А для этого нужно скомпилировать каждый файл из исходников:
$(F1).o: $(F1).f08
$(CF) -c $(FLAGS) $(MY_FLAGS) $^
$(F2).o: $(F2).f08
$(CF) -c $(FLAGS) $(MY_FLAGS) $^
$(F3).o: $(F3).f08
$(CF) -c $(FLAGS) $(MY_FLAGS) $^
# 8. Альтернативная цель - очистка проекта от промежуточных файлов:
clean:
rm -rf *.o *.mod $(PRG_NAME).exe
Возможно, это не лучший стиль написания Make
, но для меня он «родной» и наглядный.
В главной цели all
перед командой запуска файла мы указали реквизит, одноименный с названием программы:
all: $(PRG_NAME)
$(PRG_NAME).exe
Make
– как хороший солдат, любой ценой (но желательно наименьшей), должен выполнить поставленную перед ним цель. А заключается она в исполнении команд, при этом обязательным условием перед их исполнением является наличие реквизита. В качестве реквизита основной цели мы указали необходимость достижения «дочерней» цели, правило для которой описали так:
$(PRG_NAME): $(F1).o $(F2).o $(F3).o
$(CF) $^ $(FLAGS) $(MY_FLAGS) -o $@.exe
Оно требует наличия еще трех реквизитов – готовых объектных файлов $(F1).o $(F2).o $(F3).o
. Если они имеются, должна быть выполнена команда по их линковке в exe
.
Выражение $@
означает «подставить сюда текущую цель», а $^
– «подставить сюда текущий реквизит».
Цели, связанные с данными реквизитами, мы описали так:
$(F1).o: $(F1).f08
$(CF) -c $(FLAGS) $(MY_FLAGS) $^
Это значит, что для достижения очередной цели надо, чтобы присутствовал файл $(F1).f08
, после чего его надо скомпилировать в объектный файл.
Make
, как любой нормальный боец, чрезвычайно ленив. Если какая-то цель была уже выполнена, он не будет рваться достигать ее еще раз. Если у него уже есть скомпилированный объектный файл, он его компилировать повторно не будет. Но он – все же очень исполнительный и прилежный боец. Если файл исходника был изменен позже объектного файла, Make
старательно это исправит, перекомпилировав объектный файл снова.
Здесь находится перевод официальной справки по GNU Make.
Здесь – хороший материал «Эффективное использование GNU Make» от Владимира Игнатова.
Некоторые опции компилятора
При компиляции и линковке GFortran
’у можно указать огромную кучу опций – все они описаны в справках по GFortran
и GСС
. Перечислю здесь некоторые «типовые» (используемые мной в практике):
Опция | Пояснение |
---|---|
-static | Указывается при линковке. Собирает весь код зависимых библиотек в один exe -файл. Его можно будет отдавать и переносить без дополнительных dll . |
-shared-libgcc | Наоборот, dll от GCC – отлинковать динамически. exe -файл будет маленький, но без установленных в доступности PATH нескольких «родных» dll ничего работать не будет. |
-m32 | Если установлен 64-разрядный GFortran , по умолчанию проект компилируется как 64-разрядное приложение, этой опцией можно попросить сделать версию для 32-разрядных машин. |
-O0 , -O1 , -O2 или -O3 или -Ofast | Уровни оптимизации, кода. С -O0 сама компиляция будет быстрой (удобно при разработке), но код – медленный. -O3 или -Ofast – для релиза. -Og – оптимизация для отладки. |
-mfpmath=sse -ftree-vectorize -funroll-all-loops | Дополнительные оптимизации для релиза (но без фанатизма, для современных процессоров можно навернуть больше). |
-fdollar-ok -llaipe2 -lneuloop4 -ljcl | Опции для подключения библиотек от Ляо. Первая разрешает доллар в именах функций как у него принято, остальные просто подключают библиотеки laipe2, neuloop4, jcl . |
-fopenmp | Подключает библиотеку OpenMP для параллельных вычислений. |
-fimplicit-none | Запрещает неявное определение типов по первой букве у переменных (i – integer и т.д.). Эквивалентно указанию в каждой процедуре implicit none , где их можно забыть и поиметь проблемы с переменными, которые забыли определить явно. |
-ffree-line-length-512 | Позволяет писать длинные строки в коде (с современными мониторами трудно умещать код в 72 символа, считающиеся ранее максимальной шириной кода). |
-freal-4-real-8 | Считать все вещественные числа типа real(4) , которые в фортране по-умолчанию можно задавать как «просто» real , числами с двойной точностью real(8) (8 байт). На мой взгляд, для большинства задач сейчас лучше использовать 8 байт, а с этой опцией будет меньше визуального мусора типа real(8) :: r = 1.0_8 , будет просто real :: r = 1.0 . |
-w | Подавлять все синтактические предупреждения |
-Wall -Wno-tabs -fcheck=all | Наоборот, выводить все предупреждения (кроме абсурдных), а также выполнять все проверки времени выполнения (выходы за пределы индексов в массивах и т.д., и т.п.) |
-ggdb | Генерировать информацию для отладчика gdb |
-pg | Генерировать информацию для профайлера gprof |
О расширениях исходных файлов
Расширение файлов по традиции отражает стандарт языка: в наших примерах оно f08
– стандарт 2008. Это дань традиции: компилятор без доп. опций понимает f08
, f03
, f95
, f90
одинаково, как файлы в свободном формате. Для старого фиксированного формата используются расширения f
, for
и др.
Если расширение начинается с большой буквы (F08
, F95
и т.п.), а также если оно fpp
, то файл будет сначала пропускаться через предпроцессор – в нем можно будет писать директивы компилятору с синтаксисом gcc
-Си, например:
#define Compile_R16
! ...
#if defined Compile_R16
real*16 :: MyReal
#else
real*8 :: MyReal
#endif
Подключение математических библиотек к GFortran
BLAS и LAPACK
BLAS
и LAPACK
– классические библиотеки линейной алгебры, которые не совсем очевидно подключаются к GFortran
в Windows
. Здесь описан процесс подключения, если вы работаете c Visual Studio
с линкером от Майкрософт, который подключает библиотеки *.dll
, используя *.lib
. Родной линкер GFortran
использует библиотеки в архивированном формате *.a
.
Как подключить BLAS
и LAPACK
к GFortran
в Windows
:
Скачиваем с официального сайта архив
lapack-3.7.0.tgz
. (На сегодня последняя версия 3.7.0). Распаковываем архив в папкуlapack-3.7.0
.В подпапке
INSTALL
находим файлmake.inc.gfortran
, копируем его в корневую папку (lapack-3.7.0
) и переименовываем его в простоmake.inc
.Идем в каталог
\BLAS\SRC
(например, в файловом менеджере FAR) и набираем в нем командуmake
(GNU make
должен быть подключен, см. выше). После компиляции ряда файлов в корневой папке (lapack-3.7.0
) должен появиться файлlibrefblas.a
.Аналогично, идем в каталог
\SRC
и набираем в нем командуmake all
. После компиляции ряда файлов (довольно долгой) в корне должен появиться файлliblapack.a
.Переписываем файлы
librefblas.a
иliblapack.a
туда, где GCC хранит свои библиотеки. В зависимости от установленного дистрибутива это папки вроде\i686-pc-mingw32\lib
,\x86_64-w64-mingw32\lib
или\MinGW\lib
(в CodeBlocks).Если мы работаем с дистрибутивом разрядности 64 (например от TDM-GCC) и хотим по опции
-m32
уметь компилировать наши программы для 32-разрядных машин, весь процесс придется повторить, скомпилировавlibrefblas.a
иliblapack.a
с опцией-m32
(придется дописать ее в make-файл), а потом положить 32-разрядные версии библиотек в соответствующую папку (у TDM-GCC это\x86_64-w64-mingw32\lib32
).Все!
Протестируем. Решим систему линейных алгебраических уравнений (СЛАУ)
\[ \mathbf{A}\cdot\mathbf{X}=\mathbf{B}, \]
где \(\mathbf{A}\) – матрица коэффициентов, \(\mathbf{X}\) – вектор неизвестных и \(\mathbf{B}\) – вектор правых частей.
Пусть матрица \(\mathbf{A}\) – плотная, общего вида. Для СЛАУ с такими матрицами в подойдет LAPACK
-процедура dgesv
. Первая буква «d
» означает вариант для вещественных чисел двойной точности, real(8)
. Пусть \(\mathbf{A}\) имеет размер \(10 \times 10\) с заполнением случайными числами от \(0\) до \(100\), а вектор \(\mathbf{B}\) заполнен числами \(100 \dots 1000\).
Наберем в файле test.f08
:
program LAPACK_test
implicit none
integer, parameter :: n = 10 ! Размерность матриц
real(8), dimension(n,n) :: A ! Матрица коэффициентов
real(8), dimension(n,1) :: B, X ! Векторы правых частей / результатов
integer :: i ! Вспомог. индекс
integer, dimension(n) :: ipiv ! Вспомог. матрица для процедуры LAPACK
integer :: info ! Инфо об успешности решения
character(100) :: FRM ! Формат для вывода матриц
!----------------------------------------------------------------------------
print *, "Проверка LAPACK. Решение СЛАУ А*X=B с плотной матрицей общего вида"
call random_number(A); A = 100*A ! Заполняем матрицу А
forall(i = 1:n) B(i,1) = 100*i ! Заполняем вектор B
write(FRM, "(a,i10,a)") "(",n,"f8.2)" ! Формируем формат для вывода матриц
print *, "Матрица коэффициентов A:"
print FRM, A
print *, "Вектор правых частей B:"
print FRM, B
X = B
call dgesv(n, 1, A, n, ipiv, X, n, info) ! Вызов LAPACK для решения СЛАУ
print *, "Решение:"
print FRM, X
print *, "Успешность решения (флаг «info»):", info
endprogram
Компилируем, подключая библиотеки lapack
/ refblas
:
gfortran test.f08 -llapack -lrefblas -static -o test.exe
Получаем:
Проверка LAPACK. Решение СЛАУ А*X=B с плотной матрицей общего вида
Матрица коэффициентов A:
99.76 56.68 96.59 74.79 36.74 48.06 7.38 0.54 34.71 34.22
21.80 13.32 90.05 38.68 44.55 66.19 1.61 65.09 64.64 32.30
85.57 40.13 20.69 96.85 59.84 67.30 45.69 33.00 10.04 75.55
60.57 71.90 89.73 65.82 15.07 61.23 97.87 99.91 25.68 55.09
65.90 55.40 97.78 90.19 65.79 72.89 40.25 92.86 14.78 67.45
76.96 33.93 11.58 61.44 82.06 94.71 73.11 49.76 37.48 42.15
55.29 99.79 99.04 74.63 95.38 9.33 73.40 75.18 94.68 70.62
81.38 55.86 6.17 48.04 59.77 13.75 58.74 52.00 88.59 30.38
66.97 66.49 50.37 26.16 7.66 10.12 54.93 37.56 1.51 79.29
62.09 77.36 95.36 11.42 31.85 59.68 4.82 11.42 21.60 10.06
Вектор правых частей B:
100.00 200.00 300.00 400.00 500.00 600.00 700.00 800.00 900.00 1000.00
Решение:
-8.79 16.14 12.52 3.00 -14.42 -0.18 4.31 -2.40 5.41 -1.10
Библиотеки дядюшки Ляо
В дистрибутиве GCC
профессора Ляо (с сайта www.equation.com) имеются очень неплохие библиотеки:
NEOLOOP
– для параллельных вычислений;LAIPE2
– решение СЛАУ с матрицами коэффициентов различного вида, включая симметричные положительно определенные ленточные или просто разряженные, представляющие для нас особый интерес (почему – об этом потом). Решение выполняется с распараллеливанием процессов.JCL
– перенумерация строк и столбцов разряженных матриц для приведения их к ленточной форме.
Если нужно установить эти библиотеки в другие дистрибутивы, делаем следующее:
Находим в дистрибутиве от Ляо файлы библиотек:
libneuloop4.a
,libneuloop6.a
,liblaipe2.a
,libjcl.a
.Переписываем их в место, где хранит основные библиотеки
.а
ваш фортран.
Как обычно, если мы работаем с 64-битной версией GCC
с желанием сохранить возможность компиляции программ под 32, это надо сделать для обеих версий библиотек, положив их в lib
и lib32
.
Компиляция программ с этими библиотеками выполняется с опциями:
-fdollar-ok -llaipe2 -lneuloop4 -ljcl
(Первая разрешает доллар в именах функций как у него принято, остальные просто подключают библиотеки laipe2, neuloop4, jcl
).
Чтобы подпрограммы LAIPE2
заработали, надо предварительно вызвать процедуру laipe$use
с указанием числа ядер процессора, которые вы хотите использовать (иначе не «заведется» – об этом Ляо забыл написать в справке).
Библиотеки NEOLOOP
и LAIPE2
работают только на Windows 7 и более старших.
IDE и редакторы для GFortran
Для серьезной работы с языком хорошо иметь как IDE, со всем богатством возможностей, так и текстовый редактор (может быть, не один), позволяющий «что-нибудь сварганить» по-быстрому или подправить. Честно говоря, я IDE почему-то запускаю реже текстового редактора, с которым намного лучше постигается Дзен.
Какие есть варианты для GFortran
в Windows?
IDE Code::Blocks
Это самый простой вариант начать работать в Fortran
, пользуясь свободным ПО. Все ставится из коробки и заводится с пол-оборота. По сравнению с альтернативными IDE – относительно легкая. Тут есть все что нужно: автодополнение кода, подсказки, навигация по объектам программы, отладчик, профайлер, минимальные средства рефакторинга.
Когда-то я «щупал» ifort
+ Visual Studio
, там таких удобных подсказок и умного автодополнения и близко не было (это не претензия к студии – в других языках все очень круто, но не в Intel Fortan
). Т.е. в определенном смысле CodeBlocks
– даже лучше. (Может конечно я отстал от жизни, последних версий ifort
+ VS
не видел).
На 64-разрядных машинах желательно поставить 64-разрядный компилятор GFortran
(см. выше) и подключить его в качестве основного (все делается мышкой в настройках элементарно).
Вывод сообщений ваших программ не в консоль, в окно редактора (чтобы не было проблем с кодировками) можно сделать через меню Tools, создав новый инструмент, указав для него:
Executable: ${TARGET_OUTPUT_BASENAME}
Working directory: ${TARGET_OUTPUT_DIR}
Launch tool hidden with standart output redirect
IDE Eclipse с расширением Photran
Eclipse
– знаменитая мощнейшая IDE для множества языков программирования. Для Fortran
есть расширение Photran
, работающее в том числе c GCC
. Доступно здесь.
Eclipse
– очень тяжелая штука. Я побаловался, но дальше она как-то не прижилась.
Прочие IDE
Еще можно посмотреть в сторону легонькой Geany
.
Не уверен, что своими силами можно толком настроить GFortran
c Visual Studio
. Есть коммерческий пакет, где это сделано – Lahey/Fujitsu Fortran
. Это не наш путь.
Текстовые редакторы
Не могу отделаться от ассоциации программистских текстовых редакторов с людьми разных возрастных групп. :)
Emacs и Vim
Седовласые старики. Очень опытные, мудрые и крутые, но найти с ними общий язык – очень сложно. Я пока не сумел, хотя пару десятков попыток делал. :)
Sublime Text, Atom, VS Code
Энергичные мужики. Моложавые, хипстерской внешности. На этих редакторах получается сделать «почти IDE».
Atom
– открытый, мощный, но жирный и неторопливый. Использую его для некоторых других языков, Фортран как-то на нем не прижился.
Sublime Text
– мой выбор. Мощный, быстрый, правда не открытый и не бесплатный (70 долларов, хотя незарегистрированной версией можно пользоваться сколько угодно).
VS Code
– удачная попытка Microsoft написать бесплатный Sublime
. Очень приятный. Все намереваюсь на него перейти, но руки пока не доходят.
SciTE, NotePad++, AkelPad
Эдакие продвинутые юнцы. Меленькие, легкие, дерзкие, но далеко не такие мощные, как товарищи из первых двух групп. Не «почти IDE».
Раньше я очень любил SciTE
, последнее время перешел в AkelPad
. Он крошечный, работает со сверхзвуковой скоростью. Очень просто настраивается подсветка. Все, чего только только можно желать, реализуется на скриптах на простом человеческом JavaScript
.
С GFortran
нетрудно настроить любой редактор. Привожу некоторые скриншоты работы приведенной выше программы по тестированию правильности установки LAPACK.
Литература по Fortran
Лучшая книга на русском языке – О. В. Бартеньев «Современный Фортран». Она настолько полная и и исчерпывающая, что книги остальных авторов – не нужны. Хорош также его же трехтомник по библиотеке IMSL
. Даже если вы не применяете IMSL
, там много полезной теории. (На самом деле книги Бартеньева – по сути качественный перевод полной документации по Фортрану и IMSL
и от минимума «отсебятины» они только лучше). Жаль только, что его книги несколько устарели, но это совсем не критично – Фортран развивается не так быстро.
Здесь – GFortran Wiki.
Здесь – PDF-справка по GFortran. Очень неплоха. Коротко и по делу. Для работы нужен второй PDF по GCC в целом.
Здесь – очень хорошее краткое описание языка в стандарте 95 в википедии. Если вы пишете на других языках, прочитав это, станете гуру в Фортране за каких-нибудь пару часов. :)
Здесь, в открытом доступе, – прекрасная справка по Intel Fortran
, в основной части (в части самого языка) подходящая и для GFortran.
Здесь, аналогично, – прекрасная документация по Intel MKL
, в основной части подходящая для BLAS и LAPACK.
Что прекрасно в Fortran
- Работа с матрицами.
С матрицами (например, A, B, C) можно работать как с переменными:
A = 2*B + sin(C)
К матрицам (поэлементно) могут быть применены любые чистые функции (как синус в примере).
Вырезки и сечения матриц:
B[1:5,1:5] = A[1:10:2,1:10:2]
В отличие от MatLAB-подобных языков, шаг - третий, а не второй элемент триплета.
Конструкция forall
. Например, заполним матрицу \(n \times n\) единицами по диагонали (сделаем ее единичной):
A = 0
forall(i = 1:n) A(i,i) = 1
Конструкция where
. Например, так можно обнулить все отрицательные компоненты матрицы B:
where(B < 0) B = 0
С матрицами, вообще, множество удобных встроенных функций – практически на все случаи жизни.
Матрицы могут быть многомерными.
Вызов процедур: поддерживается перегрузка параметров, необязательные и именованные параметры.
Типы, включая комплексные числа. Удобные древовидные пользовательские типы (структуры данных).
Как ни странно, форматный ввод-вывод, к которому привыкаешь, но который я также отмечу как одно из самых ужасных свойств языка. :)
Что ужасно в Fortran
Современный Fortran позволяет писать более-менее сносный код, если не пользоваться устаревшими конструкциями языка, поддерживаемыми ради совместимости, например:
типизацией переменных по умолчанию на основе первых букв их имен;
разными точками входа в процедуры;
переходами goto с метками;
указателями и прочими средствами работы с памятью «напрямую» и т.д.
Иногда чешутся руки, наоборот, это все использовать и писать программы аутентично «индийские». В идеале можно выйти на абсолютный Brainfuck
. :)
Не только эти, но и в принципе многие конструкции языка, конечно же, устарели. Что особенно бесит:
- Работа со строками.
В Fortran нет строк переменной длины, размер строк нужно задавать явно.
Специфичны преобразования других типов данных в строку и обратно. Чтобы преобразовать, например, вещественное число r
в строку str
или наоборот, приходится делать это через операторы ввода-вывода:
write(str, "(f8.3)") r ! str <- r в формате f8.3
read(str,*) r ! str -> r
Поскольку строки – фиксированной длины, которая обычно берется с запасом, даже при обычной конкатенации приходится строки обрезать по пробелам:
str = trim(str1) // trim(str2) ! Если строки выровнены влево
str = trim(AdjustL(str1)) // trim(AdjustL(str2)) ! Если не выровнены :)
Разумеется, в Fortran
нет никаких регулярных выражений и других «вкусностей», к которым в современных языках мы успели уже «прикипеть».
- Форматный ввод-вывод – очень мясной. Он был удобен для терминалов, но сейчас это – кошмарный анахронизм:
print "(a,i4,a,10f8.3)", "Шаг:", i, "Вектор:", V
С другой стороны, если к этому попривыкнуть, печатать стройные красивые матрицы (а также засасывать их из текстовых файлов как исходные данные кодом в одну строку) – и в правду очень удобно.
Нет списков и вообще структур данных с изменяемой длиной «на лету». (Динамически размещаемые массивы, конечно же, есть).
Переусложненная система типов. Вещественные числа разной длины пишутся с указанием разновидности (KIND) весьма специфично:
real :: r1 = 1. ! По умолчанию real – это real(4)
real(8) :: r2 = 1._8
real(16) :: r3 = 1._16
С другой стороны, KIND можно определить на уровне директивы компилятора и везде использовать просто real
и числа без этих «_8
»
Мало средств «декомпозиции и абстракции» для поддержания структуры больших проектов.
Избыточность языка, связанная с историей и совместимостью снизу вверх.
Наследуемый код, написанный за пол века «учеными и инженерами» – в подавляющей массе просто чудовищен. Чуть менее, чем весь созданный код. :) Почему так – загадка. Прямой вины языка в этом нет. Потемки – душа ученого.
Вместо Conclusions
В этой импровизированной статье пока не был раскрыт ключевой вопрос абстракта «как заставить себя в XXI веке писать на Fortran?». :)
Мейнстримовый C++
предлагает аналогичную скорость.
Лаконичный Python
предоставляет неслыханную мощь и ясность/читаемость кода. Одна строчка идет за 5-10 Фортрана.
Julia
совсем наступает на пятки, предлагая почти ту же мощь и прозрачность, что и Python
, и вполне сравнимую с Fortran
скорость.
На самом деле, у меня нет разумного ответа на сакраментальный вопрос, кроме лишь одного: Fortran
надо любить. :) Как дедушку и стареющего отца.
Каждый раз, открывая текстовый редактор с Фортраном, чувствуешь, что «подключаешься» к чему-то ретрофутуристично-прекрасному. :) Будущее, как видели его в 60-х годах, космические полеты, покорение звезд. Элементарные частицы, атомные реакторы. Шуршащие пленкой вычислительные машины. Папиросы профессоров «Прикладной математики». Все это преломляется в вашем редакторе, хочется открывать его еще и еще. :) А на современных машинах все эти отблески проносятся с бешенной скоростью… Ну и конечно, по совокупности качеств, с чего «статья» начиналась…
Пускай в этом блоге Fortran
будет отправной точкой в мир более современных решений.
Спасибо за очень интересный и информативный обзор. Я использую старый Compaq Visual Fortran, думаю со временем перейти на что то более современное.
ОтветитьУдалитьСпасибо за статью, но забыли про тип REAL(10) - 80-битовое вещественное число, который также поддерживается в GNU-фортране наряду с типами REAl(4), REAL(8) и REAL(16). Вместо опции компилятора -freal-4-real-8 можно задать опцию -freal-4-real-10, и тогда все вещественные переменные и числовые константы в программе будут иметь не двойную, а расширенную точность (80 бит), что соответствует 19-значной десятичной разрядности. Если же есть старая программа, в которой понапиханы всякие implicit real*8(a-h,o-z) и горячо любимые буквы "D" во все числовые константы, то можно эту программу откомпилировать ничего в ней не меняя с опцией -freal-8-real-10, и точность вычислений будет на три порядка выше.
ОтветитьУдалить