Причем обычно выбор не представляется очевидным — всегда находится какой-нибудь продукт, возможности которого оказываются шире, а бывает и так, что ни одна из программ в полной мере не отвечает требованиям решаемой проблемы. Следовательно, в том случае, когда «еще не все написано», приходится создавать приложения самостоятельно, а при работе с Windows обязательно возникнет вопрос: какое средство разработки лучше выбрать?

Большинство существующих визуальных сред на различных языках программирования предназначены прежде всего для построения развитого интерфейса приложений, ориентированных на деловое и коммерческое применение. Они не оптимизированы специально для разработки программ, использующих интенсивные вычисления с плавающей точкой, за исключением лишь некоторых компиляторов с языка Си++, поддерживающих стандарт NCEG (Numerical C Extension Group). Поэтому для решения подобных задач, как и десятки лет назад, применяется Фортран.

За последнее десятилетие Фортран уже поменял два стандарта, а производительность ПК возросла во много раз и приблизилась к тому уровню, который характерен для средних рабочих станций под управлением ОС Unix. Поэтому многие программы стали переноситься с традиционной RISC-платформы Unix на Intel-платформу под управлением ОС Windows NT, да и сама ОС Unix стала устанавливаться на ПК. Сегодня многопроцессорные компьютеры (MPS) c симметричной мультипроцессорной обработкой данных (SMP), функционирующие в среде Windows NT, стали вполне доступны. При работе на них программисту нужно полностью использовать все ресурсы, причем как можно проще и быстрее, и сделать это можно с помощью Фортрана.

В России сложилось так, что для разработки Фортран-приложений на ПК применяли MS Fortran PowerStation. Сейчас преемником этого языка можно считать Compaq Visual Fortran — полноценную визуальную среду разработки, в которой полностью реализован стандарт Фортран-95 (ISO 1539-1997(E)). Она поставляется вместе с Microsoft Visual Studio 6.0 и полностью заменяет компиляторы с языка Фортран для этой среды, не выпускаемые Microsoft уже с 1996 г. Среда Compaq Visual Fortran предлагается в версиях Standard, Professional и Enterprise Еdition. Версия Standard включает в себя визуальную среду программирования, программу визуализации данных Array Viewer и расширенную библиотеку математических программ CXML, содержащую пакеты BLAS, LAPACK и LINPACK.

В версию Professional дополнительно входит математическая библиотека IMSL фирмы Visual Numerics с возможностью реализации интерфейса MPI для распределенных вычислений, расширенная версия программы визуализации Array Visualizer и профессиональная версия Compaq Visual Fortran для систем на основе процессора Alpha. Версия Enterprise включает все компоненты Professional, а также Enterprise Toolkit, содержащий Microsoft Visual SourceSafe и расширения Technical Programming Extensions.

Все версии дополняются компакт-диском Companion Disk, на котором собраны полнофункциональные варианты программы автоматического распараллеливания исходного текста в стандарте OpenMP—Visual KAP фирмы Kuck & Associates (htpp://www.kai.com) и математическая библиотека NAG с поддержкой SMP фирмы Numerical Algorithms Group (htpp://www.nag.com). Лицензии на каждый из этих продуктов приобретаются по отдельности через Internet.

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

В основе реализации параллельных вычислений лежит оперирование массивами с помощью встроенных операций и функций, оптимизированных компилятором для выполнения на конкретной вычислительной платформе. Это параллельность типа SIMD (одна инструкция, много данных). Таким образом реализована работа с массивами в стандарте Фортран-95. Для проведения с ними арифметических операций можно, например, записать: A = B + C, где A, B, C — идентификаторы массивов.

Параллельность типа MIMD (много инструкций, много данных) также доступна через несколько функций. Так, функции упаковки и распаковки массивов (pack, unpack), pure-расширения этих функций и оператор forall позволяют получить доступ к свойствам MIMD, скрывая детали его реализации.

Рассмотрим пример вычисления суммы элементов массива по формуле (1)



(1)

Стандартная функция для последовательной реализации алгоритма может выглядеть как SumAbs (листинг 1), а для параллельной — как ParSumAbs (листинг 2).

Листинг 1

 Subroutine SumAbs(w,x,n) 
 Implicitnone 
 integer,intent(in) :: n 
 real,dimension(n),intent(in) :: x 
 real,dimension(n),intent(out):: w 
 integer I,j
 do i=1, n 
 w(i) = 0.0 
 do j=1, n
 w(i)=w(i)+ABS(x(i)+x(j)) 
 enddo
 enddo
 end subroutine

Листинг 2

 Subroutine SumAbs(w,x,n) 
 implicit none 
 integer,intent(in) :: n 
 real,dimension(n),intent(in) :: x 
 real,dimension(n),intent(out):: w 
 integer i
 forall(i=1:n)w(i)=SUM(ABS(x(i)+x))
 end subroutine 

Встроенная параллельная функция SUM в примере 2 заменяет внутренний цикл DO в примере 1, а параллельный оператор FORALL — внешний цикл DO.

Можно сравнить, насколько параллельное вычисление выполняется быстрее, чем последовательное. Для этого проводилось тестирование на компьютере Digital 5500 (два процессора Pentium II-266, ОЗУ объемом 256 Мбайт, шина Ultra Wide SCSI) под управлением ОС Windows NT с использованием компилятора Compaq Visual Fortran v. 6.1A при отключении соответствующей опции, чтобы не оказывала влияние оптимизация. Время работы программы для n=30 000 составило при последовательном и параллельном алгоритмах 1 мин 6 с и 34 с соответственно.

Возможно ли полностью избавиться от циклов при параллельном программировании? В общем-то, да. Для этого нужно убрать цикл FORALL из примера 2 с помощью встроенной параллельной функции SPREAD. Тогда ParSumAbs будет выглядеть следующим образом (листинг 3):

Листинг 3

subroutine ParSumAbs(w,x,n)
integer, intent(in) :: n
real, dimension(n), intent(in) :: x
real, dimension(n), intent(out) :: w
real, allocatable, dimension(:,:) :: work
allocate(work(n,n))
work = SPREAD(x,dim=2,ncopies=n)
+SPREAD(x,dim=1,ncopies=n)
w = SUM(ABS(a),dim=1)
deallocate work
end subroutine

В листинге 3 добавился динамический массив work, что можно рассматривать как определенную (причем довольно весомую!) плату за исключение циклов. Как же работает функция SPREAD? В первом случае она создает массив размерностью n Ё n, состоящий из элементов массива X, которые размещены по строкам, а во втором организует аналогичный массив, но уже по столбцам. При параллельной операции «+» массивы поэлементно суммируются, а при операции «=» полученный результат записывается в массив WORK. Схематически этот процесс можно представить следующим образом:

X(1) X(1) X(1)          X(1) X(2) X(3)
X(2) X(2) X(2)    +     X(1) X(2) X(3)
X(3) X(3) X(3)          X(1) X(2) X(3)
. . . . . . .           . . . . . . . .
. . . . . . .           . . . . . . . .

SPREAD(x,dim=2,ncopies=n) + SPREAD(x,dim=1,ncopies=n)

Затем следует включить в работу параллельную функцию SUM, которая просуммирует элементы массива WORK по столбцам и запишет результат в массив W.

Однако такой подход требует определенной осторожности. Если величина n велика, то для эффективного распараллеливания (когда в памяти производится примерно n х n арифметических операций для двух вызовов функции SPREAD) необходима мощная вычислительная платформа с числом процессоров, намного превышающим n, иначе эффективность будет невысока. Поэтому с практической точки зрения вариант с одним циклом FORALL (листинг 2) более предпочтителен.

Используя рассмотренные выше примеры, можно составить и другие алгоритмы, например параллельный алгоритм для перемножения двух квадратных матриц

[В][С]=[A].

(2)

Элемент матрицы аij определяется по формуле линейной алгебры



(3)

Стандартная функция перемножения матриц по формуле (3) реализуется следующим образом:

Листинг 4

subroutine Matrix_mul(a,b,c,n)
integer, intent(in) :: n
real, dimension(n,n), intent(in) :: b,c
real, dimension(n,n), intent(out) :: a
integer i,j,k
real sm
do i=1, n 
 do j=1, n 
 sm=0.0 
 do k=1,n 
 sm=sm+b(i,k)*c(k,j) 
 enddo 
 a(i,j) = sm 
 enddo 
enddo 
end subroutine

Функция Matrix_mul содержит два внешних и один внутренний цикл DO, который соответствует формуле (3). Проанализировав выражение (2), можно сделать вывод, что в основу параллельного варианта функции перемножения матриц следует положить выражение

,

(4)

где вектор {} составлен из транспонированной i-й строки матрицы B, а транспонированный вектор {} является i-й строкой матрицы A.

Формулу (4) необходимо применить последовательно ко всем строкам матрицы B, т. е. для всех i = 1, . . . ,n. Параллельный вариант функции перемножения матриц можно представить следующим образом:

Листинг 5

subroutine Parallel_mul(a,b,c,n)
implicit none
integer, intent(in) :: n
real, dimension(n,n), intent(in) :: b,c
real, dimension(n,n), intent(out) :: a
real, allocatable, dimension(:,:) :: work
integer i
allocate(work(n,n))
do i=1, n 
 work=SPREAD( b(i,:), dim=1, ncopies=n )
 a(i,:)=SUM( TRANSPOSE(c)*work,dim=2 ) 
enddo
deallocate(work)
end subroutine 

Параллельная функция SPREAD создает из i-й строки матрицы B массив размерностью n х n, строки которого представляют собой i-ю строку матрицы B, и записывает его вместо WORK. В параллельной функции SUM происходят перемножение транспонированной матрицы С на матрицу WORK (здесь применена встроенная функция транспонирования матриц «на лету» — TRANSPOSE), и вычисление суммы элементов произведения по строкам. При этом параллельная операция «*» означает поэлементное перемножение компонент двух матриц. В результате согласно формуле (4) образуется i-я строка матрицы А. Применив цикл DO для всех строк матрицы B (i = 1, . . . ,n), получим искомую матрицу A, также заполняемую по строкам.

Необходимо отметить, что не каждый алгоритм может быть записан в параллельной форме. Например, метод исключения Гаусса для решения систем линейных алгебраических уравнений плохо поддается распараллеливанию, тогда как модификация метода Гаусса для трехдиагональных систем позволяет получить параллельную версию алгоритма [1].

Благодаря параллельному программированию можно избавиться от операторов цикла либо сократить их число до минимума, и тогда программа выглядит более компактно, а получаемый при трансляции код лучше оптимизирован. Чтобы описание было полным, нужно учесть, что Фортран-95 содержит встроенную процедуру перемножения матриц, вызывать которую можно так:

a = matmul(b,c). 

Эффективность применения параллельных алгоритмов в большей степени проявляется на многопроцессорных платформах при наличии соответствующего ПО. В семействе Unix поддержка на уровне ОС и средств разработки приложений используется давно — это API-интерфейсы для систем с разделенной памятью, MPI- и OpenMP-интерфейсы для систем с общей памятью. Для того чтобы задействовать все ресурсы многопроцессорной системы (процессоры, общую память и т. д.), в OpenMP-интерфейсе программист должен указать препроцессорные директивы оптимизации в исходном тексте программы, и тогда работу по равномерному распределению ресурсов возьмут на себя компилятор и ОС.

В семействе Microsoft Windows только Windows NT и Windows 2000 поддерживают на уровне функций ОС многопроцессорные SMP-платформы с общей памятью. Для разработки мультипроцессорных приложений на языках Фортран и Cи++ в Windows NT/2000 существуют специальные Multithreaded (многопоточные) функции WIN32 API. В этом случае вся работа по распределению системных ресурсов целиком ложится на программиста, что может оказаться довольно сложной задачей.

Компилятор Compaq Visual Fortran поддерживает OpenMP-интерфейс только в версиях для ОС Unix (Compaq Fortran for Tru64 Unix). Чтобы реализовать OpenMP на Windows NT/2000, требуются программы сторонних разработчиков. В частности, неплохим выбором может оказаться указанный выше Visual KAP, представляющий собой препроцессор с анализатором программы, который позволяет автоматически модифицировать ее исходный текст в OpenMP API-интерфейсе и собственной библиотеке функций, использующей Multithreaded WIN32 API. Поэтому препроцессор Visual KAP поставляется в версиях Autoparallel (на основе Multithreaded WIN32 API) и for OpenMP, причем последняя дает возможность вставлять директивы OpenMP как автоматически, так и вручную.

Наибольшая эффективность при применении Visual KAP в автоматическом режиме может быть получена при распараллеливании последовательных программ, а вот оптимизация параллельных не приносит значительного выигрыша, и тогда приходится вручную устанавливать директивы OpenMP. Теперь рассмотрим, насколько повышается производительность программ при использовании версии препроцессора Visual KAP for OpenMP в автоматическом режиме, в частности для функции перемножения матриц в случае последовательного алгоритма (пример 4). В демонстрационной версии Visual KAP, имеющейся на диске Companion disk, приведен подобный пример.

На тестовой машине Digital PC5500, работающей под управлением Windows NT c компилятором Compaq Visual Fortran 6.1A и препроцессором Visual KAP 3.6f, были получены следующие результаты. Листинг 4 при работе только одного процессора (без записи директив OpenMP) выполнялся за 14 с., в то время как листинг 5 (с записью директив OpenMP и API) при функционировании всех процессоров в системе — за 9 с. Таким образом, реальная производительность двухпроцессорной системы в среднем на 70—80% выше, чем однопроцессорной, что весьма эффективно даже с учетом всех накладных расходов на разделение общих ресурсов для SMP-систем. В случае версии Autoparallel результаты получаются еще лучше, а листинг 4 рассчитывается за 3 с и производительность возрастает почти в пять раз. К сожалению, для параллельного программирования приходится вручную оптимизировать текст, вставляя директивы OpenMP.

Не секрет, что математик при разработке вычислительных программ большую часть времени тратит на решение конкретной технической или математической задачи, а не на создание интерфейса для нее. На такой стадии интерфейс либо имеет довольно «спартанский» вид, либо вовсе отсутствует. Чтобы восполнить этот пробел и предложить разработчикам программ математического и инженерно-технического профиля полноценный Windows-интерфейс, вместе с компилятором поставляется библиотека QuickWin. Она позволяет с минимальными усилиями быстро организовать SDI- и MDI-интерфейсы для приложений под Windows. Стандартное MDI-приложение на Фортране-95, где используется библиотека QuickWin, выглядит следующим образом (см. рис. 1).

Рис. 1

Конечно же, в Compaq Visual Fortran можно реализовать все функции Windows API и организовать любой интерфейс для Win32-приложений. Но на это потребуются большие трудозатраты, в то время как для его создания с помощью QuickWin нужно написать лишь несколько десятков строк текста, а разработанные приложения можно будет без значительных изменений переносить на различные вычислительные платформы.

Особый интерес представляет основанная на OpenGL программа трехмерной визуализации данных Array Vizualizer, которая может вызываться непосредственно из приложения или работать как автономный модуль. Она позволяет вращать полученные изображения в любом направлении, чтобы можно было лучше просмотреть не только отдельные детали, но и таблицу данных, по которым выполнена визуализация (см. рис. 2).

Рис. 2

В заключение необходимо отметить, что объектный код Compaq Visual Fortran совместим с Microsoft Visual C++ и Visual Basic, т. е. можно создавать проект из нескольких файлов на различных языках программирования, если, естественно, дополнительно установлены Visual C++ и Visual Basic.

ОБ АВТОРЕ

Максютов Малик Сабитович — доцент кафедры ВНиММ МГГА, e-mail: mmax@online.ru

ЛИТЕРАТУРА

1. Press W. H., Teukolsky S. A., Vetterling W. T., Flannery B. P., Metcalf M. Numerical Recipes in Fortran 90, second edition. Cambridge University Press, 1996.

2. Бартеньев О.В. Visual Fortran: новые возможности. М.: Диалог-МИФИ, 1999.


VISUAL FORTRAN

Compaq Visual Fortran предъявляет скромные требования к программному и аппаратному обеспечению ПК: ОС Windows 95, процессор Pentium 90, 24-Мбайт ОЗУ и примерно 320 Мбайт свободного дискового пространства при полной установке. Среда разработчика Compaq Visual Fortran (Developer Studio) интегрирована в MS Visual Studio и имеет стандартный интерфейс. Если у вас дополнительно установлен MS Visual C++ 6.0, то можно не только создавать смешанные проекты на разных языках программирования, но и редактировать их средствами Visual C++. В языке доступны все функции отладчика как из собственной среды, так и из среды Visual C++.

ПРЕПРОЦЕССОР VISUAL KAP

Препроцессор Visual KAP позволяет оптимизировать исходные файлы в Compaq Visual Fortran для выполнения на многопроцессорных системах под управлением Windows NT/2000. Он содержит различные опции и режимы параллелизма для более тонкой настройки процесса оптимизации за счет использования параллельных функций, реализованных в собственных библиотеках Visual KAP. После обработки исходных файлов (причем файл не изменяется, а создается его оптимизированная версия с другим именем) остается только подключить библиотеку ompd.lib препроцессора к проекту Compaq Visual Fortran — и проект готов к запуску на многопроцессорной машине в оптимальном режиме.

Visual KAP не только распараллеливает алгоритмы, но и оптимизирует последовательные алгоритмы (опция Serial optimizations only), что позволяет избавиться от неэффективных циклов, вызовов функций и др. При этом программы, оптимизированные для многопроцессорных машин, можно устанавливать и на однопроцессорные компьютеры.