HPF (High Performance Fortran) является расширением языка Фортран 90 для многопроцессорных вычислительных систем [18]. HPF реализует модель программирования, базирующуюся на параллелизме данных. Параллелизм данных означает, что одна и та же операция применяется к части или всему ансамблю данных, поэтому HPF существенным образом ориентирован на обработку массивов. Массивы распределяются по вычислительным узлам, и каждый узел выполняет обработку своей части массива. От программиста требуется только задать распределение данных, а компилятор автоматически вставит необходимые операции обменов и синхронизаций. Эффективность HPF программы существенным образом зависит от того, насколько удачно выбрано распределение данных. Процедура распределения данных плохо формализуется, поэтому она возлагается на программиста. Для обеспечения параллельной обработки данных в язык HPF по сравнению с Фортран 90 добавлены следующие расширения:
Основная часть расширений реализована в виде директив компилятору. Директивы HPF имеют вид комментариев, начинающихся с символов !HPF$. Директивы не выполняют никаких действий, а лишь задают компилятору дополнительную информацию, требуемую для генерации эффективного кода. Важнейшие из директив связаны с размещением данных и призваны минимизировать коммуникационные операции. Кроме того, имеются директивы для явного указания режимов выполнения циклов. Язык HPF реализует идею <инкрементального распараллеливания> для систем с распределенной памятью и с точки зрения организации и идеологии очень похож на OpenMP.
Разработкой стандарта HPF занимается High Performance Fortran Forum (HPFF). Первая версия HPF 1.0 вышла в 1993 году. Текущей версией является HPF 2.0, разработанной в 1997 г. На сегодняшний день доступно несколько коммерческих версий и несколько версий с открытым кодом. На объединенном кластере механико-математического комплекса ЮФУ (установлена свободно распространяемая система компиляции Adaptor [19].
Посмотрим, как выглядит наша программа на языке HPF.
program pi_hpf integer n, i double precision d, s, pi double precision, dimension (:), $ allocatable :: x, y !HPF$ PROCESSORS procs(4) !HPF$ DISTRIBUTE x(CYCLIC) ONTO procs !HPF$ ALIGN y(i) WITH x(i) write(*,*) 'n?' read(*,*) n allocate(x(n)) allocate(y(n)) d = 1.0/n !HPF$ INDEPENDENT FORALL (i = 1:n) x(i) = (i-0.5)*d y(i) = 4.0/(1.0 + x(i)*x(i)) end FORALL pi = d*SUM(y) write (*, 100) pi 100 format(' pi = ', f20.15) deallocate(x) deallocate(y) end
Бросается в глаза, что параллельная программа действительно очень похожа на программу, разработанную средствами OpenMP. Сама программа мало чем отличается от обычной последовательной программы, кроме вставки директив HPF и замены цикла DO на цикл FORALL. Однако, это не принципиальная замена, поскольку оператор цикла FORALL уже давно вошел в стандарты современных версий Фортрана. Кроме того, директива INDEPENDENT воздействует и на обычный цикл DO, заставляя его выполняться в параллельном режиме.
Программа может компилироваться как на многопроцессорных системах с компилятором HPF, так и на обычных компьютерах обычным компилятором. В последнем случае, директивы HPF будут восприниматься как обычные комментарии.
Директива "PROCESSORS procs(4)" определяет форму сетки абстрактных процессоров. Подразумевается использование 4-х процессоров с линейной топологией. В данном случае эта директива не обязательна, поскольку линейная топология подразумевается по умолчанию. В тех случаях, когда требуется более сложная топология, то ее необходимо задавать явно. Например, директива ''!HPF$ PROCESSORS procs(6,2)'' описывает двумерную сетку 6?2 из 12 процессоров. При этом программа теряет некоторую универсальность - она может запускаться только на 12 процессорах.
Директива DISTRIBUTE определяет распределение по процессорам массива x. В данном случае принято циклическое распределение - каждый последующий элемент циклически привязывается к следующему процессору. Альтернативный способ x(BLOCK) будет распределять по процессорам непрерывные блоки примерно равной длины.
Директива ALIGN используется для выравнивания распределений массивов x и y. В данном случае требуется, чтобы каждый i-й элемент массива y находился в том же процессоре, где i-й элемент x. При такой схеме размещения заведомо не потребуется пересылок.
Директива INDEPENDENT указывает, что цикл должен выполняться параллельно, хотя оператор FORALL и так подразумевает параллельное выполнение. Внутренняя функция SUM выполняет глобальное суммирование по процессорам.
Возвращаясь к сравнению HPF с OpenMP, можем отметить, что оба подхода базируются на добавлении директив компилятору в обычную последовательную программу. Однако при всей своей похожести, они реализуют совершенно разные парадигмы. В OpenMP модель программирования базируется на распределении вычислений, а в HPF модель программирования базируется на распределении данных. Поэтому OpenMP ориентирован на системы с общей памятью, а HPF на системы с распределенной памятью.
В программе pi_hpf.f искусственно введены массивы x и y, чтобы подчеркнуть присущую HPF парадигму параллелизма данных. Мы векторизуем цикл, затем вектор распределяем по процессорам и производим параллельную обработку частей вектора. Однако некоторые реализации HPF позволяют напрямую производить распараллеливание циклов в стиле OpenMP. В частности, такую возможность допускает система компиляции Adaptor. В этом случае оказалось, что для распараллеливания нашей последовательной программы достаточно добавить всего одну строку - директиву HPF, выделенную жирным шрифтом.
program pi_adp integer i, n double precision w, gsuM double precision v print *, 'Input number of stripes : ' read *, n w = 1.0 / n gsum = 0.0d0 !hpf$ independent, new (v), reduction (gsum) do i = 1, n v = (i - 0.5d0 ) * w v = 4.0d0 / (1.0d0 + v * v) gsum = gsum + v end do print *, 'pi ist approximated with ', gsum *w end
Распараллеливание программ с помощью HPF выглядит очень привлекательным. Переделки последовательной программы минимальны, программа очень компактна, однако эффективность программ оставляет желать лучшего. Даже для такой простой задачи, программа на HPF работает в два раза медленнее, чем MPI программа. Причем на любом числе процессоров, даже на одном. Возможно, это относится к конкретной реализации системы компиляции, но в любом случае, из-за низкой эффективности HPF пока не получил такого распространения и признания как MPI.