Информационные технологии еестник Нижегородского университета им. Н.И. Лобачевского, -¡011, Ns 3 (2), с. 201-206
УДК 519.673:004.89
ЦЕЛЕСООБРАЗНОСТЬ СОЗДАНИЯ СТАНДАРТА OPENMP ДЛЯ ОБЪЕКТНО-ОРИЕНТИРОВАННЫХ ЯЗЫКОВ ПРОГРАММИРОВАНИЯ
НА ПРИМЕРЕ JAVA
© 2011 г. Д.А. Грудзинский
Нижегородский госуниверситет им. Н.И. Лобачевского
Поступила в редакцию 27.01.2011
Приводится адаптированная спецификация стандарта OpenMP для языка программирования Java и ее реализация. Демонстрируется эффективность разработанной программной системы при решении некоторых классических задач параллельного программирования.
Ключевые слова: OpenMP, Java.
Введение
В настоящее время технология OpenMP является одним из наиболее популярных средств организации параллельных вычислений на многопроцессорных системах с общей памятью. Она реализует подход, при котором указания программиста по организации параллельных вычислений добавляются в программу при помощи тех или иных внеязыковых средств языка программирования - например, в виде директив или комментариев, которые обрабатываются специальным препроцессором до начала компиляции программы. При этом остается неизменным код программы, по которому, в случае отсутствия препроцессора, компилятор построит исходный последовательный программный код. Препроцессор же, будучи примененным, заменяет директивы параллелизма на некоторый дополнительный программный код (как правило, в виде обращений к интерфейсам специальной библиотеки). Основным достоинством этого подхода является отсутствие необходимости значительной переработки уже существующего программного обеспечения при его распараллеливании.
Разработкой стандарта OpenMP занимается некоммерческая организация OpenMP ARB (Architectural Review Board) [1], в которую вошли представители крупнейших компаний -разработчиков SMP-архитектур и программного обеспечения. На данный момент официально существует описание стандарта только для языков Фортран и Си/Си++. Первая спецификация
для языка Фортран появилась в октябре 1997 года, а спецификация для языков С/Си++ - в октябре 1998 года. На данный момент последняя официальная спецификация стандарта -OpenMP 3.0 [2] (принята в мае 2008 года).
В свою очередь, в последнее время особой популярностью при создании программного обеспечения пользуются современные обьект-но-ориентированные языки. Так, по данным ежегодного рейтинга популярности языков программирования, составляемого организацией TIOBE software [3] на основе данных о запросах в таких онлайн-сервисах, как Google, MSN, Yahoo!, Wikipedia, язык Java уже на протяжении нескольких лет занимает первое место. Следовательно большинство приложений создается именно на этой платформе, и при возникновении задачи распараллеливания такое средство, как OpenMP, будет необходимым.
Для оценки возможности и целесообразности создания подобного средства была разработана и реализована адаптированная спецификация OpenMP для платформы Java. В статье демонстрируется эффективность разработанной программной системы на примере классических задач параллельного программирования.
Основные положения спецификации OpenMP
Технология OpenMP реализует параллельные вычисления с помощью многопоточности, в которой основной (master) поток создает набор подчиненных (slave) потоков и задача
распределяется между ними. После завершения потоками параллельной области программа продолжает исполнение основным потоком. Такая модель распараллеливания называется fork/join. Предполагается, что потоки выполняются параллельно на машине с несколькими процессорами.
Задачи, выполняемые потоками параллельно, так же как и данные, требуемые для выполнения этих задач, описываются с помощью специальных комментариев. Перед запуском OpenMP программы производится трансляция этих комментариев в вызовы процедур интерфейсов специальной библиотеки времени выполнения. Количество создаваемых потоков может регулироваться самой программой при помощи вызовов библиотечных процедур.
Выделяют следующие типы директив:
• Конструкция parallel является входной точкой в параллельную область программы. При ее вызове создается команда потоков и указанная в конструкции задача выполняется параллельно.
• Директивы разделения работы позволяют распределить работу между потоками, отдавая каждому потоку свою порцию задачи. Например, директива for позволяет распределить выполнение итераций цикла.
• Директива task определяет явную задачу, которую необходимо выполнить какому-либо потоку команды. После создания задача отправляется в очередь невыполненных задач, пока какой-либо освободившийся поток не выполнит ее. Данная конструкция является основным новвоведением спецификации 3.0 по сравнению с предшественниками. Для внесения данной конструкции разработчики существенно изменили модель исполнения OpenMP программы.
• Директивы синхронизации работы потоков.
Также, помимо директив, существуют специальные функции библиотеки поддержки времени выполнения, позволяющие управлять работой параллельной программы.
OpenMP программа начинает выполнение одним потоком, называемым исходным потоком. Он выполняет программу последовательно, как будто находится внутри неявного региона задачи (называемой исходной), определенного неявным неактивным параллельным регионом, окружающим всю программу.
При столкновении с параллельным регионом:
1. Создается команда потоков. Этот поток становится потоком-мастером, и ему присваивается номер 0.
2. Для каждого потока создается неявная задача, определенная кодом внутри параллельного региона.
3. Каждая задача назначается одному потоку и становится связанной.
4. Исходная задача приостанавливается, и все потоки команды начинают выполнять назначенные им задачи.
5. В конце региона находится неявный барьер, синхронизирующий работу всех потоков.
6. После достижения этого барьера все потоки, кроме мастера, умирают и продолжается выполнение исходный задачи.
В программе может быть неограниченное количество параллельных конструкций. Если в реализации OpenMP разрешен вложенный параллелизм, то параллельные конструкции могут быть вложены друг в друга.
Когда команда потоков встречает конструкцию разделения работы, тогда работа внутри конструкции распределяется между потоками и выполняется кооперативно, вместо выполнения каждым потоком. В конце каждой конструкции разделения работы есть опциональный барьер.
При столкновении потока с конструкцией task:
1. Создается новая явная задача.
2. Выполнение назначается какому-либо потоку команды в зависимости от существования свободного потока, то есть задача может быть выполнена немедленно либо отложена.
Потокам разрешается переключаться между задачами в точках разрыва. Если задача является связанной, то только изначальный поток может ее закончить, в противном случае - любой. Для несвязанных регионов точки разрыва могут произойти в любом месте, определенном реализацией.
В регионах связанных задач точки разрыва находятся в следующих местах:
• при встрече конструкции task,
• при встрече директивы taskwait,
• при встрече директивы barrier,
• при вхождении в неявный барьер,
• в конце исполнения связанной задачи.
OpenMP поддерживает разделяемую модель
памяти. Все OpenMP потоки имеют доступ к единой области памяти для хранения переменных и получения их значений. В дополнение каждый поток может иметь свое временное представление памяти. Также каждый поток содержит отдельную область памяти, называемую приватной памятью потока, доступ к которой не разрешен другим потокам.
Директива, которая позволяет указывать опции разделения данных, определяет два типа
доступа к переменным, используемым в ассоциированном структурном блоке: приватный и разделяемый. Каждая переменная, на которую есть ссылка в структурном блоке, имеет свое оригинальное значение, которое содержится в переменной с тем же именем вне конструкции. Каждая ссылка к разделяемой переменной в структурном блоке является ссылкой на оригинальную переменную. Для каждой приватной переменной создается копия оригинальной переменной для ее использования в ассоциированном структурном блоке.
Более подробно описание спецификации приведено в [4].
Обоснование подхода к разработке
При создании спецификации OpenMP для языка программирования Java было принято решение как можно больше соответствовать оригинальной спецификации для языков Си/Си++ и Фортран в названиях процедур, переменных окружения, модели памяти и исполнения OpenMP программы. При этом учитывались особенности и возможности платформы Java при создании многопоточных приложений, модель памяти Java и соглашения по названиям функций и переменных.
При выборе подхода к разработке программного комплекса, реализующего стандарт OpenMP для языка программирования Java, было принято решение следовать следующим принципам:
• Система должна реализовывать всю функциональность, в том числе и опциональные возможности создания вложенных параллельных регионов, следовать модели исполнения и модели памяти OpenMP.
• Система не должна создавать дополнительных ограничений к существующим ограничениям спецификации OpenMP и платформы Java при ее использовании.
• Система должна минимизировать требования к пользователю по установке дополнительного оборудования для ее использования.
• Система должна быть спроектирована так, чтобы ее можно было в дальнейшем легко расширять при появлении новых требований спецификации OpenMP.
Руководствуясь изложенными выше принципами, было принято несколько стратегических решений подхода к разработке программного комплекса.
В качестве платформы для реализации как библиотеки времени исполнения, так и препроцессора OpenMP директив выбрана платформа
Java, т.к. поддержка многопоточности встроена в саму платформу, а использование других средств приводит к следующим проблемам:
• возможно возникновение ограничений на мультиплатформенность пользовательского Java приложения,
• затрудняется организация многопоточности,
• возникают лишние затраты времени на коммуникацию интерфейсов, реализованных на разных языках программирования.
Препроцессор OpenMP директив создается как отдельный программный модуль, позволяющий из кода входной программы, написанной с использованием комментариев OpenMP, генерировать код выходной программы, использующий интерфейсы библиотеки времени исполнения, т.к.:
• это наиболее простое решение, не требующее создания расширений стандартного Java компилятора,
• код выходной программы доступен пользователю для анализа и редактирования,
• не требует от пользователя установки специального компилятора языка Java, позволяющего обрабатывать дополнительные структуры языка.
При реализации принято решение наиболее широко использовать объектно-ориентированный подход к созданию программного обеспечения, т.к. он позволяет создавать легко модифицируемые и расширяемые системы.
Подробное описание полученного программного комплекса приводится в [4].
Сравнение производительности библиотек OpenMP для Си++ и Java на примере базового алгоритма перемножения матриц
Операция перемножения матриц является одной из основных задач матричных вычислений. В данном примере проводится сравнение времени выполнения базового алгоритма перемножения квадратных матриц при использовании библиотеки OpenMP для Си++, поставляемой со средой разработки Visual Studio 8, и времени выполнения реализации на Java, которая использует OpenMP библиотеку, разработанную в ходе выполнения данной работы. Для реализации параллельного алгоритма использовалась директива распараллеливания циклов.
Умножение матрицы A размера m-n на матрицу B размера nl приводит к получению матрицы С размера m l, каждый элемент которой определяется в соответствии с выражением:
П-1
cij = Z aik bkj > 0 ^ m 0 ^ J <l-
k=0
Каждый элемент результирующей матрицы С есть скалярное произведение соответствующих строки матрицы А и столбца матрицы В:
с = (а»Ъ]), а = (а о, аы—),
Ъ] = (¿о ], Ъх ]Ьп-1 )Т.
Этот алгоритм предполагает выполнение ш-п-1 операций умножения и столько же операций сложения элементов исходных матриц. При умножении квадратных матриц размера количество выполненных операций имеет порядок 0(п3). Подробный анализ теоретических оценок работы последовательного и параллельного ал-
Как видно из результатов экспериментов, время выполнения и последовательного, и параллельного алгоритмов на Си++ и Java практически одинаково. Это подтверждает то, что в последнее время программы, написанные на Java, не уступают по производительности их аналогам на Си++.
Таблица 2
Сравнение ускорения при распараллеливании базового алгоритма перемножения матриц на С++ и Java
горитмов, а также детали реализации алгоритмов приведены в [5].
Эксперименты проводились на вычислительном узле на базе процессора AMD Athlon™ 64 X2 Dual Core Processor 4200+, 2.20 ГГц, 2 Гб RAM, под управлением операционной системы Windows XP Professional. Разработка программы на Си++ проводилась в среде Visual Studio 2005 с включенной оптимизацией компилятора, тогда как программа на Java разрабатывалась в среде Eclipse 3.5.0 на платформе Java 1.6. Параллельные алгоритмы использовали 2 потока для выполнения параллельных вычислений. Результаты экспериментов приведены в табл. 1.
Обе реализации показали практически двукратное ускорение при использовании OpenMP библиотеки. Результаты представлены в табл. 2.
Алгоритм умножения матриц, основанный на ленточном разделении данных
Данный пример представляет собой иной способ выделения информационных зависимостей при разработке параллельного алгоритма, чем описанный в предыдущем разделе. Этот метод позволяет продемонстировать функциональность вложенного параллелизма, предо-ставлямего реализованной библиотекой на Java. Стоит отметить, что не все существующие реализации для Фортрана и Си/Си++ реализуют данную функциональность.
В отличие от предыдущего примера, в данном алгоритме кроме одного параллельного региона, распределяющего итерирование по строкам первой матрицы, на каждой итерации обхода строк первой матрицы создается вложенный параллельный регион, распределяющий итерации обхода столбцов второй матрицы. Подробное описание последовательного и параллельного алгоритмов приведено в [5].
Диагональ матрицы Ускорение С++ Ускорение Java
200 1.94 1.94
300 1.85 2.00
400 1.71 1.94
500 1.72 1.83
600 1.81 1.84
700 1.88 1.73
800 1.76 1.81
900 1.81 1.77
1000 1.77 1.81
1500 1.88 1.83
2000 1.84 1.79
Таблица 1
Сравнение реализаций на С++ и Java базового алгоритма перемножения матриц.
Время работы представлено в миллисекундах
Диагональ матрицы Последовательный С++ Последовательный Java Параллельный С++ Параллельный Java
200 31 31 16 16
300 172 250 93 125
400 375 515 219 266
500 860 1141 500 625
600 1922 2156 1063 1172
700 3969 4140 2110 2391
800 7938 7641 4500 4226
900 10375 10609 5735 5984
1000 15312 15031 8657 8297
1500 63158 64001 33564 34922
2000 166066 180441 90049 100783
Эксперименты проводились на вычислительном узле на базе процессора Intel Core 2 Duo, 2.53 ГГц, 4Гб RAM, под управлением операционной системы Mac OSX 10.6. Результаты для 2х потоков представлены в табл. 3.
Таблица 3
Результаты вычислительных экспериментов для параллельного умножения матриц при ленточной схеме разделения данных.
Время указано в миллисекундах
Диагональ матрицы Последова- тельный Парал- лельный Ускорение
100 2 99 0.02
300 66 219 0.30
500 649 736 0.88
1000 7236 4803 1.51
1500 29167 16317 1.79
2000 73934 41892 1.76
2500 152035 81083 1.88
3000 254931 138864 1.84
Как видно из результатов экспериментов, при увеличении размера матриц ускорение от использования параллельных алгоритмов растет и насыщение достигается около значения 1.9.
Если сравнить первый и второй подходы при решении задачи умножения матриц, то можно отметить, что при малых размерах диагонали матрицы (до 1500 элементов) второй подход сильно уступает первому. Это связано с накладными расходами на создание дополнительных потоков при порождении вложенных параллельных регионов. При дальнейшем увеличении размера матриц ускорение выравнивается и насыщение достигается при значении около 1.9.
Параллельная обработка связного списка
Одной из классических задач, возникающих при разработке любого приложения, является выполнение обработки каждого элемента некоторого связного списка. Если задачи, выполняемые над каждым звеном списка, независимы друг от друга, то можно выполнить эти задачи параллельно на многопроцессорной системе.
Спецификации ОрепМР, выпущенные до версии 3.0, не предоставляли удобных инструментов для распараллеливания таких задач. В последней же версии стандарта появилась концепция явных задач и точек разрыва, специально введенная для решения такого рода проблем. Подробное описание алгоритма решения данной проблемы представлено в [4].
Эксперименты проводились для 2х потоков на вычислительном узле на базе процессора
Intel Core 2 Duo, 2.53 ГГц, 4Гб RAM, под управлением операционной системы Mac OSX 10.6. Результаты экспериментов относительно размера списка приведены в табл. 4.
Таблица 4 Параллельная обработка списка для различного количества звеньев
Размер списка Последова- тельный Парал- лельный Ускорение
100 77 42 1.83
300 226 127 1.78
500 376 220 1.71
1000 742 417 1.78
1500 1115 597 1.87
2000 1499 800 1.87
2500 1876 1021 1.84
3000 2100 1089 1.93
5000 3491 1835 1.90
Таким образом, минимальное ускорение при распараллеливании обработки списка на 2 потока составляет 1.7, а насыщение достигается около значения 1.9.
Заключение
В ходе работы была разработана спецификация технологии OpenMP для Java и создана ее реализация. Также была продемонстрирована эффективность использования реализованной библиотеки на примере нескольких классических задач параллельного программирования. Приведено сравнение эффективности использования полученной библиотеки с аналогом на языке Си++.
Исходя из полученных результатов можно сделать вывод, что по производительности полученная библиотека практически не уступает уже существующим реализациям для Си++, что показывает целесообразность развития технологии в сторону платформы Java.
Список литературы
1. Официальный сайт некоммерческой организации OpenMP Architecture Review Board. URL:http://www.openmp.org.
2. OpenMP Application Program Interface Version 3.0. May 2008. 318 p.
3. Официальный сайт компании TIOBE Software: URL:http://www.tiobe.com.
4. Грудзинский Д.А. Реализация стандарта OpenMP для языка программирования Java. Магистерская диссертация. Н. Новгород: ННГУ, 2010. 59 с.
5. Гергель В.П. Высокопроизводительные вычисления для многоядерных многопроцессорных систем. Н. Новгород: Изд-во ННГУ, 2010.
6. Гергель В.П., Стронгин Р.Г. Основы параллельных вычислений для многопроцессорных вычислительных систем. Н. Новгород: Изд-во ННГУ, 2003. 184 с.
7. Антонов А.С. Параллельное программирование с использованием технологии ОрепМР: Учебное пособие. М.: Изд-во МГУ, 2009. 76 с.
8. Букатов А.А., Дацюк В.Н., Жегуло А.И. Программирование многопроцессорных вычислительных систем. Ростов-на-Дону: ООО «ЦВВР», 2003. 208 с.
9. Ахо А., Ульман Дж. Теория синтаксического анализа, перевода и компиляции. М.: Мир, 1978. 308 с.
10. Eckel B. Thinking in Java, 3rd Edition. Prentice Hall PTR, 2006. 1482 p.
11. Lea D. Concurrent Programming in Java™: Design Principles and Patterns. Prentice Hall Ptr, 1999. 432 p.
RATIONALE FOR THE DEVELOPMENT OF OPENMP STANDARD SPECIFICATIONS FOR OBJECT-ORIENTED PROGRAMMING LANGUAGES AS EXEMPLIFIED BY THE JAVA LANGUAGE
D.A. Grudzinsky
An implementation of OpenMP 3.0 standard specifications adapted for the Java language is presented. The performance of the developed system is demonstrated when solving some classical problems of parallel programming.
Keywords: OpenMP, Java.