Проблема параллельного программирования для мобильных платформ и подходы к ее решению
Сидорин А.В., Романова Т.Н.
Московский государственный технический университет им. Н.Э. Баумана [email protected], а1ехеу. у. .»'¿¿опп @ у а. ги Аннотация. В статье анализируются актуальные проблемы, существующие в сфере параллельного программирования для мобильных платформ. В результате исследования существующих проблем и подходов в данной статье аргументируется выбор подхода, предполагающего преобразование программы на уровне исходного кода, и показываются его основные преимущества. Приводится математическая постановка задачи, которая позволит произвести количественную оценку эффективности выбранного подхода.
Ключевые слова: автоматическое распараллеливание, программирование для мобильных платформ, воигсе^о-воигсе трансляция.
1. Введение
В настоящее время использование многопроцессорных систем становится всё более массовым. Уже несколько лет назад многоядерные конфигурации стали обычными для настольных персональных компьютеров и переносных ПК. Кроме того, последние два года были отмечены появлением многоядерных мобильных систем, причём количество ядер мобильных процессоров, как и их производительность, быстро растёт: уже перестали быть необычными четырёхъядерные конфигурации как в мобильных телефонах (смартфонах), так и в планшетных компьютерах. Поскольку одним из основных предназначений данного класса устройств являются мультимедийные технологии, весьма вероятно, что подобная тенденция будет сохраняться и в дальнейшем. Вместе с тем, разработчики мобильных приложений достаточно часто не могут разрабатывать эффективные многопоточные приложения, поскольку появление многопроцессорных мобильных систем произошло лишь недавно. В связи с этим потенциальный интерес представляет разработка и исследование методов, нацеленных на автоматизацию распараллеливания и оптимизированных для мобильных платформ.
Сейчас даже мобильные приложения достигли такого уровня сложности, что параллельное выполнение может заметно увеличить их производительность. В первую очередь это касается мультимедийных приложений, обрабатывающих видеоинформацию (например, видео и фото высокого разрешения), звук и ЗБ-графику. Игровые приложения также часто содержат физические симуляторы, которые, как правило, хорошо распараллеливаются.
Проблема параллельного программирования для мобильных платформ _и подходы к ее решению
Обеспечить параллельное выполнение можно, либо создавая единицы выполнения вручную, т.е. создавая потоки, управляя их выполнением и уничтожая их по мере необходимости, либо используя директивы компилятора, чтобы он автоматически сгенерировал код для создания потоков и параллельного выполнения заданных фрагментов кода (данный подход используется в ОрепМР). Ручное распараллеливание программы чревато большим количеством трудноуловимых ошибок. Кроме того, не всегда удаётся найти эффективный метод распараллеливания.
2. Имеющиеся подходы и проблемы их применения
Проблемой автоматизации распараллеливания занимаются давно, однако, помимо достигаемой производительности, мобильные устройства имеют и другие характеристики, играющие значительную роль. Главной из них является энергопотребление, оказывающее влияние на автономность устройства и его тепловыделение, лимитирующее, в свою очередь, размеры устройства. В целом, параллельные вычисления обладают более высокой энергоэффективностью, чем последовательные [1]. Однако исследования существующих и новых методов автоматического распараллеливания проводились и продолжают проводиться на серверных и настольных конфигурациях, без учёта особенностей мобильных процессоров и особенностей их использования. Как замечают Миеттинен и Хирвисало [1], инструментов и подходов, ставших индустриальным стандартом в этой области, на данный момент принципиально недостаточно. Также недостаточен и объём научных публикаций по данной тематике.
По историческим причинам исследования в области автоматического распараллеливания касались, в первую очередь, языка FORTRAN, поскольку параллельное выполнение важно, в первую очередь, именно в научных расчётах. В меньшей степени затронут исследованиями язык С, но в настоящее время и для него имеется ряд средств автоматического распараллеливания, в том числе имеющих открытый исходный код. Для языка С++ известных программных средств, позволяющих произвести автоматическое распараллеливание кода, всего два: это Intel С++ Compiler и Sun Studio С++ Compiler (с недавних пор в этот список вошёл и компилятор Microsoft). Оба средства являются проприетарными. Недостатком данных программных средств является их неактуальность для мобильных платформ - процессоры Intel занимают лишь малый сектор рынка.
Для автоматизации распараллеливания предлагались различные подходы [2, 3, 4, 5]. Предлагаемые подходы можно разделить на несколько категорий: это распараллеливание времени выполнения, т.е. распараллеливание уже собранной программы в процессе выполнения (как
правило, средствами виртуальной машины), распараллеливание на этапе сборки (с использованием распараллеливающего компилятора), распараллеливание с преобразованием исходного кода. Стоит заметить, что распараллеливание времени выполнения, хотя и кажется перспективным в связи с потенциальной возможностью конфигурировать выполняемую программу с учётом особенностей конкретного устройства, меньше подходит для мобильных платформ по следующим причинам. Во-первых, далеко не любой анализ программы (особенно, если это относится к анализу совместного доступа к данным и областей синхронизации) может быть выполнен за приемлемое время. Во-вторых, дополнительные расчёты времени выполнения уменьшают время отклика (критичное с точки зрения потребительских качеств устройства) и повышают энергопотребление устройства, препятствуя переходам устройства в режимы пониженного энергопотребления. Таким образом, распараллеливание кода для мобильного устройства имеет смысл проводить до выполнения кода - на этапе создания кода или на этапе компиляции или сборки приложения.
Особенный интерес представляет распараллеливание исходного кода программы в автоматическом или автоматизированном режиме. В первом случае распараллеливающий инструмент самостоятельно преобразует код программы (source-to-source транслятор) [6], во втором - делает это при взаимодействии с программистом. Такой подход оправдан, поскольку, во-первых, позволяет сохранять контроль разработчика над поведением программы, а во-вторых, позволяет получать прогнозируемый результат. Кроме того, компиляторы для мобильных платформ отличаются разнообразием, причём для различных платформ зачастую используются различные компиляторы. Это сильно сужает область решения, основанного на распараллеливании во время генерации промежуточного или машинного кода. Трансляция же на уровне исходного кода позволяет использовать различные компиляторы, не акцентируя внимание на их особенностях.
Может представлять интерес подход, заключающийся в использовании компиляции программы на устройстве перед её выполнением (ahead-of-time). Первые шаги в этом направлении сделаны в системе выполнения ART (Android Runtime). Данная система выполнения не является распараллеливающей, но использование распараллеливающего компилятора может позволить производить распараллеливание с учётом особенностей аппаратуры и программного окружения конечного устройства. Но и у этого подхода есть несколько недостатков. Первый из них заключается в том, что необходимо производить компиляцию (или трансляцию, если речь идёт о байт-коде) непосредственно на процессоре устройства. Мобильные процессоры слабо приспособлены для выполнения подобных задач. Кроме того, мобильные устройства имеют ограниченный
Проблема параллельного программирования для мобильных платформ
_и подходы к ее решению
объём памяти и системные ресурсы, что также затрудняет трансляцию на конечном устройстве. Второй недостаток подхода связан с тем, что на устройстве со столь серьёзными ограничениями по быстродействию и времени реакции на действия пользователя можно проводить только простейшие виды анализа программы. Даже не распараллеливающая трансляция требует значительного времени ожидания пользователя и способна проводить только простейшие оптимизации. Анализ, необходимый для распараллеливающей трансляции, требует значительного времени и системных ресурсов. Последний недостаток связан с энергозатратами на трансляцию: в случае использования распараллеливающего транслятора с глубоким анализом возможно значительное уменьшение времени автономной работы устройства.
Наиболее удачным видится преобразование кода программы с использованием параллельных примитивов самой стандартной библиотеки языка программирования (например, это решение подойдёт для Java) либо библиотек параллельных примитивов для данного языка (в частности, если речь идёт о C/C++). Этот подход должен позволить сгенерировать легко читаемый и сопровождаемый код. Схожую функциональность предлагает Intel Parallel Studio, но он его алгоритмы недоступны для изучения, и, кроме того, этот продукт платформенно-зависим.
Вероятный подход заключается в статическом анализе кода программы и построении по результатам анализа нового кода. Задача состоит в преобразовании программы путём генерации кода с использованием некоторого набора примитивов оптимальным образом. Критерием оптимальности является производительность сгенерированного кода: время выполнения некоторого заданного количества операций либо количество выполняемых операций в единицу времени. Основную трудность в решении данной задачи составляет необходимость обеспечения корректности преобразования: исходный и преобразованный коды должны получать один и тот же результат при одинаковых входных данных. Для обеспечения корректности необходимо не просто преобразовывать код, но и осуществлять поиск зависимостей в коде, чтобы обеспечить необходимые взаимные исключения потоков и избежать взаимных блокировок в полученной программе, т.е. избегать побочных эффектов параллельного выполнения. Данный подход требует как локального анализа, т.е. анализа отдельных функций и базовых блоков программы, так и межпроцедурного анализа программы в целом.
3. Критерий оценки
Введем следующие обозначения: 1) X = (рс[,Х2> ■■■ вектор входных данных для каждого из п
прогонов программы, где х- вектор входных данных (аргументов
программы и оказывающих влияние на программу характеристик окружения) каждого конкретного прогона, ш - количество элементов вектора входных данных, одинаковое для всех прогонов.
2) у? = (и'1,ир2, - вектор весовых коэффициентов, который учитывает значимость каждого прогона для вычисления целевой функции. Например, это может быть вероятность появления на входе программы соответствующего набора данных. Эти параметры определяются разработчиком на этапе проектирования программы и уточняются при испытаниях прототипов.
3) N - количество процессоров системы.
4) Р - однопоточная программа, Р' = РЩ) - программа, распараллеленная на N процессоров. При этом следует учесть, что преобразование Р' должно быть корректным, то есть должно соблюдаться условие Р = Р'.
5) £ = £(Р',х) = (£1; ,.., £п) - время выполнения преобразованной программы в соответствии с прогоном.
6) е = е(Р\х) = (е1ге2гэнергия, затраченная на прогон программы. Энергия может измеряться в абсолютных величинах, например, А-ч (ампер-час) или в процентах от ёмкости батареи устройства.
7) С(Р, х) = £Г= 1 tj.Wj. - общая метрика для производительности программы с учётом её энергоэффективности. Под энергоэффективностью здесь и в дальнейшем будем понимать расход энергии, затраченный на один прогон программы.
Если в качестве целевой функции выбрать метрику для оценки
производительности распараллеленной программы, то оптимизационная
задача примет следующий вид:
£=1
Необходимость увеличения быстродействия программы диктует необходимость следующего ограничения для оптимизационной задачи:
Условие корректности преобразования диктует необходимость эквивалентности получаемых результатов исходной программы и программы, распараллеленной на N процессоров:
Хотя решение данной задачи в общем виде невозможно, полученные формулы могут использоваться в качестве критерия количественной оценки для сравнения исходной программы и преобразованных её вариантов между собой.
Кроме того, для энергоэффективности прогона программы можно получить и численное выражение. Примем следующие обозначения:
(1)
Р = Р'
т
(3)
Проблема параллельного программирования для мобильных платформ _и подходы к ее решению
1) А- ёмкость батареи устройства
2) £0 - время полного разряда батареи устройства без дополнительной нагрузки, кроме активностей ОС и фоновых процессов
3) £-| - время полного разряда батареи устройства при циклическом выполнении программы
4) Д Е - изменение заряда батареи устройства за промежуток времени, причём Д£ = А в случае полного разряда батареи
Д Е-
5) ИГ1 = —1 - расход энергии в единицу времени на выполнение
программы либо функций ОС и аппаратуры в режиме ожидания
6) п - количество итераций выполненний программы на момент замера
7) е - расход энергии на одну итерацию выполнения программы
Среднее энергопотребление системы без нагрузки можно вычислить следующим образом:
А (4)
При работе устройства под нагрузкой
£ = А (5)
1 №а; +
откуда получаем прирост энергопотребления при выполнении программы:
ш1 = --ша
Суммарное энергопотребление п итераций программы до момента разряда устройства:
Е1 = шгг1 = А-мй11 (7)
или, с учётом (4),
откуда расход энергии на один прогон программы при её циклическом выполнении:
г = ^ = (9)
И К Сс
Измерения необязательно проводить до полного разряда устройства. Можно выделить такие варианты, как прогон программы в течение какого-либо фиксированного времени и прогон фиксированного количества итераций выполнения программы.
Для фиксированного времени выполнения программы можно замерить изменение заряда устройства за данное время при нагрузке и без неё. Пусть за время £ заряд устройства меняется на ДЕ0 без нагрузки и на ДБ| при выполнении программы. Тогда:
Ег = дЕ1 - ДЕа (10)
Е% АЕ1 - ДЕ
(П)
е =
и
п
При фиксировании количества итераций п необходимо измерить скорость разряда устройства в режиме без нагрузки (Ид), суммарное время выполнения итераций и изменение заряда батареи под нагрузкой (ДЯх). Поскольку Д Б1 = Ж0£ + пе, для фиксированного количества итераций получаем:
и
Данные методики позволяют провести однозначную количественную оценку влияния изменений программы на её энергоэффективность.
4. Потенциальные методы решения проблемы
Наиболее многообещающим представляется решение задачи с использованием распознавания фрагментов кода, которые потенциально могут быть распараллелены. Для найденных фрагментов необходимо вычислить потенциальный прирост производительности, т.е. оценить, насколько увеличится производительность приложения от их параллельного выполнения. Для этого необходимо учесть факторы, влияющие на время выполнения, а именно, среднее время выполнения отдельных путей распараллеливаемого фрагмента и примерный процент времени, который распараллеливаемый фрагмент может провести в блокировках. Кроме того, на этом этапе необходимо учитывать стоимость запуска и приостановки потока. Для получения этих данных необходим анализ связей инструкций по данным. Также стоит отметить, что вычислительные ядра современных процессоров могут иметь различную производительность в зависимости от количества задействованных ядер.
На втором этапе предполагается производить подбор примитивов из набора параллельных примитивов для замены последовательных фрагментов. На этом же этапе решается проблема взаимного исключения на основе анализа графов связей, построенного на предыдущем этапе. Определяется, какие переменные будут внутренними для потока, а какие -разделяемыми между параллельно выполняющимися потоками, при этом разделяемые переменные, при необходимости, защищаются примитивами блокировок.
Последним этапом должна стать кодогенерация, т.е. замена последовательных инструкций кода параллельными. Непосредственно по окончании данного этапа, возможно, требуется дать программисту возможность определения имён вновь создаваемых структур данных и переменных.
В связи с особенностями исходного многопоточного кода наиболее вероятными примитивами многопоточности будут следующие:
1. Циклы в исходном коде. Стоит отметить, что, поскольку задачи, решаемые прикладными программистами, как правило, отличаются
- ЩзХ
1
(12)
е =
Проблема параллельного программирования для мобильных платформ
_и подходы к ее решению
от научных, поэтому необходимо использовать методы разрешения зависимостей в циклах, причём необходимо разрешать как зависимости по данным, так и зависимости по управлению, в том числе такие, как обработка исключений. С другой стороны, циклические конструкции в исходном коде встречаются часто и именно их выполнение занимает больше всего времени работы программы, поэтому распараллеливание циклов потенциально может дать значительный эффект как в отношении производительности, так и в отношении энергоэффективности приложения.
2. Конвейерные механизмы. Данное преобразование можно использовать внутри циклов, имеющих явные последовательные зависимости одних фрагментов от других. Данный механизм отличается тем, что позволяет распараллеливать циклы с заранее неизвестным количеством итераций, например, циклы while.
3. Разделение длительных или независимых операций в разные потоки. Это, в первую очередь, операции ввода-вывода или нераспараллеливаемые циклы, не зависящие друг от друга.
5. Выводы
В данной работе проведён анализ существующих подходов к распараллеливанию программного кода для многоядерных мобильных устройств и на его основе разработана методика для автоматического распараллеливания программного кода, написанного на языке Java. Разработана математическая модель для оценки эффективности работы программы, распараллеленной с использованием данной методики. Данная математическая модель учитывает как время выполнения программы, так и её энергоэффективность. Показывается, что существующая на данный момент задача автоматизации распараллеливания для мобильных платформ может быть эффективно решена с использованием source-to-source трансляции кода, поскольку данный подход не требует вычислительных ресурсов во время выполнения программы при автономной работе устройства и обеспечивает портируемость. В настоящий момент разрабатывается программное обеспечение, реализующее данную методику для ОС Android и поддерживаемых ей аппаратных платформ.
Список литературы
[1] Energy-efficient parallel software for mobile hand-held devices. Antti P. Miettinen (Nokia Research Center), Vesa Hirvisalo (Helsinki University of Technology). Proceedings of the First USENIX conference on Hot topics in parallelism, 2009, стр.: 6.
[2] Bryan Chan. Run-Time Support for the Automatic Parallelization of Java Programs. Graduate Department of Electrical and Computer Engineering, University of Toronto, 2002, стр.: 110
[3] Borys J. Bradel and Tarek S. Abdelrahman. Automatic Trace-Based Parallelization of Java Programs. Edward S. Rogers Sr. Department of Electrical and Computer Engineering, University of Toronto, 2008, стр.: 10.
[4] Michael K. Chen, Kunle Olukotun. The Jrpm System for Dynamically Parallelizing Java Programs. Stanford University, 2003, стр.: 12.
[5] D. F. Bacon, S. L. Graham, and O. J. Sharp. Compiler transformations for high-performance computing. ACM Computing Surveys, 1994, стр.:76.
[6] Альфред Ахо, Рави Сети, Джеффри Ульман. Компиляторы. Принципы, технологии, инструменты. Издательство Вильяме, 2008, стр.: 1175.