пятница, 25 мая 2007 г.

Параллельное программирование. Что делать?

Параллельное программирование. Что делать?



Автор - Joseph Landman(с) | scalability.org
Перевод - Петров Александр(с)

Ранее мы уже обсуждали некоторые аспекты будущего параллельного программирования и современных тенденций в области. Грубо говоря, на сегодняшний день мы можем выделить два типа параллельных систем и инструментов их создания – системы с разделяемой (общей) памятью и системы с распределенной памятью. Наиболее известными инструментами создания таких систем являются OpenMP и MPI, соответственно. Впрочем есть и другие типы систем, и соответственно другие инструменты, но наиболее широко распространены именно эти.

Проблемы?

Используя OpenMP, вы по сути дела снабжаете свой код некоторыми пометками, которые позволяют компилятору «развернуть» эти пометки в недостающий код. Т.е., другими словами, OpenMP является макрорасширением языка (языком в языке) – компилятор выполняет автоматическое распараллеливания вашего алгоритма, руководствуясь вашими пометками (иногда это вызывает интересные проблемы - пример). Однако OpenMP предполагает, что вы программируете систему с разделяемой памятью – OpenMP не способна генерировать код, предназначенный для распределенного исполнения.

Используя MPI, вы полновластно владеете своим кодом. Вы – а не компилятор выполняете основную работу, используя библиотечные функции MPI. Компилятор не способен оптимизировать (в плане параллелизма) ваш mpi-код, поскольку это всего лишь вызовы функций. Ещё хуже, что вы по сути дела находитесь в свободном плавании и вольны выбирать тот способ решения задачи, какой вам заблагорассудиться, понятно, что этот способ вовсе не обязательно будет лучшим или вообще правильным.

Раньше я уже говорил, о том, что нам следует пересмотреть те инструменты, языки, которые мы используем для параллельного программирования. Дело не в том будет ли программист сам писать весь код или отдаст часть задач на откуп компилятору – вопрос не в предоставлении программисту большей или меньшей степени контроля над кодом, вопрос в том, что нам нужны более выразительные средства управления параллелизмом.

Я вовсе не хочу сказать, что OpenMP – это плохо. Мне нравиться OpenMP, но мы не сможем использовать OpenMP в распределенных системах (а именно за ними будущее высокопроизводительных вычислений). Я не хочу сказать, что MPI – это плохо, просто MPI – это довольно-таки низкоуровневый инструмент, используя, MPI вам приходиться явно прописывать все то, что обычно генерирует за нас сам компилятор.

MPI – это что-то вроде ассемблера в параллельном программировании. Поэтому используя его, нужно быть готовым к тому, что совсем не просто написать рабочий код, неп росто его отлаживать, не просто его оптимизировать. Большинство программ MPI-программистов дают лишь 10-15% прирост производительности при увеличении числа ядер. Очень редко эта цифра достигает 50+%. Проблема не в программистах (а вы как думали?!) – проблема в том, что при распараллелировании крайне тяжело выделить действительно продолжительные по времени исполнения куски кода.

Другой вопрос, который заинтересовал меня, когда я размышлял о файловых системах и прочих хранилищах информации – это концепция локальности. Сегодня большинство MPI программ написаны так, словно распределенное взаимодействие происходит без задержек (моделирование и учет задержек эт.вообще большая проблема как в аппаратуре, так и в софте– вот, имхо, интересный пример) Сегодня мы как бы считаем, что все узлы нашей системы расположены локально – и в этом смысле, мы не учитываем задержки внутри нашей системы (или считаем величину этих задержек одинаковой и постоянной).

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

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

Программирование масштабных многопроцессорных систем будет только усложняться с ростом их масштаба. К тому же очевидно в скором времени появиться не-SMP системы (aSMP - асимметричные многопроцессорные системы), а также системы, использующие дополнительные сопроцессоры (называемые также акселераторами или APU (accelerator processing units)). Любая из этих нелокальных систем потребует четкого представления принципов распределенного (как в пространстве, так и во времени) доступа к данным. Надеюсь мы сможем преодолеть господствующие сегодня стереотипы и найти простые способы решения этих непростых проблем.

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

Это Фортран.

Joseph Landman(с).




Данная заметка показалась мне интересной, поскольку она более-менее отражает сегодняшние стремления параллельных программистов (нет не стремление программировать на Фортране, а необходимость создания более выразительных инструментов) в интересном свете распределенных систем.

Реальными примерами систем, о которых говорит автор, являются Googl-овская MapReduce и системы на основе идей GPGPU (General-Purpose computation on graphics processing units (GPUs)).
MapReduce - сильно распределенная система – это кластер из 1800 узлов (в каждом по 2 Intel Xeon 2GHz), между которыми распределяются данные и задания. При таком количестве узлов одной их основных проблем кластера является его отказоустойчивость – в том, смысле, что при выходе одного узла система должна перенаправить его данные и его задания другому узлу и т.д. Система называется MapReduce – поскольку модель этой системы основана на использовании двух широко распространенных в функциональном программировании функций: Map (отображение одного множества в другое путем применения функции преобразования к элементам исходного множества) и Reduce (получение агрегированной характеристики элементов множества (нпр. их суммы)).


GPGPU cистемы принадлежат к другому классу систем, которые автор, называет APU-системы. В таких системах (аппаратно-программных комплексах, по сути) центральному процессору аккомпанирует целый ряд внешних, обычно специализированных вычислителей. Это могут быть как ПЛИСы, так и микроконтроллеры или специализированные микропроцессоры (нпр. сигнальные(DSP) процессоры). Очевидно, что такие системы прямо-таки насыщены параллелизмом (вычислителей-то много) и они не являются однородными (в отличие, скажем, от сегодняшних многоядреных процессоров). Они не являются однородными поскольку в них могут использоваться различные каналы связи между элементами системы, что влечет отсутствие локальности, о которой говорит автор. В GPGPU системах, в качестве специализированного вычислителя выступает процессор видео карточки.