Параллельное программирование с использованием OpenMP

Средства автоматизированного распараллеливания программ

Разбить на страницы
Показывать лекцию целиком

Как уже отмечалось в предыдущих разделах, средства автоматизированного распараллеливания следует использовать на первом этапе разработки параллельной программы, если у нее уже существует работающий последовательный аналог. В этом случае на первом этапе разработки параллельной программы рекомендуется применить средства автоматизированного распараллеливания к исходной последовательной программе.

В настоящее время все основные компиляторы Fortran и C/C++, предназначенные для разработки параллельных программ с использованием OpenMP, имеют возможности автоматического распараллеливания. Кроме того, эти компиляторы допускают установку различных уровней автоматического распараллеливания, а также генерируют отчеты по результатам распараллеливания.

После автоматического распараллеливания рекомендуется провести профилирование программы, и если окажется, что наиболее трудоемкие места хорошо распараллелены, то работу по созданию параллельной версии программы можно закончить. В противном случае рекомендуется воспользоваться средствами OpenMP для дальнейшего распараллеливания наиболее трудоемких участков программы. Обычно так и происходит, поскольку для сложных программ с помощью автоматического распараллеливания редко удается получить максимальный эффект. Чтобы четко разобраться в этом вопросе, далее рассмотрим некоторые основные средства автоматизированного распараллеливания и принципы их работы.

Основные средства автоматизированного распараллеливания и принципы их работы

Основным объектом автоматизированного распараллеливания с помощью специальных настроек компиляторов являются циклы. При распараллеливании циклов компилятор, во-первых, выделяет независимые по данным петли циклов. Во-вторых, он оценивает приблизительный эффект от распараллеливания цикла. Если оценка этого эффекта соответствует заданному уровню, то компилятор определяет локальные переменные. И, наконец, только затем производится само распараллеливание цикла с помощью библиотек процессов. По результатам проведенной или не проведенной работы выдается сообщение в файл отчета о распараллеливании с объяснением причин.

Как правило, других возможностей автоматизированного распараллеливания большинство компиляторов не имеет. Все это в полной мере относится к компиляторам Fortran и C/C++ компании Intel и некоторым другим. Как видим, вышеперечисленные возможности автоматизированного распараллеливания сравнительно невелики.

Отметим, что значительно более широкими возможностями в области автоматического распараллеливания обладает компилятор Fortran 95 для операционной системы Linux компании Lahey, известный под названием Lahey/Fujitsu Fortran 95 for Linux. В этом компиляторе значительно улучшен процесс оптимизации программ, а также обеспечивается поддержка аппаратных функций новых процессоров. Кроме того, поддерживается автоматическое распараллеливание и дополнительные инструкции SSE2 для процессоров Intel Xeon и Pentium IV. Компилятор Fortran Lahey/Fujitsu занимает одну из лидирующих позиций в областях, где необходимы большие объемы математических вычислений и осуществляется работа с большими массивами. Этот компилятор осуществляет автоматическое распараллеливание программ, в том числе и с использованием OpenMP версии 2.0. Оптимизация программ осуществляется для процессоров как компании Intel, так и компании AMD, при этом поддерживается оптимизация конвейера упреждающей выборки. Кроме того, для отладки параллельных программ компилятор совместим с параллельным отладчиком TotalView.

Кроме компиляторов существуют и более продвинутые программные системы в области автоматизированного распараллеливания.

В качестве примера отметим программный продукт Bert77, разрабатываемый компанией Paralogic. Программа Bert77 автоматически распараллеливает Fortran-программы в средах PVM или MPI. Распараллеливание осуществляется с использованием механизма обмена сообщениями. Программа Bert77 - это коммерческий продукт, однако существует и его упрощенная бесплатная версия (lite-версия).

В подразделении компании Intel - KAI Software Lab (бывшая Kuck Associates Inc.) разработаны и продолжают совершенствоваться:

  • KAP/Pro Toolset - набор программных средств для распараллеливания больших расчетных программ на параллельных вычислительных системах с общей памятью;
  • KAI C++ - компилятор С/C++ с широкими возможностями оптимизации и распараллеливания;
  • Visual KAP - программа автоматического распараллеливания Fortran-программ в режиме визуального диалога;
  • Visual KAP для OpenMP - инструмент визуальной генерации программ с использованием OpenMP.
  • Компания Applied Parallel Research Inc. предлагает программу spf, автоматически распараллеливающую программы для параллельных вычислительных систем с общей памятью с применением OpenMP или POSIX.

    Компания Pacific-Sierra Research разработала интересные и очень эффективные средства:

  • VAST/Parallel - программу автоматического распараллеливания программ на языках Fortran и C/C++ для параллельных вычислительных систем с общей памятью с использованием OpenMP;
  • VAST/toOpenMP - программу, автоматически распознающую параллелизм и генерирующую директивы OpenMP в программах на языке Fortran.
  • Отметим также разработанную этой компанией среду проектирования, отладки и анализа производительности параллельных программ в системах с распределенной и общей памятью DEPP (Development Environment for Parallel Programs), реализующую директивы OpenMP.

    Существует и еще целый ряд других средств автоматизированного распараллеливания, однако здесь были перечислены лишь те средства, которые осуществляют автоматическое распараллеливание с использованием OpenMP.

    Автоматическое распараллеливание программ с помощью компиляторов Intel

    Рассмотрим возможности автоматического распараллеливания программ с применением современных компиляторов компании Intel. Отметим, что в компиляторах Intel реализована только одна принципиальная возможность распараллеливания: это создание многопоточных приложений распараллеливанием цикла с помощью библиотек процессов.

    Далее рассмотрим подробнее настройки режима автоматического распараллеливания, имеющиеся в компиляторах Fortran и С/C++ компании Intel:

  • -parallel - эта настройка позволяет компилятору автоматически создавать многопоточные версии программ с безопасным распараллеливанием циклов (подчеркнем, что кроме циклов в этом режиме больше ничего не распараллеливается);
  • -par_report{0|1|2|3} - эта настройка позволяет создавать отчеты различного уровня - 0, 1, 2 или 3 (наиболее подробный отчет имеет уровень 3) по результатам автоматического распараллеливания. Рекомендуется тщательно анализировать такие отчеты, чтобы четко понять, какие еще места программы могли бы быть распараллелены и почему это не было сделано. Возможно, компилятору не хватило информации для принятия решения о распараллеливании такого участка программы. В этом случае это можно сделать вручную;
  • -par_threshold[n] - эта настройка передает компилятору целое число n в диапазоне от 0 до 100. n является оценкой эффективности распараллеливания циклов в процентах. Эту оценку компилятор производит самостоятельно.
  • Настройки компиляторов Intel для распараллеливания программ с использованием OpenMP

    В предыдущем параграфе были рассмотрены возможности автоматического распараллеливания программ для параллельных вычислительных систем с общей памятью. Однако, как правило, в автоматическом режиме нельзя выбрать все имеющиеся возможности для эффективного распараллеливания программ. Поэтому для параллельных вычислительных систем с общей памятью используют средства OpenMP для более глубокого распараллеливания программ.

    Современные компиляторы Intel содержат следующие специальные настройки для создания параллельных программ с применением средств OpenMP для параллельных вычислительных систем с общей памятью:

  • -openmp - эта настройка позволяет компилятору автоматически создавать многопоточные версии программ с использованием директив OpenMP;
  • -openmp_profile - эта настройка добавляет в создаваемую программу средства профилирования программы для последующего анализа с помощью программы VTune Performance Analyzer;
  • -openmp_stubs - эта настройка позволяет компилировать OpenMP программы в последовательном режиме. При этом предложения OpenMP игнорируются, а библиотека OpenMP используется редактором связей в последовательном режиме;
  • -openmp_report{0|1|2} - эта настройка позволяет создавать отчеты различного уровня - 0, 1 или 2 (наиболее подробный отчет имеет уровень 2) по результатам автоматического распараллеливания с использованием OpenMP. Рекомендуется тщательно анализировать такие отчеты, чтобы четко понять, какие еще места программы могли бы быть распараллелены и почему это не было сделано. Возможно, компилятору не хватило информации для принятия решения о распараллеливании того или иного участка программы. В этом случае это можно сделать вручную.
  • Итак, вышеперечисленные настройки компиляторов позволяют создавать параллельные версии программ как просто с многопоточным распараллеливанием циклов, так и с помощью директив OpenMP. Возможно также сочетание обоих этих режимов распараллеливания.

    В заключение для полноты изложения приведем простейшие команды компиляции программ prog.c и prog.f с помощью компиляторов Intel:

    icc -openmp prog.c

    и

    ifort -openmp prog.f

    Здесь prog.c и prog.f - соответственно тексты программ, написанных на алгоритмических языках C/C++ и Fortran с применением директив OpenMP. Для программ, написанных на Fortran, компилятор распознает также расширения файлов с текстами программ for и f90 (для текстов программ, написанных на алгоритмическом языке Fortran 90). В результате компиляции в среде операционной системы Linux создается выполняемый файл a.out. Отметим также, что для создания выполняемых файлов с именем prog следует использовать следующие команды компиляции для программ, написанных на алгоритмических языках C/C++ и Fortran соответственно:

    icc -openmp prog.c -o prog

    и

    ifort -openmp prog.f -o prog

    Настройки компиляторов Intel для распараллеливания программ для кластеров с распределенной памятью

    В предыдущем параграфе были рассмотрены возможности автоматического распараллеливания программ, эффективные для параллельных вычислительных систем с общей памятью. При создании программ для параллельных вычислительных систем с распределенной памятью, как было отмечено ранее, применяются методы распараллеливания с использованием MPI и PVM. Однако в новой версии компиляторов Intel, начиная с версии 9.1, реализовано расширение OpenMP, предназначенное для распараллеливания программ для вычислительных систем с распределенной памятью. Это расширение известно под названием Cluster OpenMP. В нем имеется возможность объявлять области данных доступными для всех узлов кластера. Эта возможность реализуется с помощью предложения sharable, которое будет рассмотрено далее. Пока же отметим, что это предложение реализует неявную передачу данных между узлами кластера по протоколу Lazy Release Consistency и избавляет программистов от утомительного процесса анализа посылаемых и принимаемых сообщений, необходимого при использовании MPI. В результате процесс параллельного программирования для параллельных вычислительных систем с распределенной памятью существенно упрощается.

    Компиляторы Intel, начиная с версии 9.1.x.xx, получили следующие дополнительные настройки, предназначенные для создания параллельных программ для параллельных вычислительных систем с распределенной памятью (кластеров):

  • -cluster-openmp - эта настройка позволяет компилятору создавать многопоточные версии программ с использованием директив расширенной версии OpenMP - Cluster OpenMP;
  • -cluster-openmp-profile - эта настройка добавляет в создаваемую программу с применением Cluster OpenMP средства профилирования программы для последующего анализа с помощью программы VTune Performance Analyzer;
  • -[no-]clomp-sharable-propagation - эта настройка позволяет создать отчет со списком переменных, которые должны быть объявлены программистом общедоступными для всех узлов кластера;
  • -[no-]clomp-sharable-info - эта настройка позволяет создать отчет со списком переменных, которые были автоматически объявлены компилятором общедоступными для всех узлов кластера. Далее рассмотрим, как применить эти настройки для создания параллельных программ для вычислительных систем с распределенной памятью.
  • Во-первых, отметим, что для компиляции программ prog.c и prog.f, написанных соответственно на языках C/C++ и Fortran с использованием Cluster OpenMP, можно воспользоваться соответственно следующими командами:

    icc -cluster-openmp prog.c -o prog

    и

    ifort -cluster-openmp prog.f -o prog

    В результате будут созданы выполняемые файлы с именами prog. Однако чтобы запустить программу prog на выполнение с помощью команды

    ./prog

    необходимо, чтобы в директории, откуда запускается программа, содержался файл kmp_cluster.ini. Если же в этой директории этот файл отсутствует, то операционная система ищет файл . kmp_cluster в директории с именем, определенным значением переменной окружения KMP_CLUSTER_PATH. Если же эта переменная окружения отсутствует или не определена, то операционная система ищет . kmp_cluster файл в домашнем каталоге пользователя. В файле kmp_cluster.ini и его аналогах перечисляются узлы кластера, на которых выполняется задание, и количество параллельных потоков на узлах. В следующем примере 7.1 представлена распечатка простейшего kmp_cluster.ini -файла.

    [tt1@hpc1 clomp_samples] $ cat kmp_cluster.ini
    --hostlist = hpc1,hpc2,hpc3,hpc4 --process_threads=2
    [tt1@hpc1 clomp_samples]$

    В этом файле после ключевого слова hostlist следует список узлов кластера, а после ключевого слова process_threads - число физических параллельных потоков в узле (оно равно числу процессоров в узле кластера).

    Теперь рассмотрим вопрос использования третьей из вышеперечисленных четырех настроек для перенастройки OpenMP-программ под Cluster OpenMP.

    Перед тем как начать перенастройку, следует убедиться, что программа корректно работает под OpenMP. Затем следует оттранслировать и собрать программу с настройкой -cluster-openmp. Если после этого программа будет работать корректно, то можно считать, что перенастройка успешно завершена. В противном случае следует продолжить процесс перенастройки следующим образом.

    Во-первых, надо определить переменные программы, которые необходимо сделать общими для всех узлов кластера. Эти общие переменные имеют тип sharable. Для их описания в Cluster OpenMP имеются специальные предложения следующего вида:

    #pragma intel omp sharable(var1, var2, ..., varN)

    в программах, написанных на языке C/C++, и

    !dir$ omp sharable(var1, var2, ..., varN)

    в программах, написанных на языке Fortran. Здесь var1, var2, ..., varN - список переменных, которые объявлены общими для всех узлов кластера. В программах на языке Fortran в качестве переменных могут быть использованы имена common-блоков. При этом при описании с помощью предложения Cluster OpenMP sharable имена common-блоков должны быть заключены в слэши, как показано ниже:

    !dir$ omp sharable( /cblock1/, /cblock2/, ..., /cblockN/ )

    Важно отметить, что переменные, которые применяются в описаниях EQUIVALENCE в программах, написанных на языке Fortran, не могут быть объявлены как sharable.

    Для определения sharable -переменных в программе, написанной с OpenMP, можно воспользоваться следующей настройкой компиляторов Intel:

    -clomp-sharable-propagation

    Эту настройку следует использовать обязательно вместе с настройкой - ipo. В результате компиляции с такой настройкой OMP-программ могут появиться сообщения о необходимости определения sharable -переменных. Пример такого сообщения показан в примере 7.2.

    fortcom: Warning: Sharable directive should be inserted by user
    as '!dir$ omp sharable(s)' in file p2.f, line 2, column 18

    Из этого примера видно, что сообщение компилятора указывает конкретное место в программе, где должны быть определены общие для всех узлов кластера переменные, а также Cluster OpenMP-директиву, которую следует использовать для определения имен переменных В программах на языке C++ наряду с предложениями sharable необходимо добавить включение ссылки на файл, в котором описаны переменные sharable. Эта ссылка имеет следующий вид:

    #include <kmp_sharable.h>

    На втором этапе перенастройки программы под Cluster OpenMP следует в файлах программы расставить необходимые директивы для определения общих для всех узлов кластера переменных, после чего необходимо перекомпилировать и пересобрать программу и убедиться в ее корректной работе.

    Отметим, что предложение sharable - единственное новое дополнительное предложение, отличающее Cluster OpenMP от стандартного OpenMP. Однако в Cluster OpenMP имеется значительное число дополнительных специфических настроек, таких, как kmp_cluster.ini -файл и т. п., которые и рассмотрим далее. Начнем с более подробного изучения настроек в kmp_cluster.ini -файле.

    Дополнительные настройки в kmp_cluster.ini-файле

    В этом разделе рассмотрим более подробно настройки, которые можно задавать в kmp_cluster.ini -файлах. Настройки --hostlist и --process_threads уже были рассмотрены ранее. Отметим лишь, что по умолчанию значение process_threads равно 1, а список hostlist по умолчанию тождественен узлу кластера, на котором в данный момент осуществляется работа.

    Настройка --omp_num_threads в файле kmp_cluster.ini позволяет определить число потоков OpenMP, т. е. значение переменной окружения OMP_NUM_THREADS. По умолчанию значение omp_num_threads равно произведению значений настроек

    processes * process_threads

    Настройка --processes позволяет задавать число параллельных процессов. По умолчанию оно принимает значение, либо равное числу узлов в списке hostlist, либо, если определено значение настройки omp_num_threads, - равное частному

    omp_num_threads / process_threads

    Отметим, что если значение omp_num_threads не равно значению произведения processes * process_threads, то следует переопределить настройку process_threads как omp_num_threads / processes.

    Настройка --transport определяет протокол связи между узлами кластера. Эта настройка по умолчанию принимает значение --transport=tcp, что соответствует протоколу TCP IP. Кроме того, допустимо значение --transport=dapl. В этом случае настройка --adapter должна быть определена следующим образом:

    adapter=Openib-ib0

    В последнем случае в команду компиляции программы должны быть добавлены настройки примерно следующего вида: - L/usr/local/ibgd/lib и -ldat, первая из которых определяет путь к библиотеке, а вторая - саму библиотеку dat.

    Настройка --launch определяет метод запуска Cluster OpenMP-программы на удаленных узлах кластера. Она может принимать два возможных значения - rsh или ssh ; первое значение является тем значением, которое эта настройка принимает по умолчанию.

    Настройка --sharable_heap определяет число страниц, выделяемых под общую для всех узлов кластера память. По умолчанию ее значение равно 16384.

    Настройка --suffix позволяет определить суффикс, который будет добавлен слева к именам узлов кластера. Эта настройка полезна в случае наличия различных типов межузловых соединений в кластере. По умолчанию suffix имеет значение null.

    Для задания задержки при запуске удаленных процессов можно воспользоваться настройкой --startup_timeout. Значение этой настройки задается в секундах, и по умолчанию оно равно 30. Если процесс не успевает стартовать за заданное время, то выполнение программы прерывается.

    Настройка --IO={system | debug | files} позволяет переопределять выходные потоки stderr и stdout следующим образом. Настройка по умолчанию system определяет пути к файлам stderr и stdout в соответствии с правилами оболочки shell. В случае debug происходит переадресация потоков stderr и stdout с удаленных узлов на рабочий узел кластера, при этом для идентификации файлов, соответствующих удаленным процессам, в их имена добавляется префикс Process x:, где x - номер удаленного процесса. В случае files осуществляется переадресация файлов stderr и stdout в файлы clomp-<process_id>-stdout и clomp-<process_id>-stderr соответственно, где process_id есть идентификатор процесса.

    Настройка --[no]heartbeat позволяет включить или выключить механизм контроля того, что все запущенные процессы функционируют нормально. По умолчанию устанавливается настройка --heartbeat.

    Для задания имени директории, в которой размещается файл обмена страниц динамической, общей для всех процессоров памяти, в kmp_cluster.ini -файле имеется специальная настройка --back_store= <имя директории обмена страниц памяти>. По умолчанию обмен страниц памяти осуществляется в директории /tmp.

    Наконец, последняя настройка --[no-]divert_twins предназначена для резервирования двух страниц в директории обмена страниц для файла обмена страниц памяти. По умолчанию эта настройка имеет значение --no-divert_twins, соответствующее отключению режима резервирования.

    В заключение отметим, что в kmp_cluster.ini -файле строго запрещается использовать переменные PATH, SHELL и LD_LIBRARY_PATH.

    Дополнительные настройки компилятора Fortran в Cluster OpenMP

    В программах, написанных на языке Fortran, имеются дополнительные настройки компилятора для работы и анализа переменных, размещенных в общей для всех узлов кластера памяти. Отметим, что компиляторы Fortran компании Intel, начиная с версии 9.1.xx, по умолчанию определяют как sharable переменные программы, содержащиеся в common-блоках, а также переменные программных модулей, локальные переменные типа SAVE и временные переменные для вычисления выражений в вызовах функций и подпрограмм. Для изменения этого режима определения по умолчанию в Intel Fortran компиляторе имеются четыре дополнительные настройки.

    Настройка -[no]-clomp-sharable-common позволяет отключить или включить режим определения по умолчанию переменных common-блоков как sharable.

    Для отключения или включения режима определения по умолчанию локальных переменных типа SAVE как sharable следует использовать настройку -[no]-clomp-sharable-localsaves.

    Настройка -[no]-clomp-sharable-modvars позволяет отключить или включить режим определения по умолчанию переменных программных модулей как sharable.

    Для отключения или включения режима определения по умолчанию временных переменных для вычисления выражений в вызовах функций и подпрограмм как sharable следует использовать настройку -[no]-clompsharable-argexprs.

    В заключение этого раздела отметим, что по умолчанию все четыре вышеперечисленные настройки устанавливаются с префиксом no, т. е. в выключенном режиме.

    Переменные окружения Cluster OpenMP

    При программировании с использованием Cluster OpenMP можно определить ряд переменных окружения, позволяющих контролировать и изменять процессы выполнения программ.

    Для задания размера стеков потоков в Cluster OpenMP можно воспользоваться переменной окружения KMP_STACKSIZE. Размер стека задается в килобайтах (K) или мегабайтах (M). По умолчанию задается KMP_STACKSIZE=1M. Определять размер стека можно не только для отдельных параллельных потоков, но и для общих переменных типа sharable. В последнем случае следует определить переменную окружения KMP_SHARABLE_STACKSIZE. По умолчанию значение этой переменной также равно 1M.

    Переменная окружения KMP_STATSFILE предназначена для определения имени файла, в который будет выводиться статистическая информация при профилировании программы, созданной с настройкой -cluster-openmp-profile. По умолчанию KMP_STATSFILE=guide.gvs.

    При отладке Cluster OpenMP программ необходимо задать имя отладчика. Для этого имеется специальная переменная окружения KMP_CLUSTER_DEBUGGER. Эта переменная может принимать значения idb (отладчик Intel), gdb (отладчик GNU) или totalview (отладчик TotalView). Значение по умолчанию для этой переменной окружения отсутствует.

    Переменная окружения KMP_WARNINGS позволяет включить ( 1 или on ) или выключить ( 0 или off ) режим вывода предупреждающих сообщений библиотеки выполнения ( runtime library ) Cluster OpenMP. По умолчанию задается KMP_WARNINGS=on.

    Для задания режима вывода предупреждающих сообщений об общих для всех узлов кластера переменных в Cluster OpenMP имеется переменная окружения KMP_SHARABLE_WARNINGS. Она также может принимать значения "включить" ( 1 или on ) или "выключить" ( 0 или off ) режим вывода предупреждающих сообщений. Но по умолчанию задается режим KMP_SHARABLE_WARNINGS=off.

    Переменная окружения KMP_CLUSTER_SETTINGS позволяет организовать вывод на экран всех переменных окружения, в том числе определенные в kmp_cluster.ini -файле, а также настройки, заданные в этом же файле. Для этой переменной отсутствует значение по умолчанию.

    Переменная окружения KMP_CLUSTER_PATH определяет путь к kmp_cluster.ini -файлу, если этот файл отсутствует в разделе, из которого запускается программа. По умолчанию это путь к домашнему разделу каталога пользователя.

    Для вывода информации об использовании настроек в kmp_cluster.ini -файле следует задать переменную окружения KMP_CLUSTER_HELP. Еще одна сервисная переменная окружения KMP_VERSION позволяет выводить информацию о версии в процессе работы библиотеки выполнения (run-time library).

    Наконец, последняя переменная окружения KMP_DISJOINT_HEAP задает размер несимметрично распределенной по процессам динамической памяти в килобайтах (K) или мегабайтах (M). Минимальный размер несимметрично распределенной по процессам динамической памяти равен 2K. Значение по умолчанию этой переменной не определено.

    Особенности реализации переменных окружения OpenMP в Cluster OpenMP

    В этом разделе рассматриваются особенности реализации переменных окружения OpenMP в среде Cluster OpenMP.

    В Cluster OpenMP максимальное число параллельных потоков в параллельной области программы определяется заданием настройки --omp_num_threads. Однако число параллельных потоков может быть и меньше максимального, если оно определено с помощью задания переменной окружения OMP_NUM_THREADS или с помощью вызова функции omp_set_num_threads() из библиотеки выполнения (runtime library).

    Число процессоров, которое возвращает функция omp_get_num_procs(),

    - это сумма процессоров, вычисленная по всем узлам вычислительной системы. Cluster OpenMP не поддерживает вложенный параллелизм. Поэтому вложенные параллельные области выполняются как последовательность потоков, причем на том же процессоре, на котором выполняется их родительский поток. Таким образом, вызов функции omp_set_nested или задание переменной окружения OMP_NESTED не имеют никакого влияния.

    При задании переменных окружения, определяющих загрузку вычислительной системы, следует иметь в виду, что если переменная окружения OMP_SCHEDULE не определена, то это означает, что по умолчанию задается статический ( static ) режим загрузки параллельных процессов.

    Специальные функции Cluster OpenMP

    В Cluster OpenMP имеется целый ряд специальных функций для задания некоторых переменных окружения в процессе выполнения программы, а также для выделения и освобождения памяти в процессе работы программы и передачи сигналов. Рассмотрение этих функций начнем с рассмотрения функций задания переменных окружения и функций, информирующих об окружающей среде.

    Функции

    void kmp_set_warnings_on ( void )

    и

    void kmp_set_warnings_off ( void )

    предназначены для задания режима вывода предупреждающих сообщений в процессе работы библиотеки выполнения. Первая функция устанавливает переменную окружения KMP_WARNINGS=on, а вторая - KMP_WARNINGS=off отменяет ее.

    Следующая функция

    omp_int_t kmp_get_process_num ( void )

    возвращает номер текущего выполняемого процесса. Функция

    omp_int_t kmp_num_processes ( void )

    возвращает общее число потоков в текущий момент времени. Следующая функция

    omp_int_t kmp_get_process_thread_num ( void )

    возвращает номер текущего потока в выполняемом процессе.

    Теперь перейдем к рассмотрению функций передачи сигналов в Cluster OpenMP. Всего таких функций шесть.

    Первая функция

    void kmp_lock_cond_wait ( omp_lock_t *lock )

    предназначена для приостановки выполнения процесса, содержащего данный оператор, до момента разблокировки объекта, блокировка которого установлена переменной lock.

    Вторая функция

    void kmp_lock_cond_signal ( omp_lock_t *lock )

    посылает сигнал о блокировке объекта, установленной с помощью переменной lock.

    Третья функция

    void kmp_lock_cond_broadcast ( omp_lock_t *lock )

    рассылает всем процессам сигнал о блокировке объекта, установленной с помощью переменной lock.

    Четвертая функция

    void kmp_nest_lock_cond_wait ( omp_nest_lock_t *lock )

    предназначена для приостановки выполнения процесса, содержащего данный оператор, до момента разблокировки объекта, вложенная блокировка которого установлена переменной lock.

    Пятая функция

    void kmp_nest_lock_cond_signal ( omp_nest_lock_t *lock )

    посылает сигнал о вложенной блокировке объекта, установленной с помощью переменной lock.

    Наконец, шестая функция

    void kmp_nest_lock_cond_broadcast ( omp_nest_lock_t *lock )

    рассылает всем процессам сигнал о вложенной блокировке объекта, установленной с помощью переменной lock.

    Для работы с памятью в Cluster OpenMP имеется девять специальных функций. Далее рассмотрим эти функции.

    Функция

    void *kmp_sharable_malloc ( size_t size )

    предназначена для выделения динамической, общей для всех узлов кластера памяти. Объем выделяемой памяти определяется значением переменной size.

    Следующая функция

    void *kmp_aligned_sharable_malloc ( size_t size )

    предназначена для выделения динамической, общей для всех узлов кластера памяти. Это выделение осуществляется с выравниванием по границе страницы памяти. Объем выделяемой памяти определяется значением переменной size.

    Функция

    void *kmp_sharable_сalloc ( size_t n, size_t size )

    предназначена для выделения в динамической, общей для всех узлов кластера памяти места под массив из n элементов размера size. При этом все элементы этого массива заполняются нулями.

    Еще одна функция

    void *kmp_sharable_reсalloc ( void *ptr, size_t size )

    предназначена для переинициализации выделения места в динамической, общей для всех узлов кластера памяти под массив размера size. Адрес этого места определяется указателем *ptr. При этом все элементы этого массива заполняются нулями.

    Функция

    void *kmp_sharable_free ( void *ptr )

    предназначена для освобождения в динамической, общей для всех узлов кластера памяти раздела, определенного указателем *ptr.

    Следующие функции

    void *kmp_private_mmap (char *file,size_t *len,void **addr)

    и

    void *kmp_private_munmap ( void *start )

    предназначены для отображения файлов в области индивидуальной адресной памяти процессов, начиная с одного и того же адреса. Первая функция kmp_private_mmap отображает file длиной len байт в память, начиная с адреса addr. Вторая функция kmp_private_munmap удаляет все отображения из заданной области памяти с начальным адресом start, после чего все ссылки на данную область будут вызывать ошибку "неправильное обращение к памяти". Отображение удаляется автоматически при завершении процесса. Однако закрытие файла не приводит к снятию отображения. Эти функции возвращают 0 в случае успешного завершения и -1 в противном случае. Отметим, что их можно применять только в последовательной области программы. В параллельной области они не поддерживаются. Кроме того, эти две функции имеют тип read-only, т.е. после удаления отображения с помощью второй функции содержимое файла не обновляется содержимым области памяти.

    Функции

    void *kmp_sharable_mmap ( char *file, size_t *len, void **addr )

    и

    void *kmp_sharable_munmap ( void *start )

    предназначены для отображения файлов в области адресной памяти главного процесса, начиная с одного и того же адреса. Первая функция kmp_sharable_mmap отображает file длиной len байт в память, начиная с адреса addr. Вторая функция kmp_sharable_munmap удаляет все отображения из заданной области памяти с начальным адресом start, после чего все ссылки на данную область будут вызывать ошибку "неправильное обращение к памяти". Отображение удаляется автоматически при завершении процесса. Функции возвращают 0 в случае успешного завершения и - 1 в противном случае. Эти две функции имеют тип read-write, т. е. после удаления отображения с помощью второй функции содержимое файла обновляется содержимым области памяти. Отметим также, что их можно применять только в последовательной области программы. В параллельной области их использовать нельзя.

    Загрузка данных в общую для всех узлов кластера память в Cluster OpenMP

    При рассмотрении проблемы загрузки данных в общую ( sharable ) для всех узлов кластера память прежде всего следует иметь в виду, что наименьший размер элементов, с которыми оперирует Cluster OpenMP, равен 4 байтам. Если же размер элементов по каким-то причинам оказывается меньше 4 байт, то операции с ними не производятся. Таким образом, если в программе, например, определены массивы строковых переменных

    char a[100], b[100]

    а затем эти переменные определены как sharable, то следующий параллельный блок в программе выполняться не будет:

    #pragma omp parallel for 
    for( i=0; i<N; i++ ) 
     { 
       a[i] = b[i]; 
     }

    Для динамической загрузки переменных в общую ( sharable ) для всех узлов кластера память в программах, написанных на языке C/C++, можно воспользоваться одной из двух следующих функций:

    void * kmp_sharable_malloc ( int size )

    или

    void * kmp_aligned_sharable_malloc ( int size )

    Обе эти функции определяют в динамическом режиме в общей для всех узлов кластера памяти области заданного размера и возвращают адреса этих областей. При этом следует иметь в виду, что вторая из этих функций осуществляет выравнивание выделяемой области памяти по границе страницы и тем самым существенно ускоряет процесс обращения к памяти. Для освобождения выделенной памяти следует использовать функцию

    void kmp_sharable_free ( *ptr )

    Здесь ptr - указатель на освобождаемую область памяти.

    Динамическое выделение памяти в общей ( sharable ) для всех узлов кластера памяти в программах, написанных на языке Fortran, осуществляется следующим образом:

    integer, allocatable :: x( : ) 
    !dir$ omp sharable (x) 
        allocate( x( 500 ) )

    В этом примере в общей для всех узлов кластера памяти динамически размещается целочисленный массив, состоящий из 500 элементов.

    В программах, написанных на языке C++, при динамическом размещении переменных и объектов в общей для всех узлов кластера памяти необходимо добавить включение ссылки на файл, в котором описаны переменные sharable. Эта ссылка имеет следующий вид:

    #include <kmp_sharable.h>

    Теперь рассмотрим вопрос создания динамических объектов в общей для всех узлов кластера памяти. Пусть в исходном тексте программы динамически определяется объект класса zoo, например, следующим образом:

    zoo * fp = new zoo (10);

    В Cluster OpenMP это определение модифицируется так:

    zoo * fp = new kmp_sharable zoo (10);

    Теперь рассмотрим вопрос создания класса объектов, динамически размещаемых в общей для всех узлов кластера памяти. Пусть в исходной программе этот класс создавался, например, следующим образом:

    class zoo : public zoo_base 
     { 
      // …содержание класса zoo 
     };

    В программе, написанной с использованием Cluster OpenMP, создание этого класса осуществляется следующим образом:

    class zoo : public zoo_base, public kmp_sharable_base 
     { 
      // …содержание класса zoo 
     };

    При динамическом создании STL (Standard Template Library) контейнеров в общей для всех узлов кластера памяти следует иметь в виду, что, во-первых, необходимо описать динамическое размещение контейнера как объекта и, кроме того, необходимо описать динамическое размещение его содержания. Если в исходной программе контейнер создавался, например, следующим образом:

    std::vec<int> * vecp = new std :: vec<int>;

    то в программе, написанной с использованием ClusterOpenMP, создание такого контейнера должно быть описано, например, так:

    std::vec<int,kmp_sharable_allocator<int>>*vecp=new 
    kmp_sharable std :: vec<int, kmp_sharable_allocator<int>>;

    Настройки отладчиков в Cluster OpenMP

    Для отладки многопоточных параллельных программ в Cluster OpenMP можно использовать как бесплатный отладчик GNU gdb, так и лицензионные отладчики idb компании Intel и TotalView компании Etnus. Однако для того, чтобы корректно пользоваться этими отладчиками, необходимо сделать специальные настройки в системных файлах. Далее рассмотрим подробно эти настройки применительно ко всем вышеперечисленным отладчикам. Предварительно отметим, что для корректной работы всех этих отладчиков необходимо отключить прерывание по сигналу ошибки адресации SIGSEGV (Signal Segmentation Violation). Причина в том, что этот сигнал используется для внутренних нужд Cluster OpenMP, поэтому он не является аварийным и не несет информации об ошибках доступа к памяти (в Cluster OpenMP имеется свой собственный обработчик этого сигнала). Отключение этого сигнала производится по-разному для рассматриваемых отладчиков и будет рассмотрено далее. Для всех отладчиков в kmp_cluster.ini-файле необходимо использовать настройку --no-heartbeat, отключающую режим аварийного завершения параллельных потоков при отсутствии в течение определенного промежутка времени ответных сигналов от параллельных потоков. Такими сигналами в Cluster OpenMP постоянно обмениваются все параллельные потоки. Если в течение определенного промежутка времени ответный сигнал от какого-либо потока отсутствует, то это считается свидетельством его аварийного завершения. В этом случае происходит аварийное завершение всех потоков рассматриваемой задачи.

    В бесплатном отладчике GNU gdb для отключения сигнала аварийного доступа к памяти, во-первых, необходимо в системном файле .gdbinit, находящемся в домашнем разделе пользователя, добавить строку

    handle SIGSEGV nostop noprint

    Во-вторых, необходимо определить отладчик с помощью переменной окружения KMP_CLUSTER_DEBUGGER, например следующим образом:

    export KMP_CLUSTER_DEBUGGER=gdb

    В-третьих, необходимо определить переменную окружения DISPLAY, указывающую на IP-адрес компьютера, с которого стартует X Windows. Эту переменную можно определить либо в kmp_cluster.ini -файле, например так:

    DISPLAY= 10.0.6.191:0

    либо с помощью команды

    export DISPLAY= 10.0.6.191:0

    которую следует поместить в системный файл, откуда считываются переменные окружения при старте программы xterm. После запуска отладчика gdb следует набрать команду

    break __itmk_segv_break

    для перехвата ошибок адресации памяти.

    В отладчике idb компании Intel для отключения сигнала аварийного доступа к памяти необходимо в системном файле .dbxinit, находящемся в домашнем разделе пользователя, добавить строку

    ignore segv

    Во-вторых, необходимо определить отладчик с помощью переменной окружения KMP_CLUSTER_DEBUGGER следующим образом:

    export KMP_CLUSTER_DEBUGGER=idb

    Переменная окружения DISPLAY для этого отладчика задается так же, как и для отладчика gdb. После запуска отладчика idb следует набрать команду

    stop in __itmk_segv_break

    для перехвата ошибок адресации памяти.

    В отладчике TotalView компании Etnus для отключения сигнала аварийного доступа к памяти необходимо в системном файле .tvdrc, находящемся в домашнем разделе пользователя, добавить строку

    dset TV::signal_handling_mode { Resend=SIGSEGV }

    После запуска отладчика TotalView следует набрать команду

    breakpoint in __itmk_segv_break

    для перехвата ошибок адресации памяти.

    Настройка динамической памяти при отладке в Cluster OpenMP

    При использовании динамической памяти в программах с использованием Cluster OpenMP возможна следующая ошибочная ситуация: некоторые данные одного типа в параллельных потоках размещены в локальной памяти с одними и теми же адресами (т. е. некоторые данные из параллельных потоков симметрично размещены в локальной памяти). При этом указатель на эти данные в одном из параллельных потоков ошибочно передается в другой поток, и из этого последнего потока осуществляется доступ к данным по переданному адресу. В таком случае ошибки доступа к данным может и не возникнуть, т. е. сигнал SIGSEGV не будет инициирован. Однако данные, которые будут извлечены из другого потока, будут неверными для исходного потока. Для

    того чтобы выявить такие ошибочные ситуации, в Cluster OpenMP реализован

    механизм несимметричного выделения динамической памяти (Disjoint Heap).

    Для включения механизма несимметричного выделения динамической памяти в параллельных потоках следует задать переменную окружения KMP_DISJOINT_HEAPSIZE, например, следующим образом:

    export KMP_DISJOINT_HEAPSIZE 128M

    Здесь была задана область для несимметричного выделения динамической памяти, равная 128 мегабайтам. При выделении размера этой области следует позаботиться о том, чтобы ее размер был не меньше, чем произведение размера области динамической памяти, выделяемой для одного параллельного потока, на число параллельных потоков. В этом случае области динамической памяти в параллельных потоках будут размещены в адресном пространстве без пересечений и наложений. Тогда обращение в каком-либо из параллельных потоков к области динамической памяти по указателю, переданному из другого потока, приведет к ошибке доступа к данным и возникновению сигнала SIGSEGV. Это и будет побудительной причиной более тщательного анализа работы с данными в параллельных потоках для устранения таких некорректных ситуаций.

    В заключение отметим, что по умолчанию значение переменной окружения KMP_DISJOINT_HEAPSIZE задается равным 2 мегабайтам. При задании меньшей величины размер KMP_DISJOINT_HEAPSIZE автоматически увеличивается до 2 мегабайт.

    Подчеркнем еще раз, что задание переменной окружения KMP_DISJOINT_HEAPSIZE необходимо только на этапе отладки программы для поиска ошибок некорректного обращения к динамической памяти в параллельных потоках. После выявления всех таких ошибок и создания окончательной оптимизированной версии программы эту переменную окружения следует удалить.

    Сводка переменных окружения Cluster OpenMP

    В предыдущих разделах был уже рассмотрен ряд специфических переменных окружения Cluster OpenMP. Ниже перечислены все переменные окружения, допустимые в Cluster OpenMP, и описаны способы их применения.

  • Переменная окружения KMP_STACKSIZE задает объем стека в параллельных потоках Custer OpenMP. Величина этой переменной задается в килобайтах (К) или мегабайтах (М). По умолчанию значение этой переменной равно .
  • Переменная окружения KMP_SHARABLE_STACKSIZE задает объем стека для размещения sharable (общих для всех узлов кластера) данных для OpenMP потоков. Величина этой переменной задается в килобайтах (К) или мегабайтах (М). По умолчанию значение этой переменной равно .
  • Переменная окружения KMP_STATSFILE задает имя файла статистики при использовании настройки -cluster-openmp-profile. По умолчанию имя этого файла guide.gvs.
  • Задание значения переменной окружения KMP_WARNINGS выключает ( 0 или off ) или включает ( 1 или on ) выдачу сообщений библиотеки выполнения (run-time library) Cluster OpenMP. По умолчанию значение этой переменной равно 1 или on.
  • Задание значения переменной окружения KMP_WARNINGS выключает ( 0 или off ) или включает ( 1 или on ) выдачу предупреждающих сообщений о переменных в параллельных потоках, которые должны быть общими для всех узлов кластера ( sharable ), но не являются таковыми. По умолчанию значение этой переменной равно 0 или off.
  • Переменная окружения KMP_CLUSTER_SETTINGS задает режим вывода переменных окружения и настроек, определенных в kmp_cluster.ini файле. Значения по умолчанию для этой переменной нет.
  • Переменная окружения KMP_CLUSTER_PATH задает путь к kmp_cluster.ini -файлу, если он отсутствует в текущем разделе. По умолчанию в качестве этого пути задается путь к домашнему разделу пользователя.
  • Задание переменной окружения KMP_CLUSTER_HELP позволяет выводить описание настроек, допустимых в kmp_cluster.ini -файле. Значения по умолчанию для этой переменной нет.
  • Переменная окружения KMP_CLUSTER_DEBUGGER задает имя исполняемого файла отладчика (например, idb для отладчика Intel). Значения по умолчанию для этой переменной нет.
  • Переменная окружения KMP_CLUSTER_VERSION задает режим вывода сообщений о версии системы. Значения по умолчанию для этой переменной нет.
  • Переменная окружения KMP_DISJOINT_HEAPSIZE задает значение размера динамической памяти в параллельных потоках и включает механизм Disjoint Heap. Величина этой переменной задается в килобайтах (К) или мегабайтах (М). По умолчанию значение этой переменной равно . Если заданное значение меньше , то оно автоматически увеличивается до .
  • На этом завершается знакомство c Cluster OpenMP. Отметим, что для изучения дополнительных материалов, оставшихся за рамками данного изложения, следует обратиться к руководству [7.9].

    Страницы:

    Как уже отмечалось в предыдущих разделах, средства автоматизированного распараллеливания следует использовать на первом этапе разработки параллельной программы, если у нее уже существует работающий последовательный аналог. В этом случае на первом этапе разработки параллельной программы рекомендуется применить средства автоматизированного распараллеливания к исходной последовательной программе.

    В настоящее время все основные компиляторы Fortran и C/C++, предназначенные для разработки параллельных программ с использованием OpenMP, имеют возможности автоматического распараллеливания. Кроме того, эти компиляторы допускают установку различных уровней автоматического распараллеливания, а также генерируют отчеты по результатам распараллеливания.

    После автоматического распараллеливания рекомендуется провести профилирование программы, и если окажется, что наиболее трудоемкие места хорошо распараллелены, то работу по созданию параллельной версии программы можно закончить. В противном случае рекомендуется воспользоваться средствами OpenMP для дальнейшего распараллеливания наиболее трудоемких участков программы. Обычно так и происходит, поскольку для сложных программ с помощью автоматического распараллеливания редко удается получить максимальный эффект. Чтобы четко разобраться в этом вопросе, далее рассмотрим некоторые основные средства автоматизированного распараллеливания и принципы их работы.

    Основные средства автоматизированного распараллеливания и принципы их работы

    Основным объектом автоматизированного распараллеливания с помощью специальных настроек компиляторов являются циклы. При распараллеливании циклов компилятор, во-первых, выделяет независимые по данным петли циклов. Во-вторых, он оценивает приблизительный эффект от распараллеливания цикла. Если оценка этого эффекта соответствует заданному уровню, то компилятор определяет локальные переменные. И, наконец, только затем производится само распараллеливание цикла с помощью библиотек процессов. По результатам проведенной или не проведенной работы выдается сообщение в файл отчета о распараллеливании с объяснением причин.

    Как правило, других возможностей автоматизированного распараллеливания большинство компиляторов не имеет. Все это в полной мере относится к компиляторам Fortran и C/C++ компании Intel и некоторым другим. Как видим, вышеперечисленные возможности автоматизированного распараллеливания сравнительно невелики.

    Отметим, что значительно более широкими возможностями в области автоматического распараллеливания обладает компилятор Fortran 95 для операционной системы Linux компании Lahey, известный под названием Lahey/Fujitsu Fortran 95 for Linux. В этом компиляторе значительно улучшен процесс оптимизации программ, а также обеспечивается поддержка аппаратных функций новых процессоров. Кроме того, поддерживается автоматическое распараллеливание и дополнительные инструкции SSE2 для процессоров Intel Xeon и Pentium IV. Компилятор Fortran Lahey/Fujitsu занимает одну из лидирующих позиций в областях, где необходимы большие объемы математических вычислений и осуществляется работа с большими массивами. Этот компилятор осуществляет автоматическое распараллеливание программ, в том числе и с использованием OpenMP версии 2.0. Оптимизация программ осуществляется для процессоров как компании Intel, так и компании AMD, при этом поддерживается оптимизация конвейера упреждающей выборки. Кроме того, для отладки параллельных программ компилятор совместим с параллельным отладчиком TotalView.

    Кроме компиляторов существуют и более продвинутые программные системы в области автоматизированного распараллеливания.

    В качестве примера отметим программный продукт Bert77, разрабатываемый компанией Paralogic. Программа Bert77 автоматически распараллеливает Fortran-программы в средах PVM или MPI. Распараллеливание осуществляется с использованием механизма обмена сообщениями. Программа Bert77 - это коммерческий продукт, однако существует и его упрощенная бесплатная версия (lite-версия).

    В подразделении компании Intel - KAI Software Lab (бывшая Kuck Associates Inc.) разработаны и продолжают совершенствоваться:

  • KAP/Pro Toolset - набор программных средств для распараллеливания больших расчетных программ на параллельных вычислительных системах с общей памятью;
  • KAI C++ - компилятор С/C++ с широкими возможностями оптимизации и распараллеливания;
  • Visual KAP - программа автоматического распараллеливания Fortran-программ в режиме визуального диалога;
  • Visual KAP для OpenMP - инструмент визуальной генерации программ с использованием OpenMP.
  • Компания Applied Parallel Research Inc. предлагает программу spf, автоматически распараллеливающую программы для параллельных вычислительных систем с общей памятью с применением OpenMP или POSIX.

    Компания Pacific-Sierra Research разработала интересные и очень эффективные средства:

  • VAST/Parallel - программу автоматического распараллеливания программ на языках Fortran и C/C++ для параллельных вычислительных систем с общей памятью с использованием OpenMP;
  • VAST/toOpenMP - программу, автоматически распознающую параллелизм и генерирующую директивы OpenMP в программах на языке Fortran.
  • Отметим также разработанную этой компанией среду проектирования, отладки и анализа производительности параллельных программ в системах с распределенной и общей памятью DEPP (Development Environment for Parallel Programs), реализующую директивы OpenMP.

    Существует и еще целый ряд других средств автоматизированного распараллеливания, однако здесь были перечислены лишь те средства, которые осуществляют автоматическое распараллеливание с использованием OpenMP.

    Автоматическое распараллеливание программ с помощью компиляторов Intel

    Рассмотрим возможности автоматического распараллеливания программ с применением современных компиляторов компании Intel. Отметим, что в компиляторах Intel реализована только одна принципиальная возможность распараллеливания: это создание многопоточных приложений распараллеливанием цикла с помощью библиотек процессов.

    Далее рассмотрим подробнее настройки режима автоматического распараллеливания, имеющиеся в компиляторах Fortran и С/C++ компании Intel:

  • -parallel - эта настройка позволяет компилятору автоматически создавать многопоточные версии программ с безопасным распараллеливанием циклов (подчеркнем, что кроме циклов в этом режиме больше ничего не распараллеливается);
  • -par_report{0|1|2|3} - эта настройка позволяет создавать отчеты различного уровня - 0, 1, 2 или 3 (наиболее подробный отчет имеет уровень 3) по результатам автоматического распараллеливания. Рекомендуется тщательно анализировать такие отчеты, чтобы четко понять, какие еще места программы могли бы быть распараллелены и почему это не было сделано. Возможно, компилятору не хватило информации для принятия решения о распараллеливании такого участка программы. В этом случае это можно сделать вручную;
  • -par_threshold[n] - эта настройка передает компилятору целое число n в диапазоне от 0 до 100. n является оценкой эффективности распараллеливания циклов в процентах. Эту оценку компилятор производит самостоятельно.
  • Настройки компиляторов Intel для распараллеливания программ с использованием OpenMP

    В предыдущем параграфе были рассмотрены возможности автоматического распараллеливания программ для параллельных вычислительных систем с общей памятью. Однако, как правило, в автоматическом режиме нельзя выбрать все имеющиеся возможности для эффективного распараллеливания программ. Поэтому для параллельных вычислительных систем с общей памятью используют средства OpenMP для более глубокого распараллеливания программ.

    Современные компиляторы Intel содержат следующие специальные настройки для создания параллельных программ с применением средств OpenMP для параллельных вычислительных систем с общей памятью:

  • -openmp - эта настройка позволяет компилятору автоматически создавать многопоточные версии программ с использованием директив OpenMP;
  • -openmp_profile - эта настройка добавляет в создаваемую программу средства профилирования программы для последующего анализа с помощью программы VTune Performance Analyzer;
  • -openmp_stubs - эта настройка позволяет компилировать OpenMP программы в последовательном режиме. При этом предложения OpenMP игнорируются, а библиотека OpenMP используется редактором связей в последовательном режиме;
  • -openmp_report{0|1|2} - эта настройка позволяет создавать отчеты различного уровня - 0, 1 или 2 (наиболее подробный отчет имеет уровень 2) по результатам автоматического распараллеливания с использованием OpenMP. Рекомендуется тщательно анализировать такие отчеты, чтобы четко понять, какие еще места программы могли бы быть распараллелены и почему это не было сделано. Возможно, компилятору не хватило информации для принятия решения о распараллеливании того или иного участка программы. В этом случае это можно сделать вручную.
  • Итак, вышеперечисленные настройки компиляторов позволяют создавать параллельные версии программ как просто с многопоточным распараллеливанием циклов, так и с помощью директив OpenMP. Возможно также сочетание обоих этих режимов распараллеливания.

    В заключение для полноты изложения приведем простейшие команды компиляции программ prog.c и prog.f с помощью компиляторов Intel:

    icc -openmp prog.c

    и

    ifort -openmp prog.f

    Здесь prog.c и prog.f - соответственно тексты программ, написанных на алгоритмических языках C/C++ и Fortran с применением директив OpenMP. Для программ, написанных на Fortran, компилятор распознает также расширения файлов с текстами программ for и f90 (для текстов программ, написанных на алгоритмическом языке Fortran 90). В результате компиляции в среде операционной системы Linux создается выполняемый файл a.out. Отметим также, что для создания выполняемых файлов с именем prog следует использовать следующие команды компиляции для программ, написанных на алгоритмических языках C/C++ и Fortran соответственно:

    icc -openmp prog.c -o prog

    и

    ifort -openmp prog.f -o prog

    Настройки компиляторов Intel для распараллеливания программ для кластеров с распределенной памятью

    В предыдущем параграфе были рассмотрены возможности автоматического распараллеливания программ, эффективные для параллельных вычислительных систем с общей памятью. При создании программ для параллельных вычислительных систем с распределенной памятью, как было отмечено ранее, применяются методы распараллеливания с использованием MPI и PVM. Однако в новой версии компиляторов Intel, начиная с версии 9.1, реализовано расширение OpenMP, предназначенное для распараллеливания программ для вычислительных систем с распределенной памятью. Это расширение известно под названием Cluster OpenMP. В нем имеется возможность объявлять области данных доступными для всех узлов кластера. Эта возможность реализуется с помощью предложения sharable, которое будет рассмотрено далее. Пока же отметим, что это предложение реализует неявную передачу данных между узлами кластера по протоколу Lazy Release Consistency и избавляет программистов от утомительного процесса анализа посылаемых и принимаемых сообщений, необходимого при использовании MPI. В результате процесс параллельного программирования для параллельных вычислительных систем с распределенной памятью существенно упрощается.

    Компиляторы Intel, начиная с версии 9.1.x.xx, получили следующие дополнительные настройки, предназначенные для создания параллельных программ для параллельных вычислительных систем с распределенной памятью (кластеров):

  • -cluster-openmp - эта настройка позволяет компилятору создавать многопоточные версии программ с использованием директив расширенной версии OpenMP - Cluster OpenMP;
  • -cluster-openmp-profile - эта настройка добавляет в создаваемую программу с применением Cluster OpenMP средства профилирования программы для последующего анализа с помощью программы VTune Performance Analyzer;
  • -[no-]clomp-sharable-propagation - эта настройка позволяет создать отчет со списком переменных, которые должны быть объявлены программистом общедоступными для всех узлов кластера;
  • -[no-]clomp-sharable-info - эта настройка позволяет создать отчет со списком переменных, которые были автоматически объявлены компилятором общедоступными для всех узлов кластера. Далее рассмотрим, как применить эти настройки для создания параллельных программ для вычислительных систем с распределенной памятью.
  • Во-первых, отметим, что для компиляции программ prog.c и prog.f, написанных соответственно на языках C/C++ и Fortran с использованием Cluster OpenMP, можно воспользоваться соответственно следующими командами:

    icc -cluster-openmp prog.c -o prog

    и

    ifort -cluster-openmp prog.f -o prog

    В результате будут созданы выполняемые файлы с именами prog. Однако чтобы запустить программу prog на выполнение с помощью команды

    ./prog

    необходимо, чтобы в директории, откуда запускается программа, содержался файл kmp_cluster.ini. Если же в этой директории этот файл отсутствует, то операционная система ищет файл . kmp_cluster в директории с именем, определенным значением переменной окружения KMP_CLUSTER_PATH. Если же эта переменная окружения отсутствует или не определена, то операционная система ищет . kmp_cluster файл в домашнем каталоге пользователя. В файле kmp_cluster.ini и его аналогах перечисляются узлы кластера, на которых выполняется задание, и количество параллельных потоков на узлах. В следующем примере 7.1 представлена распечатка простейшего kmp_cluster.ini -файла.

    [tt1@hpc1 clomp_samples] $ cat kmp_cluster.ini
    --hostlist = hpc1,hpc2,hpc3,hpc4 --process_threads=2
    [tt1@hpc1 clomp_samples]$

    В этом файле после ключевого слова hostlist следует список узлов кластера, а после ключевого слова process_threads - число физических параллельных потоков в узле (оно равно числу процессоров в узле кластера).

    Теперь рассмотрим вопрос использования третьей из вышеперечисленных четырех настроек для перенастройки OpenMP-программ под Cluster OpenMP.

    Перед тем как начать перенастройку, следует убедиться, что программа корректно работает под OpenMP. Затем следует оттранслировать и собрать программу с настройкой -cluster-openmp. Если после этого программа будет работать корректно, то можно считать, что перенастройка успешно завершена. В противном случае следует продолжить процесс перенастройки следующим образом.

    Во-первых, надо определить переменные программы, которые необходимо сделать общими для всех узлов кластера. Эти общие переменные имеют тип sharable. Для их описания в Cluster OpenMP имеются специальные предложения следующего вида:

    #pragma intel omp sharable(var1, var2, ..., varN)

    в программах, написанных на языке C/C++, и

    !dir$ omp sharable(var1, var2, ..., varN)

    в программах, написанных на языке Fortran. Здесь var1, var2, ..., varN - список переменных, которые объявлены общими для всех узлов кластера. В программах на языке Fortran в качестве переменных могут быть использованы имена common-блоков. При этом при описании с помощью предложения Cluster OpenMP sharable имена common-блоков должны быть заключены в слэши, как показано ниже:

    !dir$ omp sharable( /cblock1/, /cblock2/, ..., /cblockN/ )

    Важно отметить, что переменные, которые применяются в описаниях EQUIVALENCE в программах, написанных на языке Fortran, не могут быть объявлены как sharable.

    Для определения sharable -переменных в программе, написанной с OpenMP, можно воспользоваться следующей настройкой компиляторов Intel:

    -clomp-sharable-propagation

    Эту настройку следует использовать обязательно вместе с настройкой - ipo. В результате компиляции с такой настройкой OMP-программ могут появиться сообщения о необходимости определения sharable -переменных. Пример такого сообщения показан в примере 7.2.

    fortcom: Warning: Sharable directive should be inserted by user
    as '!dir$ omp sharable(s)' in file p2.f, line 2, column 18

    Из этого примера видно, что сообщение компилятора указывает конкретное место в программе, где должны быть определены общие для всех узлов кластера переменные, а также Cluster OpenMP-директиву, которую следует использовать для определения имен переменных В программах на языке C++ наряду с предложениями sharable необходимо добавить включение ссылки на файл, в котором описаны переменные sharable. Эта ссылка имеет следующий вид:

    #include <kmp_sharable.h>

    На втором этапе перенастройки программы под Cluster OpenMP следует в файлах программы расставить необходимые директивы для определения общих для всех узлов кластера переменных, после чего необходимо перекомпилировать и пересобрать программу и убедиться в ее корректной работе.

    Отметим, что предложение sharable - единственное новое дополнительное предложение, отличающее Cluster OpenMP от стандартного OpenMP. Однако в Cluster OpenMP имеется значительное число дополнительных специфических настроек, таких, как kmp_cluster.ini -файл и т. п., которые и рассмотрим далее. Начнем с более подробного изучения настроек в kmp_cluster.ini -файле.

    Дополнительные настройки в kmp_cluster.ini-файле

    В этом разделе рассмотрим более подробно настройки, которые можно задавать в kmp_cluster.ini -файлах. Настройки --hostlist и --process_threads уже были рассмотрены ранее. Отметим лишь, что по умолчанию значение process_threads равно 1, а список hostlist по умолчанию тождественен узлу кластера, на котором в данный момент осуществляется работа.

    Настройка --omp_num_threads в файле kmp_cluster.ini позволяет определить число потоков OpenMP, т. е. значение переменной окружения OMP_NUM_THREADS. По умолчанию значение omp_num_threads равно произведению значений настроек

    processes * process_threads

    Настройка --processes позволяет задавать число параллельных процессов. По умолчанию оно принимает значение, либо равное числу узлов в списке hostlist, либо, если определено значение настройки omp_num_threads, - равное частному

    omp_num_threads / process_threads

    Отметим, что если значение omp_num_threads не равно значению произведения processes * process_threads, то следует переопределить настройку process_threads как omp_num_threads / processes.

    Настройка --transport определяет протокол связи между узлами кластера. Эта настройка по умолчанию принимает значение --transport=tcp, что соответствует протоколу TCP IP. Кроме того, допустимо значение --transport=dapl. В этом случае настройка --adapter должна быть определена следующим образом:

    adapter=Openib-ib0

    В последнем случае в команду компиляции программы должны быть добавлены настройки примерно следующего вида: - L/usr/local/ibgd/lib и -ldat, первая из которых определяет путь к библиотеке, а вторая - саму библиотеку dat.

    Настройка --launch определяет метод запуска Cluster OpenMP-программы на удаленных узлах кластера. Она может принимать два возможных значения - rsh или ssh ; первое значение является тем значением, которое эта настройка принимает по умолчанию.

    Настройка --sharable_heap определяет число страниц, выделяемых под общую для всех узлов кластера память. По умолчанию ее значение равно 16384.

    Настройка --suffix позволяет определить суффикс, который будет добавлен слева к именам узлов кластера. Эта настройка полезна в случае наличия различных типов межузловых соединений в кластере. По умолчанию suffix имеет значение null.

    Для задания задержки при запуске удаленных процессов можно воспользоваться настройкой --startup_timeout. Значение этой настройки задается в секундах, и по умолчанию оно равно 30. Если процесс не успевает стартовать за заданное время, то выполнение программы прерывается.

    Настройка --IO={system | debug | files} позволяет переопределять выходные потоки stderr и stdout следующим образом. Настройка по умолчанию system определяет пути к файлам stderr и stdout в соответствии с правилами оболочки shell. В случае debug происходит переадресация потоков stderr и stdout с удаленных узлов на рабочий узел кластера, при этом для идентификации файлов, соответствующих удаленным процессам, в их имена добавляется префикс Process x:, где x - номер удаленного процесса. В случае files осуществляется переадресация файлов stderr и stdout в файлы clomp-<process_id>-stdout и clomp-<process_id>-stderr соответственно, где process_id есть идентификатор процесса.

    Настройка --[no]heartbeat позволяет включить или выключить механизм контроля того, что все запущенные процессы функционируют нормально. По умолчанию устанавливается настройка --heartbeat.

    Для задания имени директории, в которой размещается файл обмена страниц динамической, общей для всех процессоров памяти, в kmp_cluster.ini -файле имеется специальная настройка --back_store= <имя директории обмена страниц памяти>. По умолчанию обмен страниц памяти осуществляется в директории /tmp.

    Наконец, последняя настройка --[no-]divert_twins предназначена для резервирования двух страниц в директории обмена страниц для файла обмена страниц памяти. По умолчанию эта настройка имеет значение --no-divert_twins, соответствующее отключению режима резервирования.

    В заключение отметим, что в kmp_cluster.ini -файле строго запрещается использовать переменные PATH, SHELL и LD_LIBRARY_PATH.

    Дополнительные настройки компилятора Fortran в Cluster OpenMP

    В программах, написанных на языке Fortran, имеются дополнительные настройки компилятора для работы и анализа переменных, размещенных в общей для всех узлов кластера памяти. Отметим, что компиляторы Fortran компании Intel, начиная с версии 9.1.xx, по умолчанию определяют как sharable переменные программы, содержащиеся в common-блоках, а также переменные программных модулей, локальные переменные типа SAVE и временные переменные для вычисления выражений в вызовах функций и подпрограмм. Для изменения этого режима определения по умолчанию в Intel Fortran компиляторе имеются четыре дополнительные настройки.

    Настройка -[no]-clomp-sharable-common позволяет отключить или включить режим определения по умолчанию переменных common-блоков как sharable.

    Для отключения или включения режима определения по умолчанию локальных переменных типа SAVE как sharable следует использовать настройку -[no]-clomp-sharable-localsaves.

    Настройка -[no]-clomp-sharable-modvars позволяет отключить или включить режим определения по умолчанию переменных программных модулей как sharable.

    Для отключения или включения режима определения по умолчанию временных переменных для вычисления выражений в вызовах функций и подпрограмм как sharable следует использовать настройку -[no]-clompsharable-argexprs.

    В заключение этого раздела отметим, что по умолчанию все четыре вышеперечисленные настройки устанавливаются с префиксом no, т. е. в выключенном режиме.

    Переменные окружения Cluster OpenMP

    При программировании с использованием Cluster OpenMP можно определить ряд переменных окружения, позволяющих контролировать и изменять процессы выполнения программ.

    Для задания размера стеков потоков в Cluster OpenMP можно воспользоваться переменной окружения KMP_STACKSIZE. Размер стека задается в килобайтах (K) или мегабайтах (M). По умолчанию задается KMP_STACKSIZE=1M. Определять размер стека можно не только для отдельных параллельных потоков, но и для общих переменных типа sharable. В последнем случае следует определить переменную окружения KMP_SHARABLE_STACKSIZE. По умолчанию значение этой переменной также равно 1M.

    Переменная окружения KMP_STATSFILE предназначена для определения имени файла, в который будет выводиться статистическая информация при профилировании программы, созданной с настройкой -cluster-openmp-profile. По умолчанию KMP_STATSFILE=guide.gvs.

    При отладке Cluster OpenMP программ необходимо задать имя отладчика. Для этого имеется специальная переменная окружения KMP_CLUSTER_DEBUGGER. Эта переменная может принимать значения idb (отладчик Intel), gdb (отладчик GNU) или totalview (отладчик TotalView). Значение по умолчанию для этой переменной окружения отсутствует.

    Переменная окружения KMP_WARNINGS позволяет включить ( 1 или on ) или выключить ( 0 или off ) режим вывода предупреждающих сообщений библиотеки выполнения ( runtime library ) Cluster OpenMP. По умолчанию задается KMP_WARNINGS=on.

    Для задания режима вывода предупреждающих сообщений об общих для всех узлов кластера переменных в Cluster OpenMP имеется переменная окружения KMP_SHARABLE_WARNINGS. Она также может принимать значения "включить" ( 1 или on ) или "выключить" ( 0 или off ) режим вывода предупреждающих сообщений. Но по умолчанию задается режим KMP_SHARABLE_WARNINGS=off.

    Переменная окружения KMP_CLUSTER_SETTINGS позволяет организовать вывод на экран всех переменных окружения, в том числе определенные в kmp_cluster.ini -файле, а также настройки, заданные в этом же файле. Для этой переменной отсутствует значение по умолчанию.

    Переменная окружения KMP_CLUSTER_PATH определяет путь к kmp_cluster.ini -файлу, если этот файл отсутствует в разделе, из которого запускается программа. По умолчанию это путь к домашнему разделу каталога пользователя.

    Для вывода информации об использовании настроек в kmp_cluster.ini -файле следует задать переменную окружения KMP_CLUSTER_HELP. Еще одна сервисная переменная окружения KMP_VERSION позволяет выводить информацию о версии в процессе работы библиотеки выполнения (run-time library).

    Наконец, последняя переменная окружения KMP_DISJOINT_HEAP задает размер несимметрично распределенной по процессам динамической памяти в килобайтах (K) или мегабайтах (M). Минимальный размер несимметрично распределенной по процессам динамической памяти равен 2K. Значение по умолчанию этой переменной не определено.

    Особенности реализации переменных окружения OpenMP в Cluster OpenMP

    В этом разделе рассматриваются особенности реализации переменных окружения OpenMP в среде Cluster OpenMP.

    В Cluster OpenMP максимальное число параллельных потоков в параллельной области программы определяется заданием настройки --omp_num_threads. Однако число параллельных потоков может быть и меньше максимального, если оно определено с помощью задания переменной окружения OMP_NUM_THREADS или с помощью вызова функции omp_set_num_threads() из библиотеки выполнения (runtime library).

    Число процессоров, которое возвращает функция omp_get_num_procs(),

    - это сумма процессоров, вычисленная по всем узлам вычислительной системы. Cluster OpenMP не поддерживает вложенный параллелизм. Поэтому вложенные параллельные области выполняются как последовательность потоков, причем на том же процессоре, на котором выполняется их родительский поток. Таким образом, вызов функции omp_set_nested или задание переменной окружения OMP_NESTED не имеют никакого влияния.

    При задании переменных окружения, определяющих загрузку вычислительной системы, следует иметь в виду, что если переменная окружения OMP_SCHEDULE не определена, то это означает, что по умолчанию задается статический ( static ) режим загрузки параллельных процессов.

    Специальные функции Cluster OpenMP

    В Cluster OpenMP имеется целый ряд специальных функций для задания некоторых переменных окружения в процессе выполнения программы, а также для выделения и освобождения памяти в процессе работы программы и передачи сигналов. Рассмотрение этих функций начнем с рассмотрения функций задания переменных окружения и функций, информирующих об окружающей среде.

    Функции

    void kmp_set_warnings_on ( void )

    и

    void kmp_set_warnings_off ( void )

    предназначены для задания режима вывода предупреждающих сообщений в процессе работы библиотеки выполнения. Первая функция устанавливает переменную окружения KMP_WARNINGS=on, а вторая - KMP_WARNINGS=off отменяет ее.

    Следующая функция

    omp_int_t kmp_get_process_num ( void )

    возвращает номер текущего выполняемого процесса. Функция

    omp_int_t kmp_num_processes ( void )

    возвращает общее число потоков в текущий момент времени. Следующая функция

    omp_int_t kmp_get_process_thread_num ( void )

    возвращает номер текущего потока в выполняемом процессе.

    Теперь перейдем к рассмотрению функций передачи сигналов в Cluster OpenMP. Всего таких функций шесть.

    Первая функция

    void kmp_lock_cond_wait ( omp_lock_t *lock )

    предназначена для приостановки выполнения процесса, содержащего данный оператор, до момента разблокировки объекта, блокировка которого установлена переменной lock.

    Вторая функция

    void kmp_lock_cond_signal ( omp_lock_t *lock )

    посылает сигнал о блокировке объекта, установленной с помощью переменной lock.

    Третья функция

    void kmp_lock_cond_broadcast ( omp_lock_t *lock )

    рассылает всем процессам сигнал о блокировке объекта, установленной с помощью переменной lock.

    Четвертая функция

    void kmp_nest_lock_cond_wait ( omp_nest_lock_t *lock )

    предназначена для приостановки выполнения процесса, содержащего данный оператор, до момента разблокировки объекта, вложенная блокировка которого установлена переменной lock.

    Пятая функция

    void kmp_nest_lock_cond_signal ( omp_nest_lock_t *lock )

    посылает сигнал о вложенной блокировке объекта, установленной с помощью переменной lock.

    Наконец, шестая функция

    void kmp_nest_lock_cond_broadcast ( omp_nest_lock_t *lock )

    рассылает всем процессам сигнал о вложенной блокировке объекта, установленной с помощью переменной lock.

    Для работы с памятью в Cluster OpenMP имеется девять специальных функций. Далее рассмотрим эти функции.

    Функция

    void *kmp_sharable_malloc ( size_t size )

    предназначена для выделения динамической, общей для всех узлов кластера памяти. Объем выделяемой памяти определяется значением переменной size.

    Следующая функция

    void *kmp_aligned_sharable_malloc ( size_t size )

    предназначена для выделения динамической, общей для всех узлов кластера памяти. Это выделение осуществляется с выравниванием по границе страницы памяти. Объем выделяемой памяти определяется значением переменной size.

    Функция

    void *kmp_sharable_сalloc ( size_t n, size_t size )

    предназначена для выделения в динамической, общей для всех узлов кластера памяти места под массив из n элементов размера size. При этом все элементы этого массива заполняются нулями.

    Еще одна функция

    void *kmp_sharable_reсalloc ( void *ptr, size_t size )

    предназначена для переинициализации выделения места в динамической, общей для всех узлов кластера памяти под массив размера size. Адрес этого места определяется указателем *ptr. При этом все элементы этого массива заполняются нулями.

    Функция

    void *kmp_sharable_free ( void *ptr )

    предназначена для освобождения в динамической, общей для всех узлов кластера памяти раздела, определенного указателем *ptr.

    Следующие функции

    void *kmp_private_mmap (char *file,size_t *len,void **addr)

    и

    void *kmp_private_munmap ( void *start )

    предназначены для отображения файлов в области индивидуальной адресной памяти процессов, начиная с одного и того же адреса. Первая функция kmp_private_mmap отображает file длиной len байт в память, начиная с адреса addr. Вторая функция kmp_private_munmap удаляет все отображения из заданной области памяти с начальным адресом start, после чего все ссылки на данную область будут вызывать ошибку "неправильное обращение к памяти". Отображение удаляется автоматически при завершении процесса. Однако закрытие файла не приводит к снятию отображения. Эти функции возвращают 0 в случае успешного завершения и -1 в противном случае. Отметим, что их можно применять только в последовательной области программы. В параллельной области они не поддерживаются. Кроме того, эти две функции имеют тип read-only, т.е. после удаления отображения с помощью второй функции содержимое файла не обновляется содержимым области памяти.

    Функции

    void *kmp_sharable_mmap ( char *file, size_t *len, void **addr )

    и

    void *kmp_sharable_munmap ( void *start )

    предназначены для отображения файлов в области адресной памяти главного процесса, начиная с одного и того же адреса. Первая функция kmp_sharable_mmap отображает file длиной len байт в память, начиная с адреса addr. Вторая функция kmp_sharable_munmap удаляет все отображения из заданной области памяти с начальным адресом start, после чего все ссылки на данную область будут вызывать ошибку "неправильное обращение к памяти". Отображение удаляется автоматически при завершении процесса. Функции возвращают 0 в случае успешного завершения и - 1 в противном случае. Эти две функции имеют тип read-write, т. е. после удаления отображения с помощью второй функции содержимое файла обновляется содержимым области памяти. Отметим также, что их можно применять только в последовательной области программы. В параллельной области их использовать нельзя.

    Загрузка данных в общую для всех узлов кластера память в Cluster OpenMP

    При рассмотрении проблемы загрузки данных в общую ( sharable ) для всех узлов кластера память прежде всего следует иметь в виду, что наименьший размер элементов, с которыми оперирует Cluster OpenMP, равен 4 байтам. Если же размер элементов по каким-то причинам оказывается меньше 4 байт, то операции с ними не производятся. Таким образом, если в программе, например, определены массивы строковых переменных

    char a[100], b[100]

    а затем эти переменные определены как sharable, то следующий параллельный блок в программе выполняться не будет:

    #pragma omp parallel for 
    for( i=0; i<N; i++ ) 
     { 
       a[i] = b[i]; 
     }

    Для динамической загрузки переменных в общую ( sharable ) для всех узлов кластера память в программах, написанных на языке C/C++, можно воспользоваться одной из двух следующих функций:

    void * kmp_sharable_malloc ( int size )

    или

    void * kmp_aligned_sharable_malloc ( int size )

    Обе эти функции определяют в динамическом режиме в общей для всех узлов кластера памяти области заданного размера и возвращают адреса этих областей. При этом следует иметь в виду, что вторая из этих функций осуществляет выравнивание выделяемой области памяти по границе страницы и тем самым существенно ускоряет процесс обращения к памяти. Для освобождения выделенной памяти следует использовать функцию

    void kmp_sharable_free ( *ptr )

    Здесь ptr - указатель на освобождаемую область памяти.

    Динамическое выделение памяти в общей ( sharable ) для всех узлов кластера памяти в программах, написанных на языке Fortran, осуществляется следующим образом:

    integer, allocatable :: x( : ) 
    !dir$ omp sharable (x) 
        allocate( x( 500 ) )

    В этом примере в общей для всех узлов кластера памяти динамически размещается целочисленный массив, состоящий из 500 элементов.

    В программах, написанных на языке C++, при динамическом размещении переменных и объектов в общей для всех узлов кластера памяти необходимо добавить включение ссылки на файл, в котором описаны переменные sharable. Эта ссылка имеет следующий вид:

    #include <kmp_sharable.h>

    Теперь рассмотрим вопрос создания динамических объектов в общей для всех узлов кластера памяти. Пусть в исходном тексте программы динамически определяется объект класса zoo, например, следующим образом:

    zoo * fp = new zoo (10);

    В Cluster OpenMP это определение модифицируется так:

    zoo * fp = new kmp_sharable zoo (10);

    Теперь рассмотрим вопрос создания класса объектов, динамически размещаемых в общей для всех узлов кластера памяти. Пусть в исходной программе этот класс создавался, например, следующим образом:

    class zoo : public zoo_base 
     { 
      // …содержание класса zoo 
     };

    В программе, написанной с использованием Cluster OpenMP, создание этого класса осуществляется следующим образом:

    class zoo : public zoo_base, public kmp_sharable_base 
     { 
      // …содержание класса zoo 
     };

    При динамическом создании STL (Standard Template Library) контейнеров в общей для всех узлов кластера памяти следует иметь в виду, что, во-первых, необходимо описать динамическое размещение контейнера как объекта и, кроме того, необходимо описать динамическое размещение его содержания. Если в исходной программе контейнер создавался, например, следующим образом:

    std::vec<int> * vecp = new std :: vec<int>;

    то в программе, написанной с использованием ClusterOpenMP, создание такого контейнера должно быть описано, например, так:

    std::vec<int,kmp_sharable_allocator<int>>*vecp=new 
    kmp_sharable std :: vec<int, kmp_sharable_allocator<int>>;

    Настройки отладчиков в Cluster OpenMP

    Для отладки многопоточных параллельных программ в Cluster OpenMP можно использовать как бесплатный отладчик GNU gdb, так и лицензионные отладчики idb компании Intel и TotalView компании Etnus. Однако для того, чтобы корректно пользоваться этими отладчиками, необходимо сделать специальные настройки в системных файлах. Далее рассмотрим подробно эти настройки применительно ко всем вышеперечисленным отладчикам. Предварительно отметим, что для корректной работы всех этих отладчиков необходимо отключить прерывание по сигналу ошибки адресации SIGSEGV (Signal Segmentation Violation). Причина в том, что этот сигнал используется для внутренних нужд Cluster OpenMP, поэтому он не является аварийным и не несет информации об ошибках доступа к памяти (в Cluster OpenMP имеется свой собственный обработчик этого сигнала). Отключение этого сигнала производится по-разному для рассматриваемых отладчиков и будет рассмотрено далее. Для всех отладчиков в kmp_cluster.ini-файле необходимо использовать настройку --no-heartbeat, отключающую режим аварийного завершения параллельных потоков при отсутствии в течение определенного промежутка времени ответных сигналов от параллельных потоков. Такими сигналами в Cluster OpenMP постоянно обмениваются все параллельные потоки. Если в течение определенного промежутка времени ответный сигнал от какого-либо потока отсутствует, то это считается свидетельством его аварийного завершения. В этом случае происходит аварийное завершение всех потоков рассматриваемой задачи.

    В бесплатном отладчике GNU gdb для отключения сигнала аварийного доступа к памяти, во-первых, необходимо в системном файле .gdbinit, находящемся в домашнем разделе пользователя, добавить строку

    handle SIGSEGV nostop noprint

    Во-вторых, необходимо определить отладчик с помощью переменной окружения KMP_CLUSTER_DEBUGGER, например следующим образом:

    export KMP_CLUSTER_DEBUGGER=gdb

    В-третьих, необходимо определить переменную окружения DISPLAY, указывающую на IP-адрес компьютера, с которого стартует X Windows. Эту переменную можно определить либо в kmp_cluster.ini -файле, например так:

    DISPLAY= 10.0.6.191:0

    либо с помощью команды

    export DISPLAY= 10.0.6.191:0

    которую следует поместить в системный файл, откуда считываются переменные окружения при старте программы xterm. После запуска отладчика gdb следует набрать команду

    break __itmk_segv_break

    для перехвата ошибок адресации памяти.

    В отладчике idb компании Intel для отключения сигнала аварийного доступа к памяти необходимо в системном файле .dbxinit, находящемся в домашнем разделе пользователя, добавить строку

    ignore segv

    Во-вторых, необходимо определить отладчик с помощью переменной окружения KMP_CLUSTER_DEBUGGER следующим образом:

    export KMP_CLUSTER_DEBUGGER=idb

    Переменная окружения DISPLAY для этого отладчика задается так же, как и для отладчика gdb. После запуска отладчика idb следует набрать команду

    stop in __itmk_segv_break

    для перехвата ошибок адресации памяти.

    В отладчике TotalView компании Etnus для отключения сигнала аварийного доступа к памяти необходимо в системном файле .tvdrc, находящемся в домашнем разделе пользователя, добавить строку

    dset TV::signal_handling_mode { Resend=SIGSEGV }

    После запуска отладчика TotalView следует набрать команду

    breakpoint in __itmk_segv_break

    для перехвата ошибок адресации памяти.

    Настройка динамической памяти при отладке в Cluster OpenMP

    При использовании динамической памяти в программах с использованием Cluster OpenMP возможна следующая ошибочная ситуация: некоторые данные одного типа в параллельных потоках размещены в локальной памяти с одними и теми же адресами (т. е. некоторые данные из параллельных потоков симметрично размещены в локальной памяти). При этом указатель на эти данные в одном из параллельных потоков ошибочно передается в другой поток, и из этого последнего потока осуществляется доступ к данным по переданному адресу. В таком случае ошибки доступа к данным может и не возникнуть, т. е. сигнал SIGSEGV не будет инициирован. Однако данные, которые будут извлечены из другого потока, будут неверными для исходного потока. Для

    того чтобы выявить такие ошибочные ситуации, в Cluster OpenMP реализован

    механизм несимметричного выделения динамической памяти (Disjoint Heap).

    Для включения механизма несимметричного выделения динамической памяти в параллельных потоках следует задать переменную окружения KMP_DISJOINT_HEAPSIZE, например, следующим образом:

    export KMP_DISJOINT_HEAPSIZE 128M

    Здесь была задана область для несимметричного выделения динамической памяти, равная 128 мегабайтам. При выделении размера этой области следует позаботиться о том, чтобы ее размер был не меньше, чем произведение размера области динамической памяти, выделяемой для одного параллельного потока, на число параллельных потоков. В этом случае области динамической памяти в параллельных потоках будут размещены в адресном пространстве без пересечений и наложений. Тогда обращение в каком-либо из параллельных потоков к области динамической памяти по указателю, переданному из другого потока, приведет к ошибке доступа к данным и возникновению сигнала SIGSEGV. Это и будет побудительной причиной более тщательного анализа работы с данными в параллельных потоках для устранения таких некорректных ситуаций.

    В заключение отметим, что по умолчанию значение переменной окружения KMP_DISJOINT_HEAPSIZE задается равным 2 мегабайтам. При задании меньшей величины размер KMP_DISJOINT_HEAPSIZE автоматически увеличивается до 2 мегабайт.

    Подчеркнем еще раз, что задание переменной окружения KMP_DISJOINT_HEAPSIZE необходимо только на этапе отладки программы для поиска ошибок некорректного обращения к динамической памяти в параллельных потоках. После выявления всех таких ошибок и создания окончательной оптимизированной версии программы эту переменную окружения следует удалить.

    Сводка переменных окружения Cluster OpenMP

    В предыдущих разделах был уже рассмотрен ряд специфических переменных окружения Cluster OpenMP. Ниже перечислены все переменные окружения, допустимые в Cluster OpenMP, и описаны способы их применения.

  • Переменная окружения KMP_STACKSIZE задает объем стека в параллельных потоках Custer OpenMP. Величина этой переменной задается в килобайтах (К) или мегабайтах (М). По умолчанию значение этой переменной равно .
  • Переменная окружения KMP_SHARABLE_STACKSIZE задает объем стека для размещения sharable (общих для всех узлов кластера) данных для OpenMP потоков. Величина этой переменной задается в килобайтах (К) или мегабайтах (М). По умолчанию значение этой переменной равно .
  • Переменная окружения KMP_STATSFILE задает имя файла статистики при использовании настройки -cluster-openmp-profile. По умолчанию имя этого файла guide.gvs.
  • Задание значения переменной окружения KMP_WARNINGS выключает ( 0 или off ) или включает ( 1 или on ) выдачу сообщений библиотеки выполнения (run-time library) Cluster OpenMP. По умолчанию значение этой переменной равно 1 или on.
  • Задание значения переменной окружения KMP_WARNINGS выключает ( 0 или off ) или включает ( 1 или on ) выдачу предупреждающих сообщений о переменных в параллельных потоках, которые должны быть общими для всех узлов кластера ( sharable ), но не являются таковыми. По умолчанию значение этой переменной равно 0 или off.
  • Переменная окружения KMP_CLUSTER_SETTINGS задает режим вывода переменных окружения и настроек, определенных в kmp_cluster.ini файле. Значения по умолчанию для этой переменной нет.
  • Переменная окружения KMP_CLUSTER_PATH задает путь к kmp_cluster.ini -файлу, если он отсутствует в текущем разделе. По умолчанию в качестве этого пути задается путь к домашнему разделу пользователя.
  • Задание переменной окружения KMP_CLUSTER_HELP позволяет выводить описание настроек, допустимых в kmp_cluster.ini -файле. Значения по умолчанию для этой переменной нет.
  • Переменная окружения KMP_CLUSTER_DEBUGGER задает имя исполняемого файла отладчика (например, idb для отладчика Intel). Значения по умолчанию для этой переменной нет.
  • Переменная окружения KMP_CLUSTER_VERSION задает режим вывода сообщений о версии системы. Значения по умолчанию для этой переменной нет.
  • Переменная окружения KMP_DISJOINT_HEAPSIZE задает значение размера динамической памяти в параллельных потоках и включает механизм Disjoint Heap. Величина этой переменной задается в килобайтах (К) или мегабайтах (М). По умолчанию значение этой переменной равно . Если заданное значение меньше , то оно автоматически увеличивается до .
  • На этом завершается знакомство c Cluster OpenMP. Отметим, что для изучения дополнительных материалов, оставшихся за рамками данного изложения, следует обратиться к руководству [7.9].

    Вернуться к учебному плану