Справочник от Автор24
Поделись лекцией за скидку на Автор24

Высокопроизводительные вычислительные системы

  • 👀 731 просмотр
  • 📌 710 загрузок
Выбери формат для чтения
Загружаем конспект в формате doc
Это займет всего пару минут! А пока ты можешь прочитать работу в формате Word 👇
Конспект лекции по дисциплине «Высокопроизводительные вычислительные системы» doc
Лекциии по дисциплине «Высокопроизводительные вычислительные системы» Содержание Лекция 1. Введение в тему, общие сведения 4 1.1 Классификация компьютеров 4 1.2 История развития суперкомпьютеров 6 1.3 Направления развития высокопроизводительной вычислительной техники 9 1.4 Системы классификационных признаков суперкомпьютеров 10 1.5 Оценка производительности высокопроизводительных компьютеров и вычислительных систем 15 1.6 Закон Амдала. Теоретический и реальный рост производительности при распараллеливании вычислений 18 Лекция 2. Архитектура ЭВМ 19 2.1 Многоплановое толкование понятия “архитектура” 19 2.2 История архитектуры вычислителей 20 2.3 Архитектура системы команд. CISC и RISC процессоры 22 2.4 Матричный процессор 23 2.5 Процессор с конвейеризацией команд. Процессор с конвейеризацией операций 25 2.6 Суперскалярный процессор 28 2.7 Другие типы процессоров Лекция 3. Системы команд 31 Лекция 4. Виды памяти 35 4.1 Статическая и динамическая 35 4.2 Энергонезависимая память 36 4.3 Кэш 38 4.4 Flash 39 4.5 Контроль четности 40 4.6 Организация памяти 41 Лекция 5. Ускорение доступа к памяти и параллелилзм вычислений 44 5.1 Специальная организация памяти 44 5.2 RAID 45 5.3 Межпроцессорные связи 54 5.4 Симметричные мультипроцессорные системы (SMP Symmetric Multi-Processing) 55 5.5 Массивно-параллельные системы (МРР Massively Parallel Processing) 56 5.6 Параллельно-векторные системы (PVP)  57 5.7 Кластерные системы 58 Лекция 6. Организация связей в ЭВМ 59 Лекция 7. Коммуникационная сеть 62 7.1. Топологии сети передачи данных 62 7.2 Факторы, снижающие производительность параллельных компьютеров 64 Лекция 8. Кластерные системы 67 Лекция 9. Особенности ОС мультипроцессорных систем 76 9.1 Коммутация в распределенных системах 78 9.2 Синхронизация в распределенных системах 78 9.3 Распределенная общая память (DSM - Distributed Shared Memory) 79 9.4 Модели консистентности 80 9.5 Обеспечение надежности в распределенных системах 82 Лекция 10. Программирование для распределенных систем 88 10.1 Две модели программирования: последовательная и параллельная 89 10.2 Векторизация и распараллеливания программ 92 10.3 Классы задач с эффективной векторизацией или распараллеливанием 105 Лекция 11. Моделирование и анализ параллельных вычислений 107 11.1 Модель вычислений в виде графа "операции – операнды" 107 11.2 Описание схемы параллельного выполнения алгоритма 108 11.3 Определение времени выполнения параллельного алгоритма 108 11.4 Показатели эффективности параллельного алгоритма 110 11.5 Учебный пример. Вычисление частных сумм последовательности числовых значений 111 11.6 Оценка максимально достижимого параллелизма 115 11.7 Анализ масштабируемости параллельных вычислений 117 Лекция 12. Оценка коммуникационной трудоемкости параллельных алгоритмов 119 12.1 Общая характеристика механизмов передачи данных 119 12.2 Анализ трудоемкости основных операций передачи данных 120 12.3 Методы логического представления топологии коммуникационной среды 127 12.4 Оценка трудоемкости операций передачи данных для кластерных систем 128 Лекция 13. Принципы разработки параллельных методов 131 13.1 Моделирование параллельных программ 132 13.2 Этапы разработки параллельных алгоритмов 134 13.3 Параллельное решение гравитационной задачи N тел 139 Лекция 14. Параллельное программирование на основе MPI 142 14.1 MPI: основные понятия и определения 143 14.2 Введение в разработку параллельных программ с использованием MPI 145 14.3 Операции передачи данных между двумя процессами 156 14.4 Коллективные операции передачи данных 160 14.5 Производные типы данных в MPI 163 14.6 Управление группами процессов и коммуникаторами 170 14.7 Виртуальные топологии 174 Лекция 1. Введение в тему, общие сведения. 1.1 Классификация компьютеров: Персональные компьютеры Персональные компьютеры (ПК) появились в результате эволюции миникомпьютеров при переходе элементной базы машин с малой и средней степенью интеграции на большие и сверхбольшие интегральные схемы. ПК, благодаря своей низкой стоимости, очень быстро завоевали хорошие позиции на компьютерном рынке и создали предпосылки для разработки новых программных средств, ориентированных на конечного пользователя. Рабочие станции Миникомпьютеры стали прародителями и другого направления развития современных систем - настольных систем высокой производительности, которые сегодня известны как рабочие станции. Х-терминалы X-терминалы представляют собой комбинацию бездисковых рабочих станций и стандартных ASCII-терминалов. Бездисковые рабочие станции часто применялись в качестве дорогих дисплеев и в этом случае не полностью использовали локальную вычислительную мощь. Одновременно многие пользователи ASCII-терминалов хотели улучшить их характеристики, чтобы получить возможность работы в многооконной системе и графические возможности. На компьютерном рынке X-терминалы занимают промежуточное положение между персональными компьютерами и рабочими станциями. Поставщики X-терминалов заявляют, что их изделия более эффективны в стоимостном выражении, чем рабочие станции высокого ценового класса, и предлагают увеличенный уровень производительности по сравнению с персональными компьютерами. Как правило, стоимость X-терминалов составляет около половины стоимости сравнимой по конфигурации бездисковой машины и примерно четверть стоимости полностью оснащенной рабочей станции. Типовой X-терминал включает следующие элементы: • Экран высокого разрешения; • Микропроцессор • Отдельный графический сопроцессор в дополнение к основному процессору • Базовые системные программы, на которых работает система X-Windows и выполняются сетевые протоколы; • Программное обеспечение сервера X11; • Переменный объем локальной памяти для дисплея, сетевого интерфейса, поддерживающего TCP/IP и другие сетевые протоколы. • Порты для подключения клавиатуры и мыши. X-терминалы отличаются от ПК и рабочих станций не только тем, что не выполняет функции обычной локальной обработки. Работа X-терминалов зависит от главной (хост) системы, к которой они подключены посредством сети. X-Windows - результат совместной работы Масачусетского технологического института (MIT) и корпорации DEC. Система X-Windows (известная также под именем X) в настоящее время является открытым де-факто стандартом для доступа к множеству одновременно выполняющихся приложений с возможностями многооконного режима и графикой высокого разрешения на интеллектуальных терминалах, персональных компьютерах, рабочих станциях и X-терминалах. Она стала стандартом для обеспечения интероперабельности (переносимости) продуктов многих поставщиков и для организации доступа к множеству приложений. В настоящее время X-Windows является стандартом для разработки пользовательского интерфейса. Более 90% поставщиков UNIX-рабочих станций и многие поставщики персональных компьютеров адаптировали систему X-Windows и применяют в качестве стандарта. Серверы Прикладные многопользовательские коммерческие и бизнес-системы, включающие системы управления базами данных и обработки транзакций, крупные издательские системы, сетевые приложения и системы обслуживания коммуникаций, разработку программного обеспечения и обработку изображений все более настойчиво требуют перехода к модели вычислений "клиент-сервер" и распределенной обработке. В распределенной модели "клиент-сервер" часть работы выполняет сервер, а часть пользовательский компьютер (в общем случае клиентская и пользовательская части могут работать и на одном компьютере). Существует несколько типов серверов, ориентированных на разные применения: файл-сервер, сервер базы данных, принт-сервер, вычислительный сервер, сервер приложений. Таким образом, тип сервера определяется видом ресурса, которым он владеет (файловая система, база данных, принтеры, процессоры или прикладные пакеты программ). Суперсерверы Разбиение на серверы и суперсерверы весьма условно и отражает только мощность машины, в дальнейшем мы этот термин использовать не будем. Мэйнфреймы Мейнфрейм - это синоним понятия "большая универсальная ЭВМ". Мейнфреймы и до сегодняшнего дня остаются наиболее мощными (не считая суперкомпьютеров) вычислительными системами общего назначения, обеспечивающими непрерывный круглосуточный режим эксплуатации. Они могут включать один или несколько процессоров, каждый из которых, в свою очередь, может оснащаться векторными сопроцессорами (ускорителями операций с суперкомпьютерной производительностью). В нашем сознании мейнфреймы все еще ассоциируются с большими по габаритам машинами, требующими специально оборудованных помещений с системами водяного охлаждения и кондиционирования. Однако это не совсем так. Прогресс в области элементно-конструкторской базы позволил существенно сократить габариты основных устройств. Минисуперкомпьютеры и суперкомпьютеры Различие между суперкомпьютерами и минисуперкомпьютерами точно такого же толка, что и между серверами и суперсерверами. Вообще не слишком понятно, как отличить суперкомпьютер от обычного, например. Оксфордский толковый словарь по вычислительной технике, изданный в 1986 году, сообщает, что суперкомпьютер - это очень мощная ЭВМ с производительностью свыше 10 MFLOPS (миллионов операций с плавающей запятой в секунду). Сегодня этот результат перекрывают уже не только рабочие станции, но и ПК. В начале 90-х годов границу проводили уже около отметки в 300 MFLOPS. В 1996 году специалисты двух ведущих "суперкомпьютерных" стран, - США и Японии, - договорились о подъеме планки до 5 GFLOPS. Сейчас суперкомпьютер, занимающий 500 место в supercomputer top500 имеет производительность 67 Gflops. Более корректно, наверное, перечислить основные признаки, характеризующие суперЭВМ, среди которых кроме высокой производительности следует отметить: • самый современный технологический уровень (например, GaAs-технология); • специфические архитектурные решения, направленные на повышение быстродействия (например, наличие операций над векторами); • цена, обычно свыше 1-2 млн. долл. Определений суперкомпьютерам пытались давать много, иногда серьезных, иногда ироничных. В частности, лет пять назад, когда эта тема поднималась в конференции comp.parallel, Кен Батчер (Ken Batcher) предложил такой вариант: суперкомпьютер - это устройство, сводящее проблему вычислений к проблеме ввода/вывода. Все верно, в каждой шутке есть доля шутки: что раньше долго вычислялось, временами сбрасывая нечто на диск, на супер-ЭВМ может выполниться мгновенно, переводя стрелки неэффективности на относительно медленные устройства ввода/вывода. Кластеры Кластер - это связанный набор полноценных компьютеров, используемый в качестве единого ресурса. Под словосочетанием "полноценный компьютер" понимается завершенная компьютерная система, обладающая всем, что требуется для ее функционирования, включая процессоры, память, подсистему ввода/вывода, а также операционную систему, подсистемы, приложения и т.д. Обычно для этого годятся готовые компьютеры, которые могут обладать архитектурой SMP и даже NUMA. Словосочетание "единый ресурс" означает наличие программного обеспечения, дающего возможность пользователям, администраторам и даже приложениям считать, что имеется только одна сущность - кластер. Например, система пакетной обработки кластера позволяет послать задание на обработку кластеру, а не какому-нибудь отдельному компьютеру. Более сложным примером являются системы баз данных. У всех ведущих поставщиков систем баз данных имеются версии, работающие в параллельном режиме на нескольких машинах кластера. В результате приложения, использующие базу данных, не должны заботиться о том, где выполняется их работа. СУБД отвечает за синхронизацию параллельно выполняемых действий и поддержание целостности базы данных. Мета-компьютеры Что такое мета-компьютинг? Этот термин возник вместе с развитием высокоскоростной сетевой инфраструктуры в начале 90-х годов и относился к объединению нескольких разнородных вычислительных ресурсов в локальной сети организации для решения одной задачи. Основная цель построения мета-компьютера в то время заключалась в оптимальном распределении частей работы по вычислительным системам различной архитектуры и различной мощности. В дальнейшем, исследования в области технологий мета-компьютинга были развиты в сторону однородного доступа к вычислительным ресурсам большого числа (вплоть до нескольких тысяч) компьютеров в локальной или глобальной сети. Компонентами "мета-компьютера" могут быть как простейшие ПК, так и мощные массивно-параллельные системы. Что важно, мета-компьютер может не иметь постоянной конфигурации - отдельные компоненты могут включаться в его конфигурацию или отключаться от нее; при этом технологии мета-компьютинга обеспечивают непрерывное функционирование системы в целом. Современные исследовательские проекты в этой области направлены на обеспечение прозрачного доступа пользователей через Интернет к необходимым распределенным вычислительным ресурсам, а также прозрачного подключения простаивающих вычислительных систем к мета-компьютерам. Очевидно, что наилучшим образом для решения на мета-компьютерах подходят задачи переборного и поискового типа, где вычислительные узлы практически не взамодействуют друг с другом и основную часть работы производят в автономном режиме. Основная схема работы в этом случае примерно такая: специальный агент, расположенный на вычислительном узле (компьютере пользователя), определяет факт простоя этого компьютера, соединяется с управляющим узлом мета-компьютера и получает от него очередную порцию работы (область в пространстве перебора). По окончании счета по данной порции вычислительный узел передает обратно отчет о фактически проделанном переборе или сигнал о достижении цели поиска. 1.2 История развития суперкомпьютеров. А почему суперкомпьютеры считают так быстро? Вариантов ответа может быть несколько, среди которых два имеют явное преимущество: развитие элементной базы и использование новых решений в архитектуре компьютеров. Попробуем разобраться, какой из этих факторов оказывается решающим для достижения рекордной производительности. Обратимся к известным историческим фактам. На одном из первых компьютеров мира - EDSAC, появившемся в 1949 году в Кембридже и имевшем время такта 2 микросекунды (2*10-6 секунды), можно было выполнить 2*n арифметических операций за 18*n миллисекунд, то есть в среднем 100 арифметических операций в секунду. Сравним с одним вычислительным узлом современного суперкомпьютера Hewlett-Packard V2600: время такта приблизительно 1.8 наносекунды (1.8*10-9 секунд), а пиковая производительность около 77 миллиардов арифметических операций в секунду. Что же получается? За полвека производительность компьютеров выросла более, чем в семьсот миллионов раз. При этом выигрыш в быстродействии, связанный с уменьшением времени такта с 2 микросекунд до 1.8 наносекунд, составляет лишь около 1000 раз. Откуда же взялось остальное? Ответ очевиден -- использование новых решений в архитектуре компьютеров. Основное место среди них занимает принцип параллельной обработки данных, воплощающий идею одновременного (параллельного) выполнения нескольких действий. Стремительное развитие науки и проникновение человеческой мысли во все новые области вместе с решением поставленных прежде проблем постоянно порождает поток вопросов и ставит новые, как правило более сложные, задачи. Во времена первых компьютеров казалось, что увеличение их быстродействия в 100 раз позволит решить большинство проблем, однако гигафлопная производительность современных суперЭВМ сегодня является явно недостаточной для многих ученых. 1) Научные исследования, обычно сводящиеся к моделированию тех или иных сложно воспроизводимых процессов 2) Военные нужды, задачи типа C3I (command, control, communication & intelligence) 3) Коммерческая сфера. Речь идет не только скажем, о графических приложениях для кино и телевидения, где требуется все та же высокая производительность на операциях с плавающей запятой, а прежде всего о задачах, предполагающих интенсивную (в том числе,и оперативную) обработку транзакций для сверхбольших БД. В этот класс задач можно отнести также системы поддержки принятия решений и организация информационных складов Основные требования, которые предъявляют к суперкомпьютерам: 1) Отношение стоимость/производительность; 2) Надежность и отказоустойчивость; 3) Масштабируемость. Сегодня параллелизмом в архитектуре компьютеров уже мало кого удивишь. Все современные микропроцессоры используют тот или иной вид параллельной обработки. На презентациях новых чипов и в пресс-релизах корпораций это преподносится как последнее слово техники и передовой край науки, и это действительно так, если рассматривать реализацию этих принципов в миниатюрных рамках одного кристалла. Вместе с тем, сами эти идеи появились очень давно. Изначально они внедрялись в самых передовых, а потому единичных, компьютерах своего времени. Затем после должной отработки технологии и удешевления производства они спускались в компьютеры среднего класса, и наконец сегодня все это в полном объеме воплощается в рабочих станциях и персональных компьютерах. Для того чтобы убедиться, что все основные нововведения в архитектуре современных процессоров на самом деле используются еще со времен, когда ни микропроцессоров, ни понятия суперкомпьютеров еще не было, совершим маленький экскурс в историю, начав практически с момента рождения первых ЭВМ. IBM 701 (1953), IBM 704 (1955): разрядно-параллельная память, разрядно-параллельная арифметика. Все самые первые компьютеры (EDSAC, EDVAC, UNIVAC) имели разрядно-последовательную память, из которой слова считывались последовательно бит за битом. Первым коммерчески доступным компьютером, использующим разрядно-параллельную память (на CRT) и разрядно-параллельную арифметику, стал IBM 701, а наибольшую популярность получила модель IBM 704 (продано 150 экз.), в которой, помимо сказанного, была впервые применена память на ферритовых сердечниках и аппаратное АУ с плавающей точкой. IBM 709 (1958): независимые процессоры ввода/вывода. Процессоры первых компьютеров сами управляли вводом/выводом. Однако скорость работы самого быстрого внешнего устройства, а то тем временам это магнитная лента, была в 1000 раз меньше скорости процессора, поэтому во время операций ввода/вывода процессор фактически простаивал. В 1958г. к компьютеру IBM 704 присоединили 6 независимых процессоров ввода/вывода, которые после получения команд могли работать параллельно с основным процессором, а сам компьютер переименовали в IBM 709. Данная модель получилась удивительно удачной, так как вместе с модификациями было продано около 400 экземпляров, причем последний был выключен в 1975 году - 20 лет существования! IBM STRETCH (1961): опережающий просмотр вперед, расслоение памяти. В 1956 году IBM подписывает контракт с Лос-Аламосской научной лабораторией на разработку компьютера STRETCH, имеющего две принципиально важные особенности: опережающий просмотр вперед для выборки команд и расслоение памяти на два банка для согласования низкой скорости выборки из памяти и скорости выполнения операций. ATLAS (1963): конвейер команд. Впервые конвейерный принцип выполнения команд был использован в машине ATLAS, разработанной в Манчестерском университете. Выполнение команд разбито на 4 стадии: выборка команды, вычисление адреса операнда, выборка операнда и выполнение операции. Конвейеризация позволила уменьшить время выполнения команд с 6 мкс до 1,6 мкс. Данный компьютер оказал огромное влияние, как на архитектуру ЭВМ, так и на программное обеспечение: в нем впервые использована мультипрограммная ОС, основанная на использовании виртуальной памяти и системы прерываний. CDC 6600 (1964): независимые функциональные устройства. Фирма Control Data Corporation (CDC) при непосредственном участии одного из ее основателей, Сеймура Р.Крэя (Seymour R.Cray) выпускает компьютер CDC-6600 - первый компьютер, в котором использовалось несколько независимых функциональных устройств. Для сравнения с сегодняшним днем приведем некоторые параметры компьютера: • время такта 100нс, • производительность 2-3 млн. операций в секунду, • оперативная память разбита на 32 банка по 4096 60-ти разрядных слов, • цикл памяти 1мкс, • 10 независимых функциональных устройств. Машина имела громадный успех на научном рынке, активно вытесняя машины фирмы IBM. CDC 7600 (1969): конвейерные независимые функциональные устройства. CDC выпускает компьютер CDC-7600 с восемью независимыми конвейерными функциональные устройствами - сочетание параллельной и конвейерной обработки. Основные параметры: • такт 27,5 нс, • 10-15 млн. опер/сек., • 8 конвейерных ФУ, • 2-х уровневая память. ILLIAC IV (1974): матричные процессоры. Проект: 256 процессорных элементов (ПЭ) = 4 квадранта по 64ПЭ, возможность реконфигурации: 2 квадранта по 128ПЭ или 1 квадрант из 256ПЭ, такт 40нс, производительность 1Гфлоп; работы начаты в 1967 году, к концу 1971 изготовлена система из 1 квадранта, в 1974г. она введена в эксплуатацию, доводка велась до 1975 года; центральная часть: устройство управления (УУ) + матрица из 64 ПЭ; • УУ это простая ЭВМ с небольшой производительностью, управляющая матрицей ПЭ; все ПЭ матрицы работали в синхронном режиме, выполняя в каждый момент времени одну и ту же команду, поступившую от УУ, но над своими данными; • ПЭ имел собственное АЛУ с полным набором команд, ОП - 2Кслова по 64 разряда, цикл памяти 350нс, каждый ПЭ имел непосредственный доступ только к своей ОП; • сеть пересылки данных: двумерный тор со сдвигом на 1 по границе по горизонтали; Несмотря на результат в сравнении с проектом: стоимость в 4 раза выше, сделан лишь 1 квадрант, такт 80нс, реальная произв-ть до 50Мфлоп - данный проект оказал огромное влияние на архитектуру последующих машин, построенных по схожему принципу, в частности: PEPE, BSP, ICL DAP. CRAY 1 (1976): векторно-конвейерные процессоры В 1972 году С.Крэй покидает CDC и основывает свою компанию Cray Research, которая в 1976г. выпускает первый векторно-конвейерный компьютер CRAY-1: время такта 12.5нс, 12 конвейерных функциональных устройств, пиковая производительность 160 миллионов операций в секунду, оперативная память до 1Мслова (слово - 64 разряда), цикл памяти 50нс. Главным новшеством является введение векторных команд, работающих с целыми массивами независимых данных и позволяющих эффективно использовать конвейерные функциональные устройства. На этом означенный экскурс в историю можно смело закончить, поскольку роль параллелизма и его влияние на развитие архитектуры компьютеров уже очевидна. 1.3 Направления развития высокопроизводительной вычислительной техники 1. Векторно-конвейерные компьютеры. Конвейерные функциональные устройства и набор векторных команд - это две особенности таких машин. В отличие от традиционного подхода, векторные команды оперируют целыми массивами независимых данных, что позволяет эффективно загружать доступные конвейеры, т.е. команда вида A=B+C может означать сложение двух массивов, а не двух чисел. Характерным представителем данного направления является семейство векторно-конвейерных компьютеров CRAY куда входят, например, CRAY EL, CRAY J90, CRAY T90 (в марте 2000 года американская компания TERA перекупила подразделение CRAY у компании Silicon Graphics, Inc.). 2. Массивно-параллельные компьютеры с распределенной памятью. Идея построения компьютеров этого класса тривиальна: возьмем серийные микропроцессоры, снабдим каждый своей локальной памятью, соединим посредством некоторой коммуникационной среды - вот и все. Достоинств у такой архитектуры масса: если нужна высокая производительность, то можно добавить еще процессоров, если ограничены финансы или заранее известна требуемая вычислительная мощность, то легко подобрать оптимальную конфигурацию и т.п. Однако есть и решающий "минус", сводящий многие "плюсы" на нет. Дело в том, что межпроцессорное взаимодействие в компьютерах этого класса идет намного медленнее, чем происходит локальная обработка данных самими процессорами. Именно поэтому написать эффективную программу для таких компьютеров очень сложно, а для некоторых алгоритмов иногда просто невозможно. К данному классу можно отнести компьютеры Intel Paragon, IBM SP1, Parsytec, в какой-то степени IBM SP2 и CRAY T3D/T3E, хотя в этих компьютерах влияние указанного минуса значительно ослаблено. К этому же классу можно отнести и сети компьютеров, которые все чаще рассматривают как дешевую альтернативу крайне дорогим суперкомпьютерам. 3. Параллельные компьютеры с общей памятью. Вся оперативная память таких компьютеров разделяется несколькими одинаковыми процессорами. Это снимает проблемы предыдущего класса, но добавляет новые - число процессоров, имеющих доступ к общей памяти, по чисто техническим причинам нельзя сделать большим. В данное направление входят многие современные многопроцессорные SMP-компьютеры или, например, отдельные узлы компьютеров HP Exemplar и Sun StarFire. 4. Последнее направление, строго говоря, не является самостоятельным, а скорее представляет собой комбинации предыдущих трех. Из нескольких процессоров (традиционных или векторно-конвейерных) и общей для них памяти сформируем вычислительный узел. Если полученной вычислительной мощности не достаточно, то объединим несколько узлов высокоскоростными каналами. Подобную архитектуру называют кластерной, и по такому принципу построены CRAY SV1, HP Exemplar, Sun StarFire, NEC SX-5, последние модели IBM SP2 и другие. Именно это направление является в настоящее время наиболее перспективным для конструирования компьютеров с рекордными показателями производительности. 1.4 Системы классификационных признаков суперкомпьютеров. Наиболее перспективным и динамичным направлением увеличения скорости решения прикладных задач является широкое внедрение идей параллелизма в работу вычислительных систем. К настоящему времени спроектированы и опробованы сотни различных компьютеров, использующих в своей архитектуре тот или иной вид параллельной обработки данных. В научной литературе и технической документации можно найти более десятка различных названий, характеризующих лишь общие принципы функционирования параллельных машин: векторно-конвейерные, массивно-параллельные, компьютеры с широким командным словом, систолические массивы, гиперкубы, спецпроцессоры и мультипроцессоры, иерархические и кластерные компьютеры, dataflow, матричные ЭВМ и многие другие. Если же к подобным названиям для полноты описания добавить еще и данные о таких важных параметрах, как, например, организация памяти, топология связи между процессорами, синхронность работы отдельных устройств или способ исполнения арифметических операций, то число различных архитектур станет и вовсе необозримым. Попытки систематизировать все множество архитектур начались после опубликования М.Флинном первого варианта классификации вычислительных систем в конце 60-х годов и непрерывно продолжаются по сей день. Ясно, что навести порядок в хаосе очень важно для лучшего понимания исследуемой предметной области, однако нахождение удачной классификации может иметь целый ряд существенных следствий. Подобную классификацию хотелось бы найти и для архитектур параллельных вычислительных систем. Основной вопрос - что заложить в основу классификации, может решаться по-разному, в зависимости от того, для кого данная классификация создается и на решение какой задачи направлена. Так, часто используемое деление компьютеров на персональные ЭВМ, рабочие станции, мини--ЭВМ, большие универсальные ЭВМ, минисупер--ЭВМ и супер--ЭВМ, позволяет, быть может, примерно прикинуть стоимость компьютера. Однако она не приближает пользователя к пониманию того, что от него потребуется для написания программы, работающий на пределе производительности параллельного компьютера, т.е. того, ради чего он и решился его использовать. Как это ни странно, но от обилия разных параллельных компьютеров страдает, прежде всего, конечный пользователь, для которого, вроде бы, они и создавались: он вынужден каждый раз подбирать наиболее эффективный алгоритм, он испытывает на себе "прелести" параллельного программирования и отладки, решает проблемы переносимости и затем все повторяется заново. Хотелось бы, чтобы такая классификация помогла ему разобраться с тем, что представляет собой каждая архитектура, как они взаимосвязаны между собой, что он должен учитывать для написания действительно эффективных программ или же на какой класс архитектур ему следует ориентироваться для решения требуемого класса задач. Одновременно удачная классификация могла бы подсказать возможные пути совершенствования компьютеров и в этом смысле она должна быть достаточно содержательной. Трудно рассчитывать на нахождение нетривиальных "белых пятен", например, в классификации по стоимости, однако размышления о возможной систематике с точки зрения простоты и технологичности программирования могут оказаться чрезвычайно полезными для определения направлений поиска новых архитектур. 1.4.1 Классическая систематика Флинна. По-видимому, самой ранней и наиболее известной является классификация архитектур вычислительных систем, предложенная в 1966 году М.Флинном [1,2]. Классификация базируется на понятии потока, под которым понимается последовательность элементов, команд или данных, обрабатываемая процессором. На основе числа потоков команд и потоков данных Флинн выделяет четыре класса архитектур: SISD,MISD,SIMD,MIMD. SISD (single instruction stream / single data stream) - одиночный поток команд и одиночный поток данных. К этому классу относятся, прежде всего, классические последовательные машины, или иначе, машины фон-неймановского типа, например, PDP-11 или VAX 11/780. В таких машинах есть только один поток команд, все команды обрабатываются последовательно друг за другом и каждая команда инициирует одну операцию с одним потоком данных. Не имеет значения тот факт, что для увеличения скорости обработки команд и скорости выполнения арифметических операций может применяться конвейерная обработка - как машина CDC 6600 со скалярными функциональными устройствами, так и CDC 7600 с конвейерными попадают в этот класс. SIMD (single instruction stream / multiple data stream) - одиночный поток команд и множественный поток данных. В архитектурах подобного рода сохраняется один поток команд, включающий, в отличие от предыдущего класса, векторные команды. Это позволяет выполнять одну арифметическую операцию сразу над многими данными - элементами вектора. Способ выполнения векторных операций не оговаривается, поэтому обработка элементов вектора может производится либо процессорной матрицей, как в ILLIAC IV, либо с помощью конвейера, как, например, в машине CRAY-1. MISD (multiple instruction stream / single data stream) - множественный поток команд и одиночный поток данных. Определение подразумевает наличие в архитектуре многих процессоров, обрабатывающих один и тот же поток данных. Однако ни Флинн, ни другие специалисты в области архитектуры компьютеров до сих пор не смогли представить убедительный пример реально существующей вычислительной системы, построенной на данном принципе. Ряд исследователей [3,4,5] относят конвейерные машины к данному классу, однако это не нашло окончательного признания в научном сообществе. Будем считать, что пока данный класс пуст. MIMD (multiple instruction stream / multiple data stream) - множественный поток команд и множественный поток данных. Этот класс предполагает, что в вычислительной системе есть несколько устройств обработки команд, объединенных в единый комплекс и работающих каждое со своим потоком команд и данных. Итак, что же собой представляет каждый класс? В SISD, как уже говорилось, входят однопроцессорные последовательные компьютеры типа VAX 11/780. Однако, многими критиками подмечено, что в этот класс можно включить и векторно-конвейерные машины, если рассматривать вектор как одно неделимое данное для соответствующей команды. В таком случае в этот класс попадут и такие системы, как CRAY-1, CYBER 205, машины семейства FACOM VP и многие другие. Бесспорными представителями класса SIMD считаются матрицы процессоров: ILLIAC IV, ICL DAP, Goodyear Aerospace MPP, Connection Machine 1 и т.п. В таких системах единое управляющее устройство контролирует множество процессорных элементов. Каждый процессорный элемент получает от устройства управления в каждый фиксированный момент времени одинаковую команду и выполняет ее над своими локальными данными. Для классических процессорных матриц никаких вопросов не возникает, однако в этот же класс можно включить и векторно-конвейерные машины, например, CRAY-1. В этом случае каждый элемент вектора надо рассматривать как отдельный элемент потока данных. Класс MIMD чрезвычайно широк, поскольку включает в себя всевозможные мультипроцессорные системы: Cm*, C.mmp, CRAY Y-MP, Denelcor HEP,BBN Butterfly, Intel Paragon, CRAY T3D и многие другие. Интересно то, что если конвейерную обработку рассматривать как выполнение множества команд (операций ступеней конвейера) не над одиночным векторным потоком данных, а над множественным скалярным потоком, то все рассмотренные выше векторно-конвейерные компьютеры можно расположить и в данном классе. Предложенная схема классификации вплоть до настоящего времени является самой применяемой при начальной характеристике того или иного компьютера. Если говорится, что компьютер принадлежит классу SIMD или MIMD, то сразу становится понятным базовый принцип его работы, и в некоторых случаях этого бывает достаточно. Однако видны и явные недостатки. В частности, некоторые заслуживающие внимания архитектуры, например dataflow и векторно--конвейерные машины, четко не вписываются в данную классификацию. Другой недостаток - это чрезмерная заполненность класса MIMD. Необходимо средство, более избирательно систематизирующее архитектуры, которые по Флинну попадают в один класс, но совершенно различны по числу процессоров, природе и топологии связи между ними, по способу организации памяти и, конечно же, по технологии программирования. Наличие пустого класса (MISD) не стоит считать недостатком схемы. Такие классы, по мнению некоторых исследователей в области классификации архитектур [6,7], могут стать чрезвычайно полезными для разработки принципиально новых концепций в теории и практике построения вычислительных систем. Интересно также упомянуть о принципиально ином направлении в развитии компьютерных архитектур - машинах потоков данных. В середине 80-х годов многие исследователи полагали, что будущее высокопроизводительных ЭВМ связано именно с компьютерами, управляемыми потоками данных, в отличие от всех рассмотренных нами классов вычислительных систем, управляемых потоками команд. В машинах потоков данных могут одновременно выполняться сразу много команд, для которых готовы операнды. Хотя ЭВМ с такой архитектурой сегодня промышленно не выпускаются, некоторые элементы этого подхода нашли свое отражение в современных суперскалярных микропроцессорах, имеющих много параллельно работающих функциональных устройств и буфер команд, ожидающих готовности операндов. В качестве примеров таких микропроцессоров можно привести HP РА-8000 и Intel Pentium Pro. 1.4.2 Дополнения Ванга и Бриггса к классификации Флинна В книге К.Ванга и Ф.Бриггса [15] сделаны некоторые дополнения к классификации Флинна. Оставляя четыре ранее введенных базовых класса (SISD, SIMD, MISD, MIMD), авторы внесли следующие изменения. Например, в классе MIMD авторы различают • вычислительные системы со слабой связью между процессорами, к которым они относят все системы с распределенной памятью, например, Cosmic Cube, • и вычислительные системы с сильной связью (системы с общей памятью), куда попадают такие компьютеры, как C.mmp, BBN Butterfly, CRAY Y-MP, Denelcor HEP. Класс SISD разбивается на два подкласса: • архитектуры с единственным функциональным устройством, например, PDP-11; • архитектуры, имеющие в своем составе несколько функциональных устройств - CDC 6600, CRAY-1, FPS AP-120B, CDC Cyber 205, FACOM VP-200. В класс SIMD также вводится два подкласса: • архитектуры с пословно-последовательной обработкой информации - ILLIAC IV, PEPE, BSP; • архитектуры с разрядно-последовательной обработкой - STARAN, ICL DAP. 1.4.3 Классификация Фенга В 1972 году Т.Фенг предложил классифицировать вычислительные системы на основе двух простых характеристик. Первая - число бит n в машинном слове, обрабатываемых параллельно при выполнении машинных инструкций. Практически во всех современных компьютерах это число совпадает с длиной машинного слова. Вторая характеристика равна числу слов m, обрабатываемых одновременно данной вычислительной системой. Немного изменив терминологию, функционирование любого компьютера можно представить как параллельную обработку n битовых слоев, на каждом из которых независимо преобразуются m бит. Опираясь на такую интерпретацию, вторую характеристику обычно называют шириной битового слоя. Если рассмотреть предельные верхние значения данных характеристик, то каждую вычислительную систему C можно описать парой чисел (n,m) и представить точкой на плоскости в системе координат длина слова - ширина битового слоя. Площадь прямоугольника со сторонами n и m определяет интегральную характеристику потенциала параллельности P архитектуры и носит название максимальной степени параллелизма вычислительной системы: P(C)=mn. По существу, данное значение есть ничто иное, как пиковая производительность, выраженная в других единицах. В период появления данной классификации, а это начало 70-х годов, еще казалось возможным перенести понятие пиковой производительности как универсального средства сравнения и описания потенциальных возможностей компьютеров с традиционных последовательных машин на параллельные. Понимание того факта, что пиковая производительность сама по себе не столь важна, пришло позднее, и данный подход отражает, естественно, степень осмысления специфики параллельных вычислений того времени. Рассмотрим компьютер Advanced Scientific Computer фирмы Texas Instruments (TI ASC). В основном режиме он работает с 64-х разрядным словом, причем все разряды обрабатываются параллельно. Арифметико-логическое устройство имеет четыре одновременно работающих конвейера, содержащих по восемь ступеней. Такая организация дает 4x8=32 бита в каждом битовом слое, и значит компьютер TI ASC может быть представлен в виде (64,32). На основе введенных понятий все вычислительные системы в зависимости от способа обработки информации, заложенного в их архитектуру, можно разделить на четыре класса. • Разрядно-последовательные пословно-последовательные (n=m=1). В каждый момент времени такие компьютеры обрабатывают только один двоичный разряд. Представителем данного класса служит давняя система MINIMA с естественным описанием (1,1). • Разрядно-параллельные пословно-последовательные (n > 1 , m = 1). Большинство классических последовательных компьютеров, так же как и многие вычислительные системы, эксплуатируемые до сих пор, принадлежит к данному классу: IBM 701 с описанием (36,1), PDP-11 (16,1), IBM 360/50 и VAX 11/780 - обе с описанием (32,1). • Разрядно-последовательные пословно-параллельные (n = 1 , m > 1). Как правило вычислительные системы данного класса состоят из большого числа одноразрядных процессорных элементов, каждый из которых может независимо от остальных обрабатывать свои данные. Типичными примерами служат STARAN (1, 256) и MPP (1,16384) фирмы Goodyear Aerospace, прототип известной системы ILLIAC IV компьютер SOLOMON (1, 1024) и ICL DAP (1, 4096). • Разрядно-параллельные пословно-параллельные (n > 1, m > 1). Большая часть существующих параллельных вычислительных систем, обрабатывая одновременно mn двоичных разрядов, принадлежит именно к этому классу: ILLIAC IV (64, 64), TI ASC (64, 32), C.mmp (16, 16), CDC 6600 (60, 10), BBN Butterfly GP1000 (32, 256). Недостатки предложенной классификации достаточно очевидны и связаны со способом вычисления ширины битового слоя m. По существу Фенг не делает никакого различия между процессорными матрицами, векторно-конвейерными и многопроцессорными системами. Не делается акцент на том, за счет чего компьютер может одновременно обрабатывать более одного слова: множественности функциональных устройств, их конвейерности или же какого-то числа независимых процессоров. Если в системе N независимых процессоров имеют каждый по F конвейерных функциональных устройств с длиной конвейера L, то для вычисления ширины битового слоя надо просто найти произведение данных характеристик. Конечно же, опираясь на данную классификацию, достаточно трудно (а иногда и невозможно) осознать специфику той или иной вычислительной системы. Однако достоинством является введение единой числовой метрики для всех типов компьютеров, которая вместе с описанием потенциала вычислительных возможностей конкретной архитектуры позволяет сравнить любые два компьютера между собой. 1.4.4 Другие классификации. Собственно, существует масса других классификаций, но смысла приводить их я не вижу, многие из них просто расширяют таксономию Флинна, особенно MIMD класс. Из интересных видов классификации можно упомянуть классификацию Базу, которая строит классификацию по последовательность решений, принятых на этапе проектирования архитектуры. Классификация Базу: последовательность решений, принятых на этапе проектирования архитектуры. По мнению А.Базу (A.Basu), любую параллельную вычислительную систему можно однозначно описать последовательностью решений, принятых на этапе ее проектирования, а сам процесс проектирования представить в виде дерева [18]. В самом деле, корень дерева - это вычислительная система (см. рисунок), а последующие ярусы дерева, фиксируя уровень параллелизма, метод реализации алгоритма, параллелизм инструкций и способ управления, последовательно дополняют друг друга, формируя описание системы. Классификация Кришнамарфи: четыре качественные характеристики параллелизма (степень гранулярности параллелизма, способ реализации, топология и природа связи процесоров, способ управления процессорами 1.5 Оценка производительности высокопроизводительных компьютеров и вычислительных систем. Оценка эффективности вычислительных систем на специально подготовленных для этих целей задачах (бенчмарках) или оценочное тестирование изначально имело цель дать прогноз относительно возможностей исследуемой системы при решении интересующего класса задач. Это важное для пользователей назначение бенчмарков является основным и в настоящее время. По мере накопления бенчмарков, опыта их использования и результатов анализа полученных данных стало ясно, что бенчмарки, как бы их не критиковали за ограниченнось и неадекватность, в достаточной на практике степени фиксируют характер и основные особенности решаемых задач. Это оказалось чрезвычайно полезным для разработчиков вычислительных систем, позволило оптимизировать разрабатываемую аппаратуру под выявленные на бенчмарках вычислительные процессы и закономерности. Важность применения оценочного тестирования значительно возросла в последнее время, когда стало возможным и экономически выгодным строить системы из готовых компонентов - вычислительных узлов на базе материнских плат персональных машин или рабочих станций и высокоскоростных коммутационных сетей. Правильность выбора компонентов в таких работах – основа успеха. Таким образом, оценочное тестирование имеет по крайней мере две цели – обеспечить необходимой информацией пользователя и разработчика вычислительной системы. Производительность ЦП зависит от трех параметров: такта (или частоты) синхронизации, среднего количества тактов на команду и количества выполняемых команд. Невозможно изменить ни один из указанных параметров изолированно от другого, поскольку базовые технологии, используемые для изменения каждого из этих параметров, взаимосвязаны: частота синхронизации определяется технологией аппаратных средств и функциональной организацией процессора; среднее количество тактов на команду зависит от функциональной организации и архитектуры системы команд; а количество выполняемых в программе команд определяется архитектурой системы команд и технологией компиляторов. Когда сравниваются две машины, необходимо рассматривать все три компоненты, чтобы понять относительную производительность. В процессе поиска стандартной единицы измерения производительности компьютеров было принято несколько популярных единиц измерения, вследствие чего несколько безвредных терминов были искусственно вырваны из их хорошо определенного контекста и использованы там, для чего они никогда не предназначались. В действительности единственной подходящей и надежной единицей измерения производительности является время выполнения реальных программ, и все предлагаемые замены этого времени в качестве единицы измерения или замены реальных программ в качестве объектов измерения на синтетические программы только вводят в заблуждение. Опасности некоторых популярных альтернативных единиц измерения (MIPS и MFLOPS) будут рассмотрены в соответствующих подразделах. MIPS Одной из альтернативных единиц измерения производительности процессора (по отношению к времени выполнения) является MIPS - (миллион команд в секунду). Имеется несколько различных вариантов интерпретации определения MIPS. В общем случае MIPS есть скорость операций в единицу времени, т.е. для любой данной программы MIPS есть просто отношение количества команд в программе к времени ее выполнения. Таким образом, производительность может быть определена как обратная к времени выполнения величина, причем более быстрые машины при этом будут иметь более высокий рейтинг MIPS. Положительными сторонами MIPS является то, что эту характеристику легко понять, особенно покупателю, и что более быстрая машина характеризуется большим числом MIPS, что соответствует нашим интуитивным представлениям. Однако использование MIPS в качестве метрики для сравнения наталкивается на три проблемы. Во-первых, MIPS зависит от набора команд процессора, что затрудняет сравнение по MIPS компьютеров, имеющих разные системы команд. Во-вторых, MIPS даже на одном и том же компьютере меняется от программы к программе. В-третьих, MIPS может меняться по отношению к производительности в противоположенную сторону. Классическим примером для последнего случая является рейтинг MIPS для машины, в состав которой входит сопроцессор плавающей точки. Поскольку в общем случае на каждую команду с плавающей точкой требуется большее количество тактов синхронизации, чем на целочисленную команду, то программы, используя сопроцессор плавающей точки вместо соответствующих подпрограмм из состава программного обеспечения, выполняются за меньшее время, но имеют меньший рейтинг MIPS. При отсутствии сопроцессора операции над числами с плавающей точкой реализуются с помощью подпрограмм, использующих более простые команды целочисленной арифметики и, как следствие, такие машины имеют более высокий рейтинг MIPS, но выполняют настолько большее количество команд, что общее время выполнения значительно увеличивается. Подобные аномалии наблюдаются и при использовании оптимизирующих компиляторов, когда в результате оптимизации сокращается количество выполняемых в программе команд, рейтинг MIPS уменьшается, а производительность увеличивается. Другое определение MIPS связано с очень популярным когда-то компьютером VAX 11/780 компании DEC. Именно этот компьютер был принят в качестве эталона для сравнения производительности различных машин. Считалось, что производительность VAX 11/780 равна 1MIPS (одному миллиону команд в секунду). В то время широкое распространение получил синтетический тест Dhrystone, который позволял оценивать эффективность процессоров и компиляторов с языка C для программ нечисловой обработки. Он представлял собой тестовую смесь, 53% которой составляли операторы присваивания, 32% - операторы управления и 15% - вызовы функций. Это был очень короткий тест: общее число команд равнялось 100. Скорость выполнения программы из этих 100 команд измерялась в Dhrystone в секунду. Быстродействие VAX 11/780 на этом синтетическом тесте составляло 1757Dhrystone в секунду. Таким образом 1MIPS равен 1757 Dhrystone в секунду. Следует отметить, что в настоящее время тест Dhrystone практически не применяется. Малый объем позволяет разместить все команды теста в кэш-памяти первого уровня современного микропроцессора и он не позволяет даже оценить эффект наличия кэш-памяти второго уровня, хотя может хорошо отражать эффект увеличения тактовой частоты. Whetstone - Синтетический тест, ориентированный на численное программирование (с плавающей запятой). Не учитывает кэш. Третье определение MIPS связано с IBM RS/6000 MIPS. Дело в том, что ряд производителей и пользователей (последователей фирмы IBM) предпочитают сравнивать производительность своих компьютеров с производительностью современных компьютеров IBM, а не со старой машиной компании DEC. Соотношение между VAX MIPS и RS/6000 MIPS никогда широко не публиковались, но 1 RS/6000 MIPS примерно равен 1.6 VAX 11/780 MIPS. MFLOPS Измерение производительности компьютеров при решении научно-технических задач, в которых существенно используется арифметика с плавающей точкой, всегда вызывало особый интерес. Именно для таких вычислений впервые встал вопрос об измерении производительности, а по достигнутым показателям часто делались выводы об общем уровне разработок компьютеров. Обычно для научно-технических задач производительность процессора оценивается в MFLOPS (миллионах чисел-результатов вычислений с плавающей точкой в секунду, или миллионах элементарных арифметических операций над числами с плавающей точкой, выполненных в секунду). Как единица измерения, MFLOPS, предназначена для оценки производительности только операций с плавающей точкой, и поэтому не применима вне этой ограниченной области. Например, программы компиляторов имеют рейтинг MFLOPS близкий к нулю вне зависимости от того, насколько быстра машина, поскольку компиляторы редко используют арифметику с плавающей точкой. Ясно, что рейтинг MFLOPS зависит от машины и от программы. Этот термин менее безобидный, чем MIPS. Он базируется на количестве выполняемых операций, а не на количестве выполняемых команд. По мнению многих программистов, одна и та же программа, работающая на различных компьютерах, будет выполнять различное количество команд, но одно и то же количество операций с плавающей точкой. Именно поэтому рейтинг MFLOPS предназначался для справедливого сравнения различных машин между собой. Однако и с MFLOPS не все обстоит так безоблачно. Прежде всего, это связано с тем, что наборы операций с плавающей точкой не совместимы на различных компьютерах. Например, в суперкомпьютерах фирмы Cray Research отсутствует команда деления (имеется, правда, операция вычисления обратной величины числа с плавающей точкой, а операция деления может быть реализована с помощью умножения делимого на обратную величину делителя). В то же время многие современные микропроцессоры имеют команды деления, вычисления квадратного корня, синуса и косинуса. Другая, осознаваемая всеми, проблема заключается в том, что рейтинг MFLOPS меняется не только на смеси целочисленных операций и операций с плавающей точкой, но и на смеси быстрых и медленных операций с плавающей точкой. Например, программа со 100% операций сложения будет иметь более высокий рейтинг, чем программа со 100% операций деления. Решение обеих проблем заключается в том, чтобы взять "каноническое" или "нормализованное" число операций с плавающей точкой из исходного текста программы и затем поделить его на время выполнения. На рис. 3.1 показано, каким образом авторы тестового пакета "Ливерморские циклы", о котором речь пойдет ниже, вычисляют для программы количество нормализованных операций с плавающей точкой в соответствии с операциями, действительно находящимися в ее исходном тексте. Таким образом, рейтинг реальных MFLOPS отличается от рейтинга нормализованных MFLOPS, который часто приводится в литературе по суперкомпьютерам. Реальные операции с ПТ Нормализованные операции с ПТ Сложение, вычитание, сравнение, умножение 1 Деление, квадратный корень 4 Экспонента, синус, ... 8 1.6 Закон Амдала. Теоретический и реальный рост производительности при распараллеливании вычислений. Предположим, что в вашей программе доля операций, которые нужно выполнять последовательно, равна f, где 0<=f<=1 (при этом доля понимается не по статическому числу строк кода, а по числу операций в процессе выполнения). Крайние случаи в значениях f соответствуют полностью параллельным (f=0) и полностью последовательным (f=1) программам. Так вот, для того, чтобы оценить, какое ускорение S может быть получено на компьютере из 'p' процессоров при данном значении f, можно воспользоваться законом Амдала: Если 9/10 программы исполняется параллельно, а 1/10 по-прежнему последовательно, то ускорения более, чем в 10 раз получить в принципе невозможно вне зависимости от качества реализации параллельной части кода и числа используемых процессоров (ясно, что 10 получается только в том случае, когда время исполнения параллельной части равно 0). Посмотрим на проблему с другой стороны: а какую же часть кода надо ускорить (а значит и предварительно исследовать), чтобы получить заданное ускорение? Ответ можно найти в следствии из закона Амдала: для того чтобы ускорить выполнение программы в q раз необходимо ускорить не менее, чем в q раз не менее, чем (1-1/q)-ю часть программы. Следовательно, если есть желание ускорить программу в 100 раз по сравнению с ее последовательным вариантом, то необходимо получить не меньшее ускорение не менее, чем на 99.99% кода, что почти всегда составляет значительную часть программы! Лекция 2. Архитектура ЭВМ 2.1 Многоплановое толкование понятия “архитектура”. Термин "архитектура системы" часто употребляется как в узком, так и в широком смысле этого слова. В узком смысле под архитектурой понимается архитектура набора команд. Архитектура набора команд служит границей между аппаратурой и программным обеспечением и представляет ту часть системы, которая видна программисту или разработчику компиляторов. Следует отметить, что это наиболее частое употребление этого термина. В широком смысле архитектура охватывает понятие организации системы, включающее такие высокоуровневые аспекты разработки компьютера как систему памяти, структуру системной шины, организацию ввода/вывода и т.п. Применительно к вычислительным системам термин "архитектура" может быть определен как распределение функций, реализуемых системой, между ее уровнями, точнее как определение границ между этими уровнями. Таким образом, архитектура вычислительной системы предполагает многоуровневую организацию. Архитектура первого уровня определяет, какие функции по обработке данных выполняются системой в целом, а какие возлагаются на внешний мир (пользователей, операторов, администраторов баз данных и т.д.). Система взаимодействует с внешним миром через набор интерфейсов: языки (язык оператора, языки программирования, языки описания и манипулирования базой данных, язык управления заданиями) и системные программы (программы-утилиты, программы редактирования, сортировки, сохранения и восстановления информации). Интерфейсы следующих уровней могут разграничивать определенные уровни внутри программного обеспечения. Например, уровень управления логическими ресурсами может включать реализацию таких функций, как управление базой данных, файлами, виртуальной памятью, сетевой телеобработкой. К уровню управления физическими ресурсами относятся функции управления внешней и оперативной памятью, управления процессами, выполняющимися в системе. Следующий уровень отражает основную линию разграничения системы, а именно границу между системным программным обеспечением и аппаратурой. Эту идею можно развить и дальше и говорить о распределении функций между отдельными частями физической системы. Например, некоторый интерфейс определяет, какие функции реализуют центральные процессоры, а какие - процессоры ввода/вывода. Архитектура следующего уровня определяет разграничение функций между процессорами ввода/вывода и контроллерами внешних устройств. В свою очередь можно разграничить функции, реализуемые контроллерами и самими устройствами ввода/вывода (терминалами, модемами, накопителями на магнитных дисках и лентах). Архитектура таких уровней часто называется архитектурой физического ввода/вывода. С точки зрения Конструктора системы наиболее важными являются способы построения (архитектурные решения) системы, удовлетворяющие сформулированным заказчиком требованиям. В начале проектирования системы Конструктор в целом представляет систему в виде совокупности функциональных блоков, определенным образом связанных (ЛОГИЧЕСКИЕ компоненты системы). Далее Конструктор системы на основании состояния дел и перспектив развития выбирает элементную базу, среды передачи данных (ФИЗИЧЕСКИЕ компоненты системы), операционную систему, системы программирования (ПРОГРАММННЫЕ компоненты системы). Наиболее строгое определение понятие АРХИТЕКТУРЫ СИСТЕМ, по-видимому, приведено у Э.А.Якубайтиса [Якубайтис Э.А. Архитектура вычислительных сетей. М.: Статистика, 1980. 279 с., ил.]: - "АРХИТЕКТУРА СИСТЕМЫ является емким понятием, включающем три важнейших вида взаимосвязанных структур: ФИЗИЧЕСКУЮ, ЛОГИЧЕСКУЮ и ПРОГРАММНУЮ. Кроме того, анализируя другие аспекты архитектуры, часто рассматривают структуры административного управления, обслуживания и ремонта. Каждая из этих структур определяется набором элементов и характером их взаимосвязи. Связь структур друг с другом образует АРХИТЕКТУРУ рассматриваемой СИСТЕМЫ. Элементами ФИЗИЧЕСКОЙ СТРУКТУРЫ являются технические объекты. В зависимости от того, какие задачи решаются, этими объектами могут быть полупроводниковые кристаллы, части вычислительных машин, а также комплексы, составленные из последних. Элементами ЛОГИЧЕСКОЙ СТРУКТУРЫ являются функции, определяющие основные операции. Очень важной характеристикой архитектуры системы является также ее ПРОГРАММНАЯ СТРУКТУРА. Эту структуру образуют взаимосвязанные программы: программы обработки информации, и др. Таким образом, АРХИТЕКТУРА СИСТЕМЫ (вычислительной сети, терминального комплекса, вычислительной машины, полупроводникового кристалла) является концепция взаимосвязи большого числа различного типа элементов. Она в основном характеризуется переплетением ФИЗИЧЕСКОЙ, ЛОГИЧЕСКОЙ И ПРОГРАММНОЙ СТРУКТУР этой системы". Известны два широко распространенных типа архитектуры вычислителей: фон-неймановская и гарвардская. • Фон-неймановская Машины фон-Неймана хранят программу и данные в одной и той же области памяти. В машинах этого типа команды содержат указание, что выполнить, и адрес данных, подлежащих обработке. Внутри этой машины имеются два основных функциональных блока. Арифметико-логическое устройство (АЛУ) выполняет самые важные операции: умножение, сложение, вычитание и многие другие. На основе этих очень простых, но важных операций, можно создать такое сложное программное средство как текстовый редактор. Другой блок – устройство ввода/вывода управляет потоком внешних, относительно машины, данных. • Гарвардская архитектура Единственное отличие гарвардской архитектуры состоит в том, что память программ и память данных разделены, и они используют физически разделенные линии передачи. Это позволяет подобной машине пересылать команды и данные одновременно. Следовательно, такая конструкция может значительно увеличить производительность ЦПОС. Гарвардская машина, как и фон-неймановская, имеет арифметико-логическое устройство и устройство ввода/вывода. К сожалению, расплатой за высокую скорость является высокая цена процессора. 2.2 История архитектуры вычислителей История рассмотренных архитектур очень интересна. Гарвардская архитектура была разработана Говардом Айхеном (Howard Aiken) в конце 1930-х годов в Гарвардском университете (отсюда - название). Первая машина Harvard Mark1 работала уже в 1944 году. За ней в 1946 году последовал «электронный числовой интегратор и калькулятор» (Electronic Numerical Integrator and Calculator - ENIAC), разработанный Пенсильванским университетом. Джон фон-Нейман, математик венгерского происхождения, предложил простейшую архитектуру, а именно – с объединенной памятью программного продукта и данных. С тех пор это простое решение стало стандартом. Машина фон-Неймана была создана в Принстонском институте новейших исследований в 1951 году. Персональные компьютеры общего назначения используют процессоры, построенные согласно архитектуре фон-Неймана. Другие семейства компьютеров также используют архитектуру фон-Неймана. Гарвардская архитектура является наиболее подходящей для специализированных микропроцессоров, предназначенных для решения прикладных задач в реальном времени. DSP (Digital Signal Procesors). Цифровые процессоры обработки сигналов обычно используют гарвардскую архитектуру, хотя существуют и процессоры с фон-неймановской архитектурой. Однако гарвардской архитектуре присущ один недостаток. Вследствие того, что память данных и память программ разделены, на кристалле необходимо иметь в два раза больше выводов адреса и данных. К сожалению, кремниевая технология такова, что увеличение числа выводов на кристалле приводит к росту цены. Однако блестящие инженеры-электронщики, которые долго бились над этой проблемой, предложили изящное решение. Оно состоит в том, чтобы для всех внешних данных, включая команды, использовать одну шину, а другую – для адресации, внутри же процессора иметь отдельную шину команд и шину данных и две соответствующих шины адреса. Разделение информации о программе и данных на выводах процессора производится благодаря их временному разделению (мультиплексированию). На это требуется два командных цикла: в первом цикле на выводы поступает информация о программе, а во втором на эти же выводы поступают данные. Затем все повторяется. Такие машины называются процессорами «с модифицированной гарвардской архитектурой». На рисунке приведены неймановская и гарвардские архитектуры: Фон Неймановская архитектура. Гарвардская архитектура Машина состоит из блока управления, арифметико-логического устройства (АЛУ), памяти и устройств ввода-вывода. В ней реализуется концепция хранимой программы: программы и данные хранятся в одной и той же памяти. Выполняемые действия определяются блоком управления и АЛУ, которые вместе являются основой центрального процессора. Центральный процессор выбирает и исполняет команды из памяти последовательно, адрес очередной команды задается “счетчиком адреса” в блоке управления. Этот принцип исполнения называется последовательной передачей управления. Фон-неймановская архитектура (или Гарвардская) — не единственные вариант построения ЭВМ; есть и другие, которые не соответствуют указанным принципам. Однако подавляющее большинство современных компьютеров основаны именно на указанных принципах, включая и сложные многопроцессорные комплексы, которые можно рассматривать как объединение фон-неймановских машин. Центральный процессор (АЛУ с блоком управления) реализуется микропроцессором семейства x86 — от 8086/88 до Pentium III и Athlon. При всей своей внутренней суперскалярности, суперконвейеризированности и спекулятивности (см. “КИ”, № 3/2000), внешне процессор соблюдает вышеупомянутый принцип последовательной передачи управления. Набор арифметических и логических команд насчитывает несколько сотен инструкций, а для потоковой обработки придуман принцип SIMD — множество комплектов данных, обрабатываемых одной инструкцией (расширения MMX, 3DNow!, SSE). Итак, выделим 3 основных признака фон Неймановской архитектуры (Принстонская): • память состоит из последовательности ячеек памяти с адресами; • хранение команд программы и обрабатываемых ими данных - на одинаковых принципах (с точки зрения обработки сообщений); • программа выполняется покомандно, в соответствии с их порядком. УУ – централизовано. Почему ФН уже не удовлетворяет? Первое – это скорость, сейчас в усредненной задачи от скорости работы CPU зависит не так уж много – важнее скорость работы памяти и других передач данных. Узкое место – единый тракт. Альтернатива – параллельная обработка, совмещение операций. ФН очень ориентирован на машинные команды, возникает семантический разрыв между языками высокого уровня. 2.3 Архитектура системы команд. CISC и RISC процессоры Как уже было отмечено, архитектура набора команд служит границей между аппаратурой и программным обеспечением и представляет ту часть системы, которая видна программисту или разработчику компиляторов. Двумя основными архитектурами набора команд, используемыми компьютерной промышленностью на современном этапе развития вычислительной техники являются архитектуры CISC и RISC. Основоположником CISC-архитектуры можно считать компанию IBM с ее базовой архитектурой /360, ядро которой используется с1964 года и дошло до наших дней, например, в таких современных мейнфреймах как IBM ES/9000. Лидером в разработке микропроцессоров c полным набором команд (CISC - Complete Instruction Set Computer) считается компания Intel со своей серией x86 и Pentium. Эта архитектура является практическим стандартом для рынка микрокомпьютеров. Для CISC-процессоров характерно: сравнительно небольшое число регистров общего назначения; большое количество машинных команд, некоторые из которых нагружены семантически аналогично операторам высокоуровневых языков программирования и выполняются за много тактов; большое количество методов адресации; большое количество форматов команд различной разрядности; преобладание двухадресного формата команд; наличие команд обработки типа регистр-память. Основой архитектуры современных рабочих станций и серверов является архитектура компьютера с сокращенным набором команд (RISC - Reduced Instruction Set Computer). Зачатки этой архитектуры уходят своими корнями к компьютерам CDC6600, разработчики которых (Торнтон, Крэй и др.) осознали важность упрощения набора команд для построения быстрых вычислительных машин. Эту традицию упрощения архитектуры С. Крэй с успехом применил при создании широко известной серии суперкомпьютеров компании Cray Research. Однако окончательно понятие RISC в современном его понимании сформировалось на базе трех исследовательских проектов компьютеров: процессора 801 компании IBM, процессора RISC университета Беркли и процессора MIPS Стенфордского университета. Разработка экспериментального проекта компании IBM началась еще в конце 70-х годов, но его результаты никогда не публиковались и компьютер на его основе в промышленных масштабах не изготавливался. В 1980 году Д.Паттерсон со своими коллегами из Беркли начали свой проект и изготовили две машины, которые получили названия RISC-I и RISC-II. Главными идеями этих машин было отделение медленной памяти от высокоскоростных регистров и использование регистровых окон. В 1981году Дж.Хеннесси со своими коллегами опубликовал описание стенфордской машины MIPS, основным аспектом разработки которой была эффективная реализация конвейерной обработки посредством тщательного планирования компилятором его загрузки. Эти три машины имели много общего. Все они придерживались архитектуры, отделяющей команды обработки от команд работы с памятью, и делали упор на эффективную конвейерную обработку. Система команд разрабатывалась таким образом, чтобы выполнение любой команды занимало небольшое количество машинных тактов (предпочтительно один машинный такт). Сама логика выполнения команд с целью повышения производительности ориентировалась на аппаратную, а не на микропрограммную реализацию. Чтобы упростить логику декодирования команд использовались команды фиксированной длины и фиксированного формата. Среди других особенностей RISC-архитектур следует отметить наличие достаточно большого регистрового файла (в типовых RISC-процессорах реализуются 32 или большее число регистров по сравнению с 8 - 16 регистрами в CISC-архитектурах), что позволяет большему объему данных храниться в регистрах на процессорном кристалле большее время и упрощает работу компилятора по распределению регистров под переменные. Для обработки, как правило, используются трехадресные команды, что помимо упрощения дешифрации дает возможность сохранять большее число переменных в регистрах без их последующей перезагрузки. Ко времени завершения университетских проектов (1983-1984 гг.) обозначился также прорыв в технологии изготовления сверхбольших интегральных схем. Простота архитектуры и ее эффективность, подтвержденная этими проектами, вызвали большой интерес в компьютерной индустрии и с 1986 года началась активная промышленная реализация архитектуры RISC. К настоящему времени эта архитектура прочно занимает лидирующие позиции на мировом компьютерном рынке рабочих станций и серверов. Развитие архитектуры RISC в значительной степени определялось прогрессом в области создания оптимизирующих компиляторов. Именно современная техника компиляции позволяет эффективно использовать преимущества большего регистрового файла, конвейерной организации и большей скорости выполнения команд. Современные компиляторы используют также преимущества другой оптимизационной техники для повышения производительности, обычно применяемой в процессорах RISC: реализацию задержанных переходов и суперскалярной обработки, позволяющей в один и тот же момент времени выдавать на выполнение несколько команд. Следует отметить, что в последних разработках компании Intel (имеется в виду Pentium P54C и процессор следующего поколения P6), а также ее последователей-конкурентов (AMD R5, Cyrix M1, NexGen Nx586 и др.) широко используются идеи, реализованные в RISC-микропроцессорах, так что многие различия между CISC и RISC стираются. Однако сложность архитектуры и системы команд x86 остается и является главным фактором, ограничивающим производительность процессоров на ее основе. 2.4 Матричный процессор. Матричный процессор объединяет множество фунцкиональных устройств, логически объединенных в матрицу и работающих в SIMD-стиле. Векторный же процессор имеет встроенные инструкции для обработки векторов данных, что позволяет эффективно загрузить конвейер из функциональных устройств.   Наиболее распространенными из систем, класса: один поток команд - множество - потоков данных, являются матричные системы, которые лучше всего приспособлены для решения задач, характеризующихся параллелизмом независимых объектов или данных. Организация систем подобного типа на первый взгляд достаточно проста. Они имеют общее управляющее устройство, генерирующее поток команд и большое число процессорных элементов, работающих параллельно и обрабатывающих каждая свой поток данных. Таким образом, производительность системы оказывается равной сумме производительностей всех процессорных элементов. Однако на практике, чтобы обеспечить достаточную эффективность системы при решении широкого круга задач необходимо организовать связи между процессорными элементами с тем, чтобы наиболее полно загрузить их работой. Именно характер связей между процессорными элементами и определяет разные свойства системы. Одним из первых матричных процессоров был SОLОМОN. Система SОLOМОN содержит 1024 процессорных элемента, соединены в виде матрицы: 32х32. Каждый процессорный элемент матрицы включает в себя процессор, обеспечивающий выполнение последовательных поразрядных арифметических и логических операций, а также оперативное ЗУ, емкостью 16 Кбайт. Длина слова - переменная от 1 до 128 разрядов. Разрядность слов устанавливается программно. По каналам связи от устройства управления передаются команды и общие константы. В процессорном элементе используется, так называемая, много модальная логика, которая позволяет каждому процессорному элементу выполнять или не выполнять общую операцию в зависимости от значений обрабатываемых данных. В каждый момент все активные процессорные элементы выполняют одну и ту же операцию над данными, хранящимися в собственной памяти и имеющими один и тот же адрес. Идея многомодальности заключается в том, что в каждом процессорном элементе имеется специальный регистр на 4 состояния - регистр моды. Мода (модальность) заносится в этот регистр от устройства управления. При выполнении последовательности команд модальность передается в коде операции и сравнивается с содержимом регистра моды. Если есть совпадения, то операция выполняется. В других случаях процессорный элемент не выполняет операцию, но может, в зависимости от кода, пересылать свои операнды соседнему процессорному элементу. Такой механизм позволяет выделить строку или столбец процессорных элементов, что очень полезно при операциях над матрицами. Взаимодействуют процессорные элементы с периферийным оборудованием через внешний процессор. Дальнейшим развитием матричных процессоров стала система ILLIАC-4, разработанная фирмой BARRОYS. Первоначально система должна была включать в себя 256 процессорных элементов, разбитых на группы, каждый из которых должен управляться специальным процессором. Однако по различным причинам была создана система, содержащая одну группу процессорных элементов и управляющий процессор. Если в начале предполагалось достичь быстродействия = 1 млрд. операций в секунду, то реальная система работала с быстродействием = 200 млн. операций в секунду. Эта система в течение ряда лет считалась одной из самых высокопроизводительных в мире. В начале 80-х годов в СССР была создана система: ПС-2000, которая также является матричной. Основой этой системы является мультипроцессор - ПС-2000, состоящий из решающего поля и устройства управления мультипроцессором. Решающее поле строится из одного, двух, четырех или восьми устройств обработки, в каждом из которых - 8 процессорных элементов. Мультипроцессор из 64 процессорных элементов обеспечивает быстродействие = 200 млн. операций в секунду на коротких операциях. 2.5 Процессор с конвейеризацией команд. Процессор с конвейеризацией операций Разработчики архитектуры компьютеров издавна прибегали к методам проектирования, известным под общим названием "совмещение операций", при котором аппаратура компьютера в любой момент времени выполняет одновременно более одной базовой операции. Этот общий метод включает два понятия: параллелизм и конвейеризацию. Хотя у них много общего и их зачастую трудно различать на практике, эти термины отражают два совершенно различных подхода. При параллелизме совмещение операций достигается путем воспроизведения в нескольких копиях аппаратной структуры. Высокая производительность достигается за счет одновременной работы всех элементов структур, осуществляющих решение различных частей задачи. Конвейеризация (или конвейерная обработка) в общем случае основана на разделении подлежащей исполнению функции на более мелкие части, называемые ступенями, и выделении для каждой из них отдельного блока аппаратуры. Так обработку любой машинной команды можно разделить на несколько этапов (несколько ступеней), организовав передачу данных от одного этапа к следующему. При этом конвейерную обработку можно использовать для совмещения этапов выполнения разных команд. Производительность при этом возрастает благодаря тому, что одновременно на различных ступенях конвейера выполняются несколько команд. Конвейерная обработка такого рода широко применяется во всех современных быстродействующих процессорах. Для иллюстрации основных принципов построения процессоров мы будем использовать простейшую архитектуру, содержащую 32 целочисленных регистра общего назначения (R0,...,R31), 32 регистра плавающей точки (F0,...,F31) и счетчик команд PC. Будем считать, что набор команд нашего процессора включает типичные арифметические и логические операции, операции с плавающей точкой, операции пересылки данных, операции управления потоком команд и системные операции. В арифметических командах используется трехадресный формат, типичный для RISC-процессоров, а для обращения к памяти используются операции загрузки и записи содержимого регистров в память. Выполнение типичной команды можно разделить на следующие этапы: • выборка команды - IF (по адресу, заданному счетчиком команд, из памяти извлекается команда); • декодирование команды / выборка операндов из регистров - ID; • выполнение операции / вычисление эффективного адреса памяти - EX; • обращение к памяти - MEM; • запоминание результата - WB. На рис. 5.1 представлена схема простейшего процессора, выполняющего указанные выше этапы выполнения команд без совмещения. Чтобы конвейеризовать эту схему, мы можем просто разбить выполнение команд на указанные выше этапы, отведя для выполнения каждого этапа один такт синхронизации, и начинать в каждом такте выполнение новой команды. Естественно, для хранения промежуточных результатов каждого этапа необходимо использовать регистровые станции. На рис. 5.2 показана схема процессора с промежуточными регистровыми станциями, которые обеспечивают передачу данных и управляющих сигналов с одной ступени конвейера на следующую. Хотя общее время выполнения одной команды в таком конвейере будет составлять пять тактов, в каждом такте аппаратура будет выполнять в совмещенном режиме пять различных команд. Работу конвейера можно условно представить в виде сдвинутых во времени схем процессора (рис. 5.3). Этот рисунок хорошо отражает совмещение во времени выполнения различных этапов команд. Однако чаще для представления работы конвейера используются временные диаграммы (рис. 5.4), на которых обычно изображаются выполняемые команды, номера тактов и этапы выполнения команд. Конвейеризация увеличивает пропускную способность процессора (количество команд, завершающихся в единицу времени), но она не сокращает время выполнения отдельной команды. В действительности, она даже несколько увеличивает время выполнения каждой команды из-за накладных расходов, связанных с управлением регистровыми станциями. Однако увеличение пропускной способности означает, что программа будет выполняться быстрее по сравнению с простой неконвейерной схемой. Тот факт, что время выполнения каждой команды в конвейере не уменьшается, накладывает некоторые ограничения на практическую длину конвейера. Кроме ограничений, связанных с задержкой конвейера, имеются также ограничения, возникающие в результате несбалансированности задержки на каждой его ступени и из-за накладных расходов на конвейеризацию. Частота синхронизации не может быть выше, а, следовательно, такт синхронизации не может быть меньше, чем время, необходимое для работы наиболее медленной ступени конвейера. Накладные расходы на организацию конвейера возникают из-за задержки сигналов в конвейерных регистрах (защелках) и из-за перекосов сигналов синхронизации. Конвейерные регистры к длительности такта добавляют время установки и задержку распространения сигналов. В предельном случае длительность такта можно уменьшить до суммы накладных расходов и перекоса сигналов синхронизации, однако при этом в такте не останется времени для выполнения полезной работы по преобразованию информации. В качестве примера рассмотрим неконвейерную машину с пятью этапами выполнения операций, которые имеют длительность 50, 50, 60, 50 и 50 нс соответственно (рис. 5.5). Пусть накладные расходы на организацию конвейерной обработки составляют 5 нс. Тогда среднее время выполнения команды в неконвейерной машине будет равно 260 нс. Если же используется конвейерная организация, длительность такта будет равна длительности самого медленного этапа обработки плюс накладные расходы, т.е. 65 нс. Это время соответствует среднему времени выполнения команды в конвейере. Конвейеризация эффективна только тогда, когда загрузка конвейера близка к полной, а скорость подачи новых команд и операндов соответствует максимальной производительности конвейера. Если произойдет задержка, то параллельно будет выполняться меньше операций и суммарная производительность снизится. Такие задержки могут возникать в результате возникновения конфликтных ситуаций. В следующих разделах будут рассмотрены различные типы конфликтов, возникающие при выполнении команд в конвейере, и способы их разрешения. Рис. Представление о работе конвейера Номер команды Номер такта 1 2 3 4 5 6 7 8 9 Команда i IF ID EX MEM WB Команда i+1 IF ID EX MEM WB Команда i+2 IF ID EX MEM WB Команда i+3 IF ID EX MEM WB Команда i+4 IF ID EX MEM WB Рис. Диаграмма работы простейшего конвейера Рис. Эффект конвейеризации при выполнении 3-х команд - четырехкратное ускорение 2.6 Суперскалярный процессор. Методы минимизации приостановок работы конвейера из-за наличия в программах логических зависимостей по данным и по управлению, нацелены на достижение идеального CPI (среднего количества тактов на выполнение команды в конвейере), равного 1. Чтобы еще больше повысить производительность процессора необходимо сделать CPI меньшим, чем 1. Однако этого нельзя добиться, если в одном такте выдается на выполнение только одна команда. Следовательно необходима параллельная выдача нескольких команд в каждом такте. Существуют два типа подобного рода машин: суперскалярные машины и VLIW-машины (машины с очень длинным командным словом). Суперскалярные машины могут выдавать на выполнение в каждом такте переменное число команд, и работа их конвейеров может планироваться как статически с помощью компилятора, так и с помощью аппаратных средств динамической оптимизации. В отличие от суперскалярных машин, VLIW-машины выдают на выполнение фиксированное количество команд, которые сформатированы либо как одна большая команда, либо как пакет команд фиксированного формата. Планирование работы VLIW-машины всегда осуществляется компилятором. Суперскалярные машины используют параллелизм на уровне команд путем посылки нескольких команд из обычного потока команд в несколько функциональных устройств. Дополнительно, чтобы снять ограничения последовательного выполнения команд, эти машины используют механизмы внеочередной выдачи и внеочередного завершения команд, прогнозирование переходов, кэши целевых адресов переходов и условное (по предположению) выполнение команд. Возросшая сложность, реализуемая этими механизмами, создает также проблемы реализации точного прерывания. В типичной суперскалярной машине аппаратура может осуществлять выдачу от одной до восьми команд в одном такте. Обычно эти команды должны быть независимыми и удовлетворять некоторым ограничениям, например таким, что в каждом такте не может выдаваться более одной команды обращения к памяти. Если какая-либо команда в потоке команд является логически зависимой или не удовлетворяет критериям выдачи, на выполнение будут выданы только команды, предшествующие данной. Поэтому скорость выдачи команд в суперскалярных машинах является переменной. Это отличает их от VLIW-машин, в которых полную ответственность за формирование пакета команд, которые могут выдаваться одновременно, несет компилятор, а аппаратура в динамике не принимает никаких решений относительно выдачи нескольких команд. Предположим, что машина может выдавать на выполнение две команды в одном такте. Одной из таких команд может быть команда загрузки регистров из памяти, записи регистров в память, команда переходов, операции целочисленного АЛУ, а другой может быть любая операция плавающей точки. Параллельная выдача целочисленной операции и операции с плавающей точкой намного проще, чем выдача двух произвольных команд. В реальных системах (например, в микропроцессорах PA7100, hyperSPARC, Pentium и др.) применяется именно такой подход. В более мощных микропроцессорах (например, MIPS R10000, UltraSPARC, PowerPC 620 и др.) реализована выдача до четырех команд в одном такте. Выдача двух команд в каждом такте требует одновременной выборки и декодирования по крайней мере 64 бит. Чтобы упростить декодирование можно потребовать, чтобы команды располагались в памяти парами и были выровнены по 64-битовым границам. В противном случае необходимо анализировать команды в процессе выборки и, возможно, менять их местами в момент пересылки в целочисленное устройство и в устройство ПТ. При этом возникают дополнительные требования к схемам обнаружения конфликтов. В любом случае вторая команда может выдаваться, только если может быть выдана на выполнение первая команда. Аппаратура принимает такие решения в динамике, обеспечивая выдачу только первой команды, если условия для одновременной выдачи двух команд не соблюдаются. Два главных преимущества суперскалярной машины по сравнению с VLIW-машиной: во-первых, малое воздействие на плотность кода, поскольку машина сама определяет, может ли быть выдана следующая команда, и нам не надо следить за тем, чтобы команды соответствовали возможностям выдачи. Во-вторых, на таких машинах могут работать неоптимизированные программы, или программы, откомпилированные в расчете на более старую реализацию. Архитектура же машин с очень длинным командным словом (VLIW Very - Long Instruction Word) позволяет сократить объем оборудования, требуемого для реализации параллельной выдачи нескольких команд, и потенциально чем большее количество команд выдается параллельно, тем больше эта экономия. Упрощенная блок схема процессора Pentium • двухпотоковая суперскалярная организация, допускающая параллельное выполнение пары простых команд; • наличие двух независимых двухканальных множественно-ассоциативных кэшей для команд и для данных, обеспечивающих выборку данных для двух операций в каждом такте; • динамическое прогнозирование переходов; • конвейерная организация устройства плавающей точки с 8 ступенями; • двоичная совместимость с существующими процессорами семейства 80x86. Прежде всего новая микроархитектура этого процессора базируется на идее суперскалярной обработки (правда с некоторыми ограничениями). Основные команды распределяются по двум независимым исполнительным устройствам (конвейерам U и V). Конвейер U может выполнять любые команды семейства x86, включая целочисленные команды и команды с плавающей точкой. Конвейер V предназначен для выполнения простых целочисленных команд и некоторых команд с плавающей точкой. Команды могут направляться в каждое из этих устройств одновременно, причем при выдаче устройством управления в одном такте пары команд более сложная команда поступает в конвейер U, а менее сложная - в конвейер V. Такая попарная выдача команд возможна правда только для ограниченного подмножества целочисленных команд. Команды арифметики с плавающей точкой не могут запускаться в паре с целочисленными командами. Одновременная выдача двух команд возможна только при отсутствии зависимостей по регистрам. При остановке команды по любой причине в одном конвейере, как правило останавливается и второй конвейер. Остальные устройства процессора предназначены для снабжения конвейеров необходимыми командами и данными. Используется раздельная кэш-память команд и данных емкостью по 8 Кбайт, что обеспечивает независимость обращений. В процессоре предусмотрен механизм динамического прогнозирования направления переходов. Это позволяет избежать простоев конвейеров при правильном прогнозе направления перехода. Окончательное решение о направлении перехода естественно принимается на основании анализа кода условия. При неправильно сделанном прогнозе содержимое конвейеров аннулируется и выдача команд начинается с необходимого адреса. Неправильный прогноз приводит к приостановке работы конвейеров на 3-4 такта. 2.7 Другие типы процессоров Коммуникационный процессор целиком выделяется для обработки передаваемой информации, контроля и устранения ошибок, кодирование сообщений, управление линией связи и т.п.) В отдельные классы следует выделить так называемые систолитические и нейросигнальные процессоры. Систолические процессоры (процессорные матрицы) - это чипы, как правило, близкие к обычным RISC-процессорам и объединяющие в своём составе некоторое число процессорных элементов. Вся же остальная логика, как правило, должна быть реализована на базе периферийных схем. У нейросигнальных процессоров ядро представляет собой типовой сигнальный процессор, а реализованная на кристале дополнительная логика обеспечивает выполнение нейросетевых операций (например, дополнительный векторный процессор и т.п.). Лекция 3. Системы команд В команде четко выделяют 2 части – операционная (ОЧ) и адресная (АЧ). В общем случае АЧ может содержать 4 адреса: Операнд1, операнд2, результат, адрес следующей команды Возможно уменьшение вплоть до безадресной команды. Удобно иметь возможность использовать команды с различным количеством адресов, быстрее выполняются команды с меньшим количеством адресов. Формат команды – это описание размеров и взаимного расположения структурных частей команды. Всегда стараются сделать так, чтобы команда занимала целое число элементов хранения. Формат задается первым словом. Количество команд может быть весьма различно, максимум был в VAX 11/780 303 команды. Основные группы команд: 1) Арифметические и логические 2) Пересылки 3) Передача выполнения (выполн не в АЛУ) 4) Операции с внешними устройствами 5) Символьные и строчные 6) Специальные команды Принципы управления потоком команд: 1) Последовательное (ФН) 2) По мере готовности данных (разумнее) (Data Flow model) (параллельно, синхронизация join, fork) 3) По мере потребности в результатах выполнения (совсем разумно) (Reduction model) (редукционно-программное управление) В методах адресации можно выделить несколько групп: 1) Прямая 2) Косвенная 3) Относительная Использование сложных методов адресации позволяет существенно сократить количество команд в программе, но при этом значительно увеличивается сложность аппаратуры. Возникает вопрос, а как часто эти методы адресации используются в реальных программах? Метод адресации Пример команды Смысл команды метода Использование Регистровая Add R4,R3 R4(R4+R5 Требуемое значение в регистре Непосредственная или литеральная Add R4,#3 R4(R4+3 Для задания констант Базовая со смещением Add R4,100(R1) R4(R4+M[100+R1] Для обращения к локальным переменным Косвенная регистровая Add R4,(R1) R4(R4+M[R1] Для обращения по указателю или вычисленному адресу Индексная Add R3,(R1+R2) R3(R3+M[R1+R2] Иногда полезна при работе с массивами: R1 - база, R3 - индекс Прямая или абсолютная Add R1,(1000) R1(R1+M[1000] Иногда полезна для обращения к статическим данным Косвенная Add R1,@(R3) R1(R1+M[M[R3]] Если R3-адрес указателя p, то выбирается значение по этому указателю Автоинкрементная Add R1,(R2)+ R1(R1+M[R2] R2(R2+d Полезна для прохода в цикле по массиву с шагом: R2 - начало массива В каждом цикле R2 получает приращение d Автодекрементная Add R1,(R2)- R2(R2-dR1(R1+M[R2] Аналогична предыдущей. Обе могут использоваться для реализации стека Базовая индексная со смещением и масштабированием Add R1,100(R2)[R3] R1(R1+M[100]+R2+R3*d Для индексации массивов При этом основной вопрос, который возникает для метода базовой адресации со смещением, связан с длиной (разрядностью) смещения. Выбор длины смещения в конечном счете определяет длину команды. Результаты измерений показали, что в подавляющем большинстве случаев длина смещения не превышает16 разрядов. Этот же вопрос важен и для непосредственной адресации. Непосредственная адресация используется при выполнении арифметических операций, операций сравнения, а также для загрузки констант в регистры. Результаты анализа статистики показывают, что в подавляющем числе случаев 16 разрядов оказывается вполне достаточно (хотя для вычисления адресов намного реже используются и более длинные константы). Можно выделить четыре основных типа команд для управления потоком команд: условные переходы, безусловные переходы, вызовы процедур и возвраты из процедур. Частота использования этих команд по статистике примерно следующая. В программах доминируют команды условного перехода. Среди указанных команд управления на разных программах частота их использования колеблется от 66 до 78%. Следующие по частоте использования - команды безусловного перехода (от 12 до 18%). Частота переходов на выполнение процедур и возврата из них составляет от 10 до 16%. При этом примерно 90% команд безусловного перехода выполняются относительно счетчика команд. Типы и размеры операндов. Имеется два альтернативных метода определения типа операнда. В первом из них тип операнда может задаваться кодом операции в команде. Это наиболее употребительный способ задания типа операнда. Второй метод предполагает указание типа операнда с помощью тега, который хранится вместе с данными и интерпретируется аппаратурой во время выполнения операций над данными. Этот метод использовался, например, в машинах фирмы Burroughs, но в настоящее время он практически не применяется и все современные процессоры пользуются первым методом. Обычно тип операнда (например, целый, вещественный с одинарной точностью или символ) определяет и его размер. Однако часто процессоры работают с целыми числами длиною 8, 16, 32 или 64 бит. Как правило целые числа представляются в дополнительном коде. Для задания символов (1 байт = 8 бит) в машинах компании IBM используется код EBCDIC, но в машинах других производителей почти повсеместно применяется кодировка ASCII. Еще до сравнительно недавнего времени каждый производитель процессоров пользовался своим собственным представлением вещественных чисел (чисел с плавающей точкой). Однако за последние несколько лет ситуация изменилась. Большинство поставщиков процессоров в настоящее время для представления вещественных чисел с одинарной и двойной точностью придерживаются стандарта IEEE 754. В некоторых процессорах используются двоично кодированные десятичные числа, которые представляются в в упакованном и неупакованном форматах. Упакованный формат предполагает, что для кодирования цифр 0-9 используются 4 разряда и что две десятичные цифры упаковываются в каждый байт. В неупакованном формате байт содержит одну десятичную цифру, которая обычно изображается в символьном коде ASCII. В большинстве процессоров, кроме того, реализуются операции над цепочками (строками) бит, байт, слов и двойных слов. Лекция 4. Виды памяти 4.1 Статическая и динамическая. "Динамическая память - DRAM (Dynamic RAM) - получила свое название от принципа действия ее запоминающих ячеек, которые выполнены в виде конденсаторов, образованных элементами полупроводниковых микросхем. С некоторым упрощением описания физических процессов можно сказать, что при записи логической единицы в ячейку конденсатор заряжается, при записи нуля - разряжается. Схема считывания разряжает через себя этот конденсатор, и, если заряд был ненулевым, вычтавляет на своем выходе единичное значение, и подзаряжает конденсатор до прежнего значения. При отсутствии обращения к ячейке со временем за счет токов утечки конденсатор разряжается и информация теряется, поэтому такая память требует постоянного периодического подзаряда конденсаторов (обращения к каждой ячейке) - память может работать только в динамическом режиме. Этим она принципиально отличается от статической памяти, реализуемой на триггерных ячейках и хранящей информацию без обращений к ней сколь угодно долго (при включенном питании). Благодаря относительной простоте ячейки динамической памяти на одном кристалле удается размещать миллионы ячеек и получать самую дешевую полупроводниковую память достаточно высокого быстродействия с умеренным энергопотреблением, используемую в качестве основной памяти компьютера. Расплатой за низкую цену являются некоторые сложности в управлении динамической памятью... Запоминающие ячейки микросхем DRAM организованы в виде двумерной матрицы. Адрес строки и столбца передается по мультиплексированной шине адреса MA (Multiplexed Address) и стробируется по спаду импульсов #RAS (Row Access Strobe) и #CAS (Column Access Strobe). Поскольку обращение (запись или чтение) к различным ячейкам памяти обычно происходит в случайном порядке, то для поддержания сохранности данных применяется регенерация (Memory Refresh - "освежение" памяти) - регулярный циклический перебор ее ячеек (обращение к ним) с холостыми циклами. Регенерация в микросхеме происходит одновременно по всей строке матрицы при обращении к любой из ее ячеек. Возможны три различных метода регенерации данных. 1) Регенерация одним RAS (RAS Only Refresh - ROR). Этот метод использовался еще в первых микросхемах DRAM. Адрес регенерируемой строки передается на шину адреса и выдается сигнал RAS (точно так же, как при чтении или записи). При этом выбирается строка ячеек, и данные из них поступают на внутренние цепи микросхемы, после чего записываются обратно. Так как далее сигнал CAS не следует, цикл чтения/записи не начинается. Затем передается адрес следующей строки и так далее, пока не будет пройдена вся матрица памяти, после чего цикл регенерации повторяется. К недостаткам этого метода можно отнести то, что занимается шина адреса, и в момент регенерации блокируется доступ к другим подсистемам компьютера. 2) CAS перед RAS (CAS Before RAS - CBR) - стандартный метод регенерации. При нормальном цикле чтения/записи сигнал RAS всегда приходит первым, за ним следует CAS. Если же CAS приходит раньше RAS, то начинается специальный цикл регенерации - CBR. При этом адрес строки не передается, а микросхема использует свой внутренний счетчик, содержимое которого увеличивается на 1 при каждом CBR-цикле (т.н. инкрементирование адреса строки). Этот режим позволяет регенерировать память, не занимая шину адреса, что, безусловно, более экономично. 3) Автоматическая регенерация памяти (Self Refresh - SR, или саморегенерация). Этот метод обычно используется в режиме энергосбережения, когда система переходит в состояние "сна" ("suspend"), и тактовый генератор перестает работать. В таком состоянии обновление памяти по вышеописанным методам невозможно (попросту отсутствуют источники сигналов), и микросхема памяти выполняет регенерацию самостоятельно. В ней запускается свой собственный генератор, который тактирует внутренние цепи регенерации. Такая технология работы памяти была внедрена с появлением EDO DRAM. Необходимо отметить, что в режиме "сна" память потребляет очень малый ток. Необходимо добавить, что циклы регенерации выполняет входящий в состав чипсета контроллер регенерации, который для выполнения своей задачи должен получать управление магистралью каждые 15 мкс. Во время цикла регенерации производится чтение одной из 256 ячеек памяти Регенерация основной памяти в РС/ХТ осуществлялась каналом DMA О. Сигнал Refr, вырабатываемый каждые 15.6 мкс по сигналу от первого канала таймера-счетчика 8253/9254 (порт 041h), вызывает холостой цикл обращения к памяти для регенерации очередной строки. В РС/АТ контроллер регенерации усложнен. В современных компьютерах регенерацию основной памяти берет на себя чипсет, и его задача - по возможности использовать для регенерации циклы шины, не занятые ее абонентами (процессорами и активными контроллерами). Самые "ловкие" контроллеры регенерации - smart redresh ставят запросы на регенерацию в очередь, которую обслуживают в свободное для шины время, и только если запросов накапливается больше предельного количества, откладывается текущий цикл обмена по шине и цикл регенерации выполняется немедленно. Статическая "Статическая память - SRAM (Static Random Access Memory), как и следует из ее названия, способна хранить информацию в статическом режиме - то есть сколь угодно долго при отсутствии обращений (но при наличии питающего напряжения). Ячейки статической памяти реализуются на триггерах - элементах с двумя устойчивыми состояниями. По сравнению с динамической памятью эти ячейки более сложные и занимают больше места в кристалле, однако они проще в управлении и не требуют регенерации. 4.2 Энергонезависимая память. "В соответствии с названием, энергонезависимая память хранит данные и при отсутствии питающего напряжения, в отличие от статической и динамической полупроводниковой памяти. Существует множество типов энергонезависимой памяти: ROM, PROM, EPROM, EEPROM, Flash Memory, различающихся по своим потребительским свойствам, обусловленным способом построения запоминающих ячеек, и сферам применения. Запись информации в энергонезависимую память, называемая программированием, обычно существенно сложнее и требует больших затрат времени и энергии, чем считывание. Основным режимом работы такой памяти является считывание данных, а некоторые типы после программирования допускают только считывание, что и обусловливает их общее название ROM (Read Only Memory - память только для чтения) или ПЗУ (постоянное запоминающее устройство). Самые первые постоянные запоминающие устройства выполнялись на магнитных сердечниках, где информация заносилась их прошивкой проводниками считывания. С тех пор применительно к программированию ПЗУ укоренилось понятие "прошивка". По возможности программирования различают: • Микросхемы, программируемые при изготовлении (масочные ПЗУ) - ROM. • Микросхемы, программируемые однократно после изготовления перед установкой в цедевое устройство (прожигаемые ПЗУ, программируемые на специальных программаторах) - PROM (Programmable ROM) или ППЗУ (программируемые ПЗУ). • Микросхемы, стираемые и программируемые многократно - РПЗУ (репрограммируемые ПЗУ), EPROM (Eraseable PROM - стираемые ПЗУ). Запоминающие ячейки энергонезависимой памяти обычно несимметричны по своей природе и позволяют записывать только нули (реже - только единицы) в предварительно стертые (чистые) ячейки. Однократно программируемые микросхемы позволяют изменять только исходное (после изготовления) состояние ячеек. Стирание ячеек выполняется либо для всей микросхемы, либо для определенного блока, либо для одной ячейки (байта). Стирание приводит все биты стираемой области в одно состояние (обычно во все единицы, реже - во все нули). Процедура стирания обычно существенно дольше записи. В зависимости от способа стирания различают: • микросхемы, стираемые ультрафиолетовым облучением, - их обычно называют просто EPROM (Eraseable ROM - стираемые микросхемы) или UV-EPROM (Ultra-Violet EPROM, УФРПЗУ); • электрически стираемые микросхемы EEPROM (Electrical Eraseable PROM, ЭСПЗУ), в том числе и флэш-память. Процедура программирования многих типов памяти требует наличия относительно высокого напряжения программирования (12-26 В)... Стирание или программирование микросхем может выполняться либо в специальном устройстве - программаторе, либо в самом целевом устройстве, если у него предусмотрены соответствующие средства. Для микросхем, не извлекаемых из целевого устройства (PC), возможны два метода их перепрограммирования: • используя собственный процессор PC - ISW (In-System Write); • подключая к плате внешний программатор - OBP (On-Board Programming). Энергонезависимая память в основном применяется для хранения неизменяемой (или редко изменяемой) информации - системного программного обеспечения (BIOS), таблиц (например, знакогенераторов графических адаптеров), памяти конфигурации устройств (ESCD, EEPROM адаптеров). Эта информация обычно является ключевой для функционирования PC ..., поэтому весьма существенна забота о ее сохранности и предотвращении несанкционированного изменения. Нежелательное (ошибочное или под воздействием вируса) изменение содержимого становится возможным при использовании для хранения BIOS флэш-памяти, программируемой в целевом устройстве (на системной плате PC). Флэш-память используется и в качестве внешней памяти (как альтернатива дисковой), позволяющей как считывать, так и оперативно записывать новые данные. Важными параметрами энергонезависимой памяти является время хранения и устойчивость к электромагнитным воздействиям, а для перепрограммируемой памяти еще и гарантированное количество циклов перепрограммирования. Энергонезависимую память, запись в которую производят при регулярной работе, называют NVRAM (Non-Volatile Random Access Memory). Это название подразумевает возможность произвольной смены информации не только во всей ее области, но и в отдельной ячейке или небольшом блоке." 4.3 Кэш "Основная память, реализуемая на относительно медленных ... микросхемах динамической памяти, обычно требует ввода тактов ожидания процессора... Статическая память, построенная как и процессор, на триггерных ячейках, ... способна догонять современные процессоры по быстродействию и избежать (или хотя бы сократить количество) тактов ожидания. Реализация основной памяти на микросхемах SRAM технически и экономически не оправдана, поскольку плотность упаковки у нее существенно ниже, а удельная стоимость хранения и энергопотребления (или, что важнее, тепловыделение) существенно выше, чем у DRAM. Разумным компромиссом для построения экономичных и производительных систем явился иерархический способ построения оперативной памяти... Идея этого способа заключается в сочетании основной памяти большого объема на DRAM с относительно небольшой кэш-памятью на быстродействующих микросхемах SRAM. Идея, конечно, далеко не нова - сверхоперативная память применялась давно, еще в "больших" компьютерах. В переводе слово "кэш" (cache) означает склад или тайник ("заначка"). Тайна этого склада заключается в его "прозрачности" - для программы он не представляет собой дополнительной адресуемой памяти. Он является дополнительным и быстродействующим хранилищем копий блоков информации основной памяти, к которым, вероятно, в ближайшее время будет обращение. Кэш не может хранить копию всей основной памяти, поскольку его объем во много раз меньше... Он хранит лишь ограниченное количество блоков данных и каталог (cache directory) - список их текущего соответствия областям основной памяти. Кроме того, кэшироваться может не вся память, доступная процессору... При каждом обращении к кэшируемой памяти контроллер кэш-памяти по каталогу проверяет, есть ли действительная копия затребованных данных в кэше. Если она там есть, то это случай кэш попадания (cache hit), и обращение за данными происходит только к кэш-памяти. Если действительной копии там нет, то это случай кэш-промах (cache miss), и данные берутся из основной памяти. В соответствии с алгоритмом кэширования блок данных, считанных из основной памяти ..., заместит один из блоков кэша. ... Обращение к основной памяти может начинаться одновременно с поиском в каталоге, а в случае попадания - прерываться (архитектура Look Aside). Это экономит время, но лишние обращения к основной памяти ведут к излишнему энергопотреблению. Другой вариант - обращение к внешней памяти начинается только после фиксации случая промаха (архитектура Look Through), но на этом теряется, по крайней мере, один такт процессора, зато экономится энергия. "Контроллер кэша оперирует со строками (cache line) фиксированной длины. ... С каждой строкой кэша связана информация об адресе скопированного в нее блока основной памяти и признаки ее состояния. Строка может быть действительной (valid) - это означает, что в текущий момент времени она достоверно отражает соответствующий блок основной памяти, или недействительной (пустой). Информация о том, какой именно блок занимает данную строку (то есть старшая часть адреса или номер страницы), и ее состояние называется тегом (tag) и хранится в связанной с данной строкой ячейке специальной памяти тегов (tag RAM)." "Строки кэша ... обычно выделяются только при операциях чтения. Запись блока, не имеющего копии в кэше, производится только в основную память... Поведение кэш-контроллера при операции записи в память, когда копия затребованной области находится в некоторой строке кэша, определяется его политикой записи (Write Policy). Существуют два основных алгоритма записи данных из кэша в основную память: сквозная запись WT (Write Through) и обратная запись WB (Write Back). Алгоритм WT предусматривает выполнение каждой операции записи (даже однобайтной), попадающей в кэшированный блок, одновременно и в строку кэша, и в основную память. ... Алгоритм достаточно прост в реализации и легко обеспечивает целостность данных за счет постоянного совпадения копий данных в кэше и в основной памяти. ... Но эта простота оплачивается низкой эффективностью записи. ... Алгоритм WB позволяет уменьшить количество операций записи на шине основной памяти. Если блок памяти, в который должна производиться запись, отображен и в кэше, то физическая запись сначала будет произведена в эту действительную строку кэша, и она будет отмечена как грязная (dirty), или модифицированная, то есть требующая выгрузки в основную память. Только после этой выгрузки (записи в основную память) строка станет чистой (clean)... Данный алгоритм сложнее в реализации, но существенно эффективнее, чем WT." "В зависимости от способа определения взаимного соответствия строки кэша и области основной памяти различают три архитектуры кэш-памяти: кэш прямого отображения (direct-mapped cache), полностью ассоциативный кэш (fully associative cache) и их комбинация - частично- или наборно-ассоциативный кэш (set-associative cache)." [Далее в книге подробно рассмотрены все эти архитектуры. Показано, что первая имеет самую простую аппаратную реализацию, но эффективность ее невысока. Напротив, самая гибкая последняя архитектура пока реализована только для небольших объемов первичного кэша в некоторых процессорах.] 4.4 Flash "Стирание микросхем постоянной памяти возможно и электрическим способом. Однако этот процесс требует значительного расхода энергии, который выражается в необходимости приложения относительно высокого напряжения стирания (10-30 В) и длительности импульса стирания более десятка микросекунд. Интерфейс традиционных микросхем EEPROM имел временную диаграмму режима записи с большой длительностью импульса, что не позволяло непосредственно использовать сигнал записи системной шины. Кроме того, перед записью информации в ячейку обычно требовалось предварительное стирание, тоже достаточно длительное. Микросхемы EEPROM относительно небольшого объема широко применяются в качестве энергонезависимой памяти конфигурирования различных адаптеров. Современные микросхемы EEPROM имеют более сложную внутреннюю структуру, в которую входит управляющий автомат. Это позволяет упростить внешний интерфейс, делая возможным непосредственное подключение к микропроцессорной шине, и скрыть специфические (и неужные пользователю) вспомогательные операции типа стирания и верификации. Флэш-память по определению относится к классу EEPROM, но использует особую технологию построения запоминающих ячеек. Стирание во флэш-памяти производится сразу для целой области ячеек (блоками или полностью всей микросхемы). Это позволило существенно повысить производительность в режиме записи (программирования). Флэш-память обладает сочетанием высокой плотности упаковки (ее ячейки на 30% меньше ячеек DRAM), энергонезависимого хранения, электрического стирания и записи, низкого потребления, высокой надежности и невысокой стоимости... Современная флэш-память имеет время доступа при чтении 35-200 нс, существуют версии с интерфейсом динамической памяти и ... напоминающим интерфейс синхронной статической памяти. Стирание информации (поблочное или во всей микросхеме) занимает 1-2 сек. Программирование (запись) байта занимает время порядка 10 мкс, причем шинные циклы при записи - нормальные для процессора (не растянутые, как для EPROM и EEPROM)." "Интерфейс микросхем флэш-памяти хорошо сочетается со стандартными сигналами, используемыми в микропроцессорных системах. Внутренние циклы стирания, записи и верификации выполняются автономно от шинных циклов внешнего интерфейса, что является существенным преимуществом перед микросхемами EPROM и EEPROM. В режиме чтения они полностью совместимы с EPROM, совпадая с ними и по расположению основных выводов." "Первые микросхемы флэш-памяти были предложены фирмой Intel в 1988 году и с тех пор претерпели существенные изменения по архитектуре, интерфейсу и напряжению питания. По организации массива различаются микросхемы: • Bulk Erase - стирание возможно только для всего объема. • Boot Lock - массив разделен на несколько блоков разного размера, стираемых независимо. Один из блоков имеет дополнительные аппаратные средства зашиты от стирания и записи. • Flash File - массив разделен на несколько равноправных независимо стираемых блоков обычно одинакового размера, что позволяет их называть микросхемами с симметричной архитектурой SA (Symmetrical Architecture)." "Микросхемы Boot Lock ... специально предназначены для хранения ... BIOS, а привелигерованный блок (Boot Block) хранит минимальный загрузчик, позволяющий загрузить (например с дискеты) и выполнить утилиту программирования основного блока флэш-памяти. В обозначении этих микросхем присутствует суффикс T (Top) или B (Bottom), определяющий положение Boot-блока либо в старших, либо в младших адресах соответственно. Первые предназначены для процессоров, стартующих со старших адресов (в том числе x86, Pentium), вторые - для стартующих с нулевого адреса, хотя возможны и противоположные варианты использования, если некоторые биты шины адреса перед микросхемой памяти инвертируются." "Микросхемы флэш-памяти выпускаются многими фирмами. Они различаются по организации, интерфейсу, напряжению питания и программирования, методам защиты и другим параметрам. Лидеры в области разработки и производства флэш-памяти - фирмы AMD, Fujitsu Corporation, Intel Corporation и Sharp Corporation летом 1996 года приняли спецификацию CFI (Common Flash Interface), обеспечивающую совместимость разрабатываемого программного обеспечения с существующими и разрабатываемыми моделями флэш-памяти." Некоторые производители микросхем памяти решили добиться повышения ее производительности путем добавления локального кэша. В самом деле, при каждом обращении к массиву усилители считывают данные из всех ячеек строки, однако в одном цикле обращения к памяти запрашиваются лишь несколько столбцов. Усилители могут сохранять свое состояние в лучшем случае до следующего цикла регенерации памяти, таким образом, большая часть уже полученной информации «пропадает впустую». При следующем обращении к той же строке за другими столбцами ее необходимо опять считывать целиком и перезаряжать ячейки массива. Если же поместить считанную из массива строку в локальный кэш, то достаточно выбирать из него необходимые столбцы. 4.5 Контроль четности "В любой из миллионов ячеек памяти возможен случайный сбой или окончательный отказ ... Вероятность ошибки, естественно, возрастает с увеличением объема памяти. Современные технологии позволяют выпускать высоконадежные системы памяти, у которых ... вероятность ошибки достаточно мала, но все-таки не нулевая." "Случайный сбой может произойти и в исправной микросхеме памяти, например, при пролете через нее ионизирующей частицы (по этой причине в условиях высокого уровня радиации обычные электронные элементы неработоспособны)." "В первых моделях PC, когда микросхемы памяти имели существенно худшие характеристики надежности по сравнению с современными, обязательно применялся контроль четности. При его использовании каждый байт памяти сопровождался битом паритета (Parity bit), дополняющим количество единиц в байте до нечетного. Значение бита паритета аппаратно генерируется при записи в память и проверяется при считывании. При обнаружении ошибки паритета схемой контроля вырабатывается немаскируемое прерывание (NMI) и его обработчик обычно выводит на экран сообщение ... и останавливает процессор командой Halt.... Со временем качество применяемых микросхем памяти улучшилось и в целях удешевления модулей памяти от применения контроля четности стали отказываться... Модули памяти ... стали выпускаться как с паритетом, так и без него, а для "ублажения" (точнее - обмана) плат, требующих наличия бита паритета, стали выпускать модули с "подделкой" паритета (Fake Parity или PG - Parity Generator). В этих модулях вместо дополнительной микросхемы памяти используется генератор паритета - логическая схема сумматора по модулю 2, формирующая всегда "хороший" бит паритета, независимо от наличия ошибки в самой памяти. ... Заметим, что никакие программные средства тестирования памяти (CheckIt, PCCheck и т.п.) не позволяют отличить память с настоящим паритетом от памяти с фиктивным паритетом. Вопрос о необходимости контроля паритета не имеет однозначного ответа. По мнению автора, лучше иметь остановку машины по ошибке с явным указанием на ее источник, чем искать причины непонятных зависаний и "вылетов"... Но контроль паритета не всесилен, он выявляет в пределах каждого байта ошибки только нечетной кратности (искажение 1, 3, 5 или 7 бит), правда, вероятность одновременного отказа или сбоя двух бит у работающей памяти весьма мала." "В компьютерах особо ответственного применения используют память с обнаружением и исправлением ошибок - ECC Memory (Error Checking and Correcting). В этом случае для каждого записываемого информационного слова памяти (а не байта, как при контроле паритета) по определенным правилам вычисляется функция свертки, результат которой разрядностью в несколько бит также хранится в памяти. Для 64-битного слова обычно используют 7-8 дополнительных бит. При считывании схема контроля с использованием этих избыточных бит способна обнаруживать ошибки с различной кратностью и (или) исправлять однократные ошибки." 4.6 Организация памяти Линейная логическая организация. В зависимости от типа ЗУ элементом памяти (ЭП) может быть: триггер, миниатюрный конденсатор, транзистор с "плавающим затвором", плавкая перемычка (или ее отсутствие). Упорядоченный набор ЭП образует ячейку памяти (ЯП). Количество элементов памяти в ячейке (длина слова) обычно кратно 2n (1,4,8,16, 32,64..), причем величины свыше 8-ми достигаются, обычно, группировкой микросхем с меньшим количеством ЭП. Количество ЭП в ЯП иногда называется длиной слова. Основными характеристиками микрсхем памяти являются: информационная емкость, быстродействие и энергопотребление. Емкость ЗУ чаще всего выражается в единицах кратных числу 210 = 1024 = 1K. Для длины слова равной биту (одному двоичному разряду) или байту (набору из восьми бит) эта единица называется килобит или килобайт и обозначается Kb или KB. Характеристики оперативной памяти и особенности ее устройства являются важнейшим фактором, от которого зависит быстродействие компьютера. Ведь даже при наличии быстрого процессора скорость выборки данных из памяти может оказаться невысокой и именно эта невысокая скорость работы с оперативной памятью будет определять быстродействие компьютера. Время цикла работы с памятью tm обычно заметно больше, чем время цикла центрального процессора tc. Если процессор инициализирует обращение к памяти, она будет занята в течение времени tc+tm и в течение этого промежутка времени ни одно другое устройство, в том числе и сам процессор не смогут работать с оперативной памятью. Таким образом, возникает проблема доступа к памяти. Эта проблема решается специальной организацией оперативной памяти. Принята следующая классификация параллельных компьютеров по архитектуре подсистем оперативной памяти: системы с разделяемой памятью, у которых имеется одна большая виртуальная память и все процессоры имеют одинаковый доступ к данным и командам, хранящимся в этой памяти; системы с распределенной памятью, у которых каждый процессор имеет свою локальную оперативную память и к этой памяти у других процессоров нет доступа. Различие этих двух типов памяти проявляется в структуре виртуальной памяти, то есть памяти, как она "видна" процессору. Физически память обычно делится на части, доступ к которым может быть организован независимо. Различие между разделяемой и распределенной памятью заключается в способе интерпретации адреса. Это различие проще пояснить на следующем примере. Пусть один из процессоров выполняет команду вида load r1, i - "загрузить в регистр r1 данные из ячейки памяти i". Если команда выполняется на компьютере с разделяемой памятью, номер ячейки i имеет одинаковый смысл для всех процессоров, i является глобальным адресом. В случае системы с распределенной памятью, ячейка i разная для разных процессоров и в регистры r1 по этой команде будут загружены разные значения. Для программиста часто бывает важно знать тип оперативной памяти компьютера, на котором он работает. Ведь архитектура памяти определяет способ взаимодействия между различными частями параллельной программы. При выполнении на компьютере с распределенной памятью программа перемножения матриц, например, должна создать копии перемножаемых матриц на каждом процессоре, что технически осуществляется передачей на эти процессоры сообщения, содержащего необходимые данные. В случае системы с разделяемой памятью достаточно лишь один раз задать соответствующую структуру данных и разместить ее в оперативной памяти. Остановимся подробнее на раличных архитектурах подсистемы памяти Чередуемая память Чередуемая память разделяется на банки памяти. Принято соглашение о том, что ячейка памяти с номером i находится в банке памяти с номером i mod n, где n - количество банков памяти. Таким образом, если имеется 8 банков памяти, то первому банку памяти будут принадлежать ячейки памяти с номерами 0, 8, 16, :, второму - 1, 9, 17, ... и т.д. Запросы к различным банкам памяти могут обрабатываться одновременно. При достаточном количестве банков памяти скорость обмена данными между памятью и процессором может быть близка к идеальному значению - одно машинное слово за один такт работы процессора. Ячейки памяти могут быть перенумерованы и непрерывным образом, то есть, скажем, в первом банке находятся ячейки с номерами от 0 до 255, во втором от 256 до 511 и т.д. В векторных компьютерах обычно используется первый способ адресации, а в многопроцессорных комплексах с разделяемой памятью – второй Разделяемая память Простейший способ создать многопроцессорный вычислительный комплекс с разделяемой памятью - взять несколько процессоров, соединить их с общей шиной и соединить эту шину с оперативной памятью. Этот простой способ является не очень удачным, поскольку между процессорами возникает борьба за доступ к шине и если один процессор принимает команду или передает данные, все остальные процессоры вынуждены будут перейти в режим ожидания. Это приводит к тому, что начиная с некоторого числа процессоров, быстродействие такой системы перестанет увеличиваться при добавлении нового процессора. Несколько улучшить картину может применение кэш-памяти для хранения команд. При наличии локальной, то есть принадлежащей данному процессору кэш-памяти, следующая необходимая ему команда с большой вероятностью будет находиться в кэш-памяти. В результате этого уменьшается количество обращений к шине и быстродействие системы возрастает. Вместе с тем возникает новая проблема - проблема кэш-когерентности. Эта проблема заключается в том, что если, скажем, двум процессорам для выполнения различных операций понадобилось значение V, это значение будет храниться в виде двух копий в кэшпамяти обоих процессоров. Один из процессоров может изменить это значение в результате выполнения своей команды и оно будет передано в оперативную память компьютера. Но в кэшпамяти второго процессора все еще хранится старое значение! Следовательно, необходимо обеспечить своевременное обновление данных в кэш-памяти всех процессоров компьютера. Имеются и другие реализации разделяемой памяти. Это, например, разделяемая память с дискретными модулями памяти. Физическая память состоит из нескольких модулей, хотя виртуальное адресное пространство остается общим. Вместо общей шины в этом случае используется переключатель, направляющий запросы от процессора к памяти. Такой переключатель может одновременно обрабатывать несколько запросов к памяти, поэтому если все процессоры обращаются к разным модулям памяти, быстродействие возрастает. Одним из примеров успешно работающих вычислительных систем с разделяемой памятью является компьютер KSR-1 фирмы Kendall Square Research. Лекция 5. Ускорение доступа к памяти и параллелилзм вычислений 5.1 Специальная организация памяти Характеристики оперативной памяти и особенности ее устройства являются важнейшим фактором, от которого зависит быстродействие компьютера. Ведь даже при наличии быстрого процессора скорость выборки данных из памяти может оказаться невысокой и именно эта невысокая скорость работы с оперативной памятью будет определять быстродействие компьютера. Время цикла работы с памятью tm обычно заметно больше, чем время цикла центрального процессора tc. Если процессор инициализирует обращение к памяти, она будет занята в течение времени tc+tm и в течение этого промежутка времени ни одно другое устройство, в том числе и сам процессор не смогут работать с оперативной памятью. Таким образом, возникает проблема доступа к памяти. Эта проблема решается специальной организацией оперативной памяти. Принята следующая классификация параллельных компьютеров по архитектуре подсистем оперативной памяти: системы с разделяемой памятью, у которых имеется одна большая виртуальная память и все процессоры имеют одинаковый доступ к данным и командам, хранящимся в этой памяти; системы с распределенной памятью, у которых каждый процессор имеет свою локальную оперативную память и к этой памяти у других процессоров нет доступа. Различие этих двух типов памяти проявляется в структуре виртуальной памяти, то есть памяти, как она "видна" процессору. Физически память обычно делится на части, доступ к которым может быть организован независимо. Различие между разделяемой и распределенной памятью заключается в способе интерпретации адреса. Это различие проще пояснить на следующем примере. Пусть один из процессоров выполняет команду вида load r1, i - "загрузить в регистр r1 данные из ячейки памяти i". Если команда выполняется на компьютере с разделяемой памятью, номер ячейки i имеет одинаковый смысл для всех процессоров, i является глобальным адресом. В случае системы с распределенной памятью, ячейка i разная для разных процессоров и в регистры r1 по этой команде будут загружены разные значения. Для программиста часто бывает важно знать тип оперативной памяти компьютера, на котором он работает. Ведь архитектура памяти определяет способ взаимодействия между различными частями параллельной программы. При выполнении на компьютере с распределенной памятью программа перемножения матриц, например, должна создать копии перемножаемых матриц на каждом процессоре, что технически осуществляется передачей на эти процессоры сообщения, содержащего необходимые данные. В случае системы с разделяемой памятью достаточно лишь один раз задать соответствующую структуру данных и разместить ее в оперативной памяти. Остановимся подробнее на различных архитектурах подсистемы памяти Память с расслоением 1) Функциональное разделение – блоки с операндами, блоки с результатом. 2) Расслоение адресов. Наличие в системе множества микросхем памяти позволяет использовать потенциальный параллелизм, заложенный в такой организации. Для этого микросхемы памяти часто объединяются в банки или модули, содержащие фиксированное число слов, причем только к одному из этих слов банка возможно обращение в каждый момент времени Чередуемая память разделяется на банки памяти. Принято соглашение о том, что ячейка памяти с номером i находится в банке памяти с номером i mod n, где n - количество банков памяти. Таким образом, если имеется 8 банков памяти, то первому банку памяти будут принадлежать ячейки памяти с номерами 0, 8, 16, :, второму - 1, 9, 17, ... и т.д. Запросы к различным банкам памяти могут обрабатываться одновременно. При достаточном количестве банков памяти скорость обмена данными между памятью и процессором может быть близка к идеальному значению - одно машинное слово за один такт работы процессора. Ячейки памяти могут быть перенумерованы и непрерывным образом, то есть, скажем, в первом банке находятся ячейки с номерами от 0 до 255, во втором от 256 до 511 и т.д. В векторных компьютерах обычно используется первый способ адресации, а в многопроцессорных комплексах с разделяемой памятью – второй Варианты объединения процессоров в случае общей памяти. Простейший способ создать многопроцессорный вычислительный комплекс с разделяемой памятью - взять несколько процессоров, соединить их с общей шиной и соединить эту шину с оперативной памятью. Этот простой способ является не очень удачным, поскольку между процессорами возникает борьба за доступ к шине и если один процессор принимает команду или передает данные, все остальные процессоры вынуждены будут перейти в режим ожидания. Это приводит к тому, что начиная с некоторого числа процессоров, быстродействие такой системы перестанет увеличиваться при добавлении нового процессора. Несколько улучшить картину может применение кэш-памяти для хранения команд. При наличии локальной, то есть принадлежащей данному процессору кэш-памяти, следующая необходимая ему команда с большой вероятностью будет находиться в кэш-памяти. В результате этого уменьшается количество обращений к шине и быстродействие системы возрастает. Вместе с тем возникает новая проблема - проблема кэш-когерентности. Эта проблема заключается в том, что если, скажем, двум процессорам для выполнения различных операций понадобилось значение V, это значение будет храниться в виде двух копий в кэшпамяти обоих процессоров. Один из процессоров может изменить это значение в результате выполнения своей команды и оно будет передано в оперативную память компьютера. Но в кэшпамяти второго процессора все еще хранится старое значение! Следовательно, необходимо обеспечить своевременное обновление данных в кэш-памяти всех процессоров компьютера. Имеются и другие реализации разделяемой памяти. Это, например, разделяемая память с дискретными модулями памяти. Физическая память состоит из нескольких модулей, хотя виртуальное адресное пространство остается общим. Вместо общей шины в этом случае используется переключатель, направляющий запросы от процессора к памяти. Такой переключатель может одновременно обрабатывать несколько запросов к памяти, поэтому если все процессоры обращаются к разным модулям памяти, быстродействие возрастает. 5.2 RAID Рассмотрим варианты эффективной организации дисковых накопителей с целью повышения надежности и скорости доступа.на примере RAID массивов. В 1987 г. три американских исследователя Паттерсон, Гибсон и Катц из Калифорнийского университета Беркли написали статью "A Case for Redundant Arrays of Inexpensive Discs (RAID)". В статье описывалось, каким образом можно несколько дешевых жестких дисков объединить в одно логическое устройство таким образом, что в результате объединения повышаются емкость и быстродействие системы, а отказ отдельных дисков не приводит к отказу всей системы. Возможность недорого (inexpensive) построить дисковый массив большой емкости, повышенного быстродействия и, к тому же, отказоустойчивый, с этого времени постоянно беспокоит умы компьютерщиков. Однако широкое распространение такие системы получили только сейчас. Это связано с тем, что с недороговизной (inexpensiveness) получилась маленькая неувязочка. Всем ведь известно, что не всякие комплектующие подходят, чтобы вышла «конфетка». В самые первые RAID-ы, чтобы они действительно работали, пришлось устанавливать очень дорогие дисковые устройства от мэйнфреймов. Поэтому и аббревиатуру RAID стали расшифровывать как "Redundant Arrays of Independent Discs" – избыточный массив независимых дисков. Сейчас достаточно хорошие диски имеют разумную стоимость, и RAID становится основным элементом современного сервера любого уровня (а часто и не только сервера). Тем не менее, когда мы говорим о RAID, то о дешевизне лучше сразу забыть. Стоимость полных RAID-систем не снижается так же быстро, как цены на базовые диски, из-за «накладных расходов»: контроллеры, коннекторы, специализированное программное обеспечение и т.п. Массивы RAID становятся вместительнее, дешевле и приобретают новые возможности. Однако покупателям по-прежнему приходится определять приоритеты и искать компромиссы: между функциональностью и ценой; между производительностью и объемом дисковой памяти. Возможность одновременной работы с несколькими дисками можно реализовать двумя способами: с использованием параллельного доступа (parallel-access array) и с использованием независимого доступа (independent-access array). Для организации параллельного доступа рабочее пространство дисков размечается на зоны определенного размера (блоки) для размещения данных и избыточной информации. Информация, подлежащая записи на диски (запрос на обслуживание), разбивается на такие же по величине блоки, и каждый блок записывается на отдельный диск. При поступлении запроса на чтение, необходимая информация собирается из нескольких блоков (рис. 1). Рис. 1. Массив с параллельным доступом Понятно, что в этом случае скорость записи (равно как и скорость чтения) увеличивается пропорционально количеству дисков, объединенных в RAID. Для организации независимого доступа рабочее пространство дисков также размечается на зоны определенного размера (блоки). Однако, в отличие от предыдущего случая, каждый запрос на запись или чтение обслуживается только одним диском (рис. 2). Рис. 2. Массив с независимым доступом Естественно, в этом случае скорость записи будет не выше, чем при работе с одним диском. Однако массив с независимым доступом в каждый момент времени может обслуживать одновременно несколько запросов, каждый диск обслуживает свой запрос. Таким образом, оба архитектурных решения способствуют повышению производительности, но механизм повышения производительности у этих решений различен. Соответственно, свойства RAID существенно зависят от того, какой из этих двух механизмов в нем используется. Именно поэтому при сравнении RAID различного уровня в первую очередь необходимо сравнивать размер логических блоков. Точнее говоря, не собственно размер, а соотношение размера блока и величины запроса на обслуживание (объем информации, подлежащей записи или считыванию). Другим фактором, влияющим на производительность, является способ размещения избыточной информации. Избыточная информация может храниться на специально выделенном для этого диске и может распределяться по всем дискам. И, наконец, в RAID различного уровня применяются различные способы вычисления избыточной информации. Это также влияет на характеристики RAID (надежность, в первую очередь, производительность и стоимость). Основные способы: полное дублирование информации, применение кодов с коррекцией ошибок (применяется код с коррекцией одиночных ошибок и обнаружением двойных ошибок ECC – код Хемминга) и вычисление четности (Parity). Для стандартизации продуктов RAID в 1992 году был организован промышленный консорциум – Комиссия советников по RAID (RAID Advisory Board: RAB). В настоящее время комиссией стандартизировано 8 вариантов (уровней) объединения дисков в массивы: от RAID-0 до RAID-7. Номера уровней определены просто в порядке, в котором были предложены различные варианты и не связаны с характеристиками RAID. Применяются также комбинированные уровни, например, уровень 0+1 означает RAID уровня 0, но в этот RAID объединены не одиночные диски, а несколько RAID уровня 1 (несколько зеркальных дисков). Уровни с 1-го по 5-й включительно были представлены в упомянутой выше статье Калифорнийского университета Беркли, и эта систематика RAID была принята как стандарт «де факто». RAID-0, строго говоря, вообще не является избыточным массивом (RAID); тем не менее, данный термин широко применяется и поэтому разрешен RAB. RAID 0. Дисковый массив без отказоустойчивости (Striped Disk Array without Fault Tolerance) Представляет собой дисковый массив, в котором данные разбиваются на блоки, и каждый блок записываются (или же считывается) на отдельный диск. Таким образом, можно осуществлять несколько операций ввода-вывода одновременно. Уровни 6 и 7 по различным причинам применяются очень редко. Например, RAID 7® является зарегистрированной торговой маркой Storage Computer Corporation (SCC), поэтому применяется исключительно в изделиях этой фирмы. Более того, многие производители RAID-контроллеров уровнями 6 и 7 обозначают нестандартизированные или комбинированные уровни. Например, в контроллерах фирмы Mylex под RAID уровня 6 понимается комбинированный уровень 0+1, а под RAID уровня 7 – простое объединение нескольких дисков в один логический. Классификация уровней RAID, составленная с учетом сделанных выше замечаний, приведена на рис. 3. Рис. 3. Классификация уровней RAID RAID уровня 1 (Mirrored disk) предназначен в основном для обеспечения отказоустойчивости системы. За счет полного дублирования информации обеспечивается очень высокий уровень надежности. Однако и стоимость хранения информации получается немалой. RAID уровня 2 (Memory-Style ECC) характеризуется очень высокой надежностью. В RAID уровня 2 применяется код с коррекцией одиночных ошибок и обнаружением двойных ошибок ECC. Такой же метод, кстати, применяется и для коррекции ошибок в оперативной памяти серверов. В RAID уровня 2 расслоение данных для записи или чтения осуществляется на уровне битов. Однако, вследствие применения кода с коррекцией ошибок, RAID уровня 2 требует для хранения контрольной информации более одного диска. Большинство контрольных дисков, используемых в RAID уровня 2, нужны для определения положения неисправного разряда. Но в этом сейчас уже нет нужды, так как большинство контроллеров в состоянии самостоятельно определить, когда диск отказал при помощи специальных сигналов, или дополнительного кодирования информации, записанной на диск и используемой для исправления случайных сбоев. В связи с этим RAID уровня 2 не нашел практического применения. В RAID уровней 3, 4 и 5 применяется простое вычисление четности путем применения к записываемым блокам операции «исключающее или» – XOR (Parity), что позволяет использовать меньшее количество избыточных дисков. В RAID уровня 3 (Bit-Interleaved Parity) для обеспечения отказоустойчивости вводится один дополнительный диск, на который записывается дополнительная (контрольная) информация. При записи данные разбиваются на блоки, каждый из которых записывается на отдельный диск. Затем вычисляется контрольная сумма, которая записывается на дополнительный диск. При выходе из строя любого диска данные на нем можно восстановить по контрольным данным и данным, оставшимся на исправных дисках. Как и в RAID уровня 2 расслоение данных для записи или чтения осуществляется на уровне битов (вообще говоря, допускается и расслоение на уровне байтов). Таким образом, в RAID уровня 3 реализуется практически в чистом виде архитектура с параллельным доступом. RAID уровня 4 (Block-Interleaved Parity) отличается от RAID уровня 3 в первую очередь значительно большим размером блока записываемых данных (большим, чем размер записываемых данных). Типичное значение – кратно размеру сектора жесткого диска. Это и есть главное отличие между RAID 3 и 4. RAID уровня 5 (Block-Interleaved Distributed-Parity) так же, как и RAID уровня 4, отличается от RAID уровня 3 большим размером блока записываемых данных. Кроме того, в отличие от RAID уровня 4, для хранения избыточной информации не выделяется отдельный диск, а контрольная информация записывается на различные диски по очереди. Такое размещение избыточной информации позволяет повысить производительность дискового массива, о чем мы поговорим далее. Однако, подчеркиваю, главное отличие RAID уровней 3 и 5 состоит, вопреки распространенному мнению, не в методе хранения избыточной информации, а в размере логических блоков, записываемых на каждый диск. В отличие от RAID уровня 3, в RAID уровня 5 реализуется архитектура с независимым доступом. Время ожидания обслуживания RAID разного уровня, естественно, по-разному ведут себя при увеличении количества запросов. Это связано в первую очередь с соотношением размера блоков на дисках и размера записываемых (считываемых) данных. Давайте еще раз вернемся к данному вопросу. Для иллюстрации на рис. 4 приведено соотношение размера запросов и размера блоков для двух случаев: запись или чтение большого файла («длинный» запрос) и малого файла («короткий» запрос). Рис. 4. Соотношение длины запросов и размера логических блоков Данный рисунок соответствует RAID уровня 3 или 4 (в зависимости от величины запроса на обслуживание), организованного на трех дисках. «Длинный» запрос хорошо иллюстрирует особенности RAID уровня 3. Такой запрос разбивается на отдельные блоки. Половина этих блоков (в соответствии с рисунком) записывается на один диск, а половина на другой. Одновременно на третий диск записывается контрольная информация. Операция вычисления четности – это очень быстрая операция и легко реализуется аппаратно. Поэтому дополнительными временными задержками, связанными с вычислением избыточной операции, можно пренебречь. В итоге большой файл будет записан или считан в два раза быстрее (для системы из 3-х дисков, как на рис. 4) чем, если бы запись осуществлялась на одиночный диск. Однако любой другой запрос, поступивший в это время, будет ожидать обслуживания. Таким образом, при интенсивном потоке запросов время ожидания может быть весьма значительным. «Короткий» запрос соответствует режиму работы RAID уровня 4 или 5. Если размер записываемого файла меньше чем размер логического блока, то такой файл, естественно, не может быть разбит на части. Он весь размещается на каком-либо одном диске. Поэтому и скорость записи должна была бы быть точно такой, как и при записи на одиночный диск. Однако контрольная информация, связанная с модифицируемым блоком, соответствует всему блоку, а не только той части, которая модифицируется. Поэтому реально, чтобы выполнить запись, необходимо вначале считать модифицируемый блок и контрольную сумму, затем вычислить новое значение контрольной суммы и только после этого записать модифицированный блок и новое значение контрольной суммы. Вместо операции записи в RAID-5 и -4 фактически осуществляется операция «чтение-модификация-запись». Таким образом, в RAID уровня 4 и 5 скорость записи практически вдвое хуже, чем при использовании одиночного диска. Это – самая большая проблема в RAID уровня 5, и производители RAID-контроллеров ведут интенсивные работы по ее преодолению. Однако наличие такой проблемы еще не говорит о том, что RAID уровня 5 обладает низкой производительностью или уступает по этому показателю RAID уровня 3. При чтении, RAID уровня 4 или 5 может одновременно обслужить несколько запросов, благодаря чему производительность RAID уровня 5 может оставаться высокой даже при весьма интенсивном потоке запросов на обслуживание. В RAID уровня 5 одновременно также может обслуживаться и несколько запросов на запись. Именно для этого контрольная информация размещается не на одном диске, а чередуется на всех. Поэтому, в целом, время ожидания обслуживания при интенсивном потоке малых запросов в RAID уровня 5 оказывается лучше, чем, например, в RAID уровня 3. Типичная зависимость времени обслуживания запросов малого размера (в потоке – 25% запросов на запись) для RAID различных уровней приведена на рис. 5. Заметим, что условия измерения близки к реальным условиям в системе с централизованной базой данных Клиент-Серверной технологии. Рис. 5. Типичная зависимость времени обслуживания запросов от интенсивности запросов Формы кривых для RAID различного уровня хорошо объясняют поведение системы с RAID в реальных условиях. RAID-0+1 обеспечивает минимальное время обслуживания при гораздо более интенсивном потоке запросов, чем все остальные уровни RAID. Более того, увеличение времени обслуживания при увеличении интенсивности потока запросов происходит достаточно плавно, благодаря чему уменьшение производительности почти незаметно для большинства пользователей. Несколько худшими характеристиками обладает система, использующая RAID-1. При увеличении интенсивности запросов увеличение времени обслуживания практически пропорционально числу запросов (примерно эквивалентно количеству подключенных к системе пользователей). Плавное снижение производительности очень хорошо воспринимается пользователями. В большинстве случаев именно RAID уровня 1 или уровня 0+1 обеспечивают наилучшую производительность в реальных системах. RAID уровня 3 обеспечивает очень высокую производительность при редких запросах, но при увеличении интенсивности запросов увеличение времени обслуживания происходит очень быстро. Это не позволяет рекомендовать RAID уровня 3 в серверах, и особенно в серверах, предназначенных для обслуживания баз данных. RAID уровня 5 занимает промежуточное положение. Несколько по-другому ведут себя RAID-ы при обслуживании больших запросов (интенсивный обмен длинными файлами). Для таких приложений RAID-3 показывает ощутимо лучшую производительность, чем RAID-5. Возможности RAID-0 несколько скромнее, однако лучше, чем у RAID-3 и RAID-5. Эти условия оказываются очень трудными для RAID-1. По времени обслуживания длинных запросов RAID-1 значительно уступает всем другим вариантам RAID. По-прежнему лидирующим оказывается RAID-0+1. Затраты и целесообразность RAID различного уровня для своей организации требуют также и различных аппаратных затрат. Предположим, если необходима дисковая система емкостью 36 ГБ, то можно, например, использовать 4 диска по 9 ГБ каждый. Однако с учетом дисков, необходимых для размещения избыточной информации, потребуется установить больше дисков (см. рис. 6). Рис. 6. Количество дисков в системах с RAID разного уровня Приведенное выше сравнение показывает наиболее целесообразные области применения RAID различного уровня. В серверах начального уровня, когда на первый план выдвигается вопрос стоимости, целесообразно применять RAID уровня 1 (требуется только один дополнительный диск, зеркальный с основным). В серверах, для которых основным требованием является высокая производительность, лучшим решением будет RAID уровня 0+1. В большинстве же других случаев RAID уровня 5 обеспечивает достаточно высокую производительность при умеренных затратах. RAID уровня 3 целесообразно применять только в специальных серверах, предназначенных для хранения больших файлов (графика, видео и т.п.). Но и в этом случае RAID уровня 0+1 обеспечит лучшую производительность. Технология I2O в RAID-контроллерах I2O (Intelligent Input/Output) – это спецификация, которая определяет стандартную архитектуру интеллектуального ввода-вывода и не зависит от конкретных устройств и операционной системы. Идея технологии I2O заключается в том, чтобы за счет применения отдельного процессора ввода-вывода разгрузить центральный процессор. Все низкоуровневые прерывания, поступающие от периферийных устройств, обрабатываются не центральным процессором, а специализированным процессором ввода-вывода (IOP). Конечно, специализированные процессоры ввода-вывода применялись очень давно, еще в «больших» ЭВМ. Но наличие отдельного процессора, даже занятого операциями ввода-вывода, еще не означает технологии I2O. В конце концов, сейчас микропроцессоры встроены в каждый жесткий диск. В данном случае речь идет о новом витке в применении известного решения. Спецификация I2O определяет разбиение драйвера устройства на две части: ОС-зависимого модуля (OSM – Operation System Services Module) и аппаратно-зависимого модуля (HDM – Hardware Device Module). Благодаря этому в значительной мере решается задача устранения зависимости от конкретной операционной системы. Под конкретную операционную систему разрабатывается только драйвер OSM. По идеологии любое I2O-совместимое устройство (в нашем случае контроллер) не требует установки каких-либо драйверов. Так, например, мы подключаем жесткие диски. Операционная система сама должна «узнать» устройство. 5.3 Межпроцессорные связи Многопроцессорные системы обработки информации классифицируются на системы с общей памятью и системы с коммутацией сообщений. Первый тип систем называют еще системами с сильной связью, а вторые - со слабой связью. Каждый процессор в многопроцессорной системе второго типа имеет особую память и канал, обеспечивающий связь с другими процессорами через сеть связи. В системах с общей памятью одно (единое) пространство памяти совместно используется всеми процессорами. Фактически общая память организуется с помощью системы связи (коммутации), как это показано на рис.3.1 Общее пространство памяти разбивается на множество модулей памяти (МП), которые соединяются с процессорами при помощи системы связи. Число процессоров, которые можно использовать для реализации многопроцессорной системы с общей памятью, ограничивается конфликтами при доступе к общей памяти. Для смягчения этих конфликтов каждый процессор снабжают локальной памятью небольшого объема или кэш-памятью. В системах с коммутацией сообщений рис.3.2 связь между процессорами осуществляется в форме передачи сообщений через посредство каналов или портов ввода-вывода и процессорную сеть связи. В таких системах часто используется пакетный режим передачи. По сравнению с системами с общей памятью таким системам свойственен большой перерасход времени при передаче данных, поэтому их не выгодно использовать в тех случаях, когда сложные структуры данных обрабатываются несколькими процессорами, однако, если частота передач и объем передаваемых данных небольшие, такую систему можно использовать даже при большом числе процессоров . Для взаимосвязи между процессорами можно использовать различные типы сетей. В системах с общей памятью наиболее распространенными являются сети типа общей шины, использующие одну или несколько шин Самой эффективной была бы топология, в которой любой узел мог бы напрямую связаться с любым другим узлом. Однако в MPP-системах это технически трудно реализуемо. В системе с коммутацией сообщений процессоры могут связывать линейные рис.3.4а, кольцевые рис.3.4б, решетчатые (плоскостные) рис.3.4в, древовидные, кубические и гиперкубические сети. Наиболее распространенными являются кольцевые, решетчатые и гиперкубические сети связи. 5.4 Симметричные мультипроцессорные системы (SMP Symmetric Multi-Processing) Симметричный многопроцессорный (SMP) узел содержит два или более одинаковых процессора, используемых равноправно. Все процессоры имеют одинаковый доступ к вычислительным ресурсам узла. Поскольку процессоры одновременно работают с данными, хранящимися в единой памяти узла, в SMP-архитектурах обязательно должен быть механизм, поддержки когерентности данных. Когерентность данных означает, что в любой момент времени для каждого элемента данных во всей памяти узла существует только одно его значение несмотря на то, что одновременно могут существовать несколько копий элемента данных, расположенных в разных видах памяти и обрабатываемых разными процессорами. Механизм когерентности должен следить за тем, чтобы операции с одним и тем же элементом данных выполнялись на разных процессорах последовательно, удаляя, в частности, устаревшие копии. В современных SMP-архитектурах когерентность реализуется аппаратными средствами. Механизм когерентности является критичным для эффективной параллельной работы узла SMP и должен иметь малое время задержки. До сегодняшнего дня самые крупные SMP-системы содержали максимум 32 процессора на узел, что объяснялось требованием малых задержек когерентных связей, приводящим к архитектуре с одной объединительной платой, а это физически ограничивает возможное число подсоединенных процессоров и плат памяти. Поэтому для дальнейшего увеличения числа процессоров в узле приходится вместо аппаратно реализованной техники когерентности применять более медленную программную реализацию, что очень существенно сказывается на программируемости систем и их производительности. SMP-узлы очень удобны для разработчиков приложений: операционная система почти автоматически масштабирует приложения, давая им возможность использовать наращиваемые ресурсы. Само приложение не должно меняться при добавлении процессоров и не обязано следить за тем, на каких ЦПУ оно работает. Временная задержка доступа от любого ЦПУ до всех частей памяти и системы ввода-вывода одна и та же. Разработчик оперирует с однородным адресным пространством. Все это приводит к тому, что SMP-архитектуры разных производителей выглядят в основном одинаково: упрощается переносимость программного обеспечения между SMP-системами. Переносимость программ - одно из основных достоинств SMP-платформ. Типичные SMP-архитектуры в качестве аппаратной реализации механизма поддержки когерентности используют шину слежения (snoopy bus). Каждый процессор имеет свой собственный локальный кэш, где он хранит копию небольшой части основной памяти, доступ к которой наиболее вероятен. Для того чтобы все кэши оставались когерентными, каждый процессор "подглядывает" за шиной, осуществляя поиск тех операций считывания и записи между другими процессорами и основной памятью, которые влияют на содержимое их собственных кэшей. Если процессор "В" запрашивает ту часть памяти, которая обрабатывается процессором "А", то процессор "А" перехватывает этот запрос и помещает свои значения области памяти на шину, где "В" их считывает. Когда процессор "А" записывает измененное значение обратно из своего кэша в память, то все другие процессоры видят, как эта запись проходит по шине и удаляют устаревшие значения из своих кэшей. SMP система состоит из нескольких однородных процессоров и массива общей памяти. Один из часто используемых в SMP архитектурах подходов для формирования масштабируемой, общедоступной системы памяти, состоит в однородной организации доступа к памяти посредством организации масштабируемого канала память-процессоры. Каждая операция доступа к памяти интерпретируется как транзакция по шине процессоры - память. Когерентность кэшей поддерживается аппаратными средствами. Недостатком данной архитектуры является необходимость организации канала процессоры - память с очень высокой пропускной способностью. 5.5 Массивно-параллельные системы (МРР Massively Parallel Processing) Узлы в архитектуре MPP обычно состоят из одного ЦПУ, небольшой памяти и нескольких устройств ввода-вывода. В каждом узле работает своя копия OC, а узлы объединяются между собой специализированным соединением. Взаимосвязи между узлами (и между копиями ОС, принадлежащими каждому узлу) не требуют аппаратно поддерживаемой когерентности, так как каждый узел имеет собственную ОС и, следовательно, свое уникальное адресное пространство физической памяти. Когерентность реализуется программными средствами, с использованием техники передачи сообщений. Задержки, которые присущи программной поддержке когерентности на основе сообщений, обычно в сотни и тысячи раз больше, чем те, которые получаются в системах с аппаратными средствами. С другой стороны, их реализация значительно менее дорогая. В некотором смысле в МРР-узлах задержкой приходится жертвовать, чтобы подсоединить большее число процессоров - сотни и даже тысячи узлов. Известно, что производительность МРР-систем весьма чувствительна к задержкам, определяемым программной реализацией протоколов и аппаратной реализацией среды передачи сообщений (будь то коммутатор, или сеть). Вообще говоря, настройка производительности МРР-систем включает распределение данных для того, чтобы минимизировать трафик между узлами. МРР-архитектуры привлекательны в первую очередь для разработчиков аппаратных средств, так как в этом случае возникает меньше проблем и ниже стоимость аппаратуры. Из-за того, что нет аппаратной поддержки ни для разделенной памяти, ни для когерентности кэшей, подсоединить большое число процессоров очень просто. Такие системы обеспечивают высокий уровень производительности для приложений с большой интенсивностью вычислений, со статистически разделяемыми данными и с минимальным обменом данными между узлами. Для большинства коммерческих приложений МРР-системы подходят плохо из-за того, что структура базы данных меняется со временем и слишком велики затраты на перераспределение данных. Ключевым различием между одиночным SMP-узлом и МРР-системой является то, что внутри SMP-узла когерентность данных поддерживается исключительно аппаратными средствами. Это действительно быстро, но и дорого. В МРР-системе с таким же числом процессоров когерентность между узлами реализуется программными средствами. Поэтому происходит это более медленно, однако и цена значительно ниже. MPP система состоит из нескольких однородных вычислительных узлов, включающих один или несколько процессоров, локальную для каждого узла память, коммуникационный процессор или сетевой адаптер. Узлы объединяются через высокоскоростную сеть или коммутатор. Существуют два основных варианта: 1) Полноценная ОС работает только на управляющей машине (front-end), на каждом узле работает сильно урезанный вариант ОС, обеспечивающие только работу расположенной в нем ветви параллельного приложения. Пример: Cray T3E. 2) На каждом узле работает полноценная UNIX-подобная ОС (вариант, близкий к кластерному подходу). Пример: IBM RS/6000 SP + ОС AIX, устанавливаемая отдельно на каждом узле. Модель программирования Программирование в рамках модели передачи сообщений Системы с неоднородным доступом к памяти (NUMA) (non uniform memory access) Система состоит из однородных базовых модулей (плат), состоящих из небольшого числа процессоров и блока памяти. Модули объединены с помощью высокоскоростного коммутатора. Поддерживается единое адресное пространство, аппаратно поддерживается доступ к удаленной памяти, т.е. к памяти других модулей. При этом доступ к локальной памяти в несколько раз быстрее, чем к удаленной. В случае, если аппаратно поддерживается когерентность кэшей во всей системе (обычно это так), говорят об архитектуре cc-NUMA (cache-coherent NUMA) Обычно вся система работает под управлением единой ОС, как в SMP. Но возможны также варианты динамического "подразделения" системы, когда отдельные "разделы" системы работают под управлением разных ОС (например, Windows NT и UNIX в NUMA-Q 2000) 5.6 Параллельно-векторные системы (PVP)  При поддержке команд обработки векторных данных говорят о векторно-конвейерных процессорах, которые, в свою очередь могут объединяться в системы с использованием общей или распределенной памяти. Основным признаком PVP-систем является наличие специальных векторно-конвейерных процессоров, в которых предусмотрены команды однотипной обработки векторов независимых данных, эффективно выполняющиеся на конвейерных функциональных устройствах. Как правило, несколько таких процессоров (1-16) работают одновременно над общей памятью (аналогично SMP) в рамках многопроцессорных конфигураций. Несколько таких узлов могут быть объединены с помощью коммутатора (аналогично MPP). Эффективное программирование подразумевает векторизацию циклов (для достижения разумной производительности одного процессора) и их распараллеливание (для одновременной загрузки нескольких процессоров одним приложением). 5.7 Кластерные системы Набор рабочих станций (или даже ПК) общего назначения, используется в качестве дешевого варианта массивно-параллельного компьютера. Для связи узлов используется одна из стандартных сетевых технологий (Fast/Gigabit Ethernet, Myrinet) на базе шинной архитектуры или коммутатора. Кластер состоит из двух или более узлов, удовлетворяющих следующим требованиям: • каждый узел работает со своей копией ОС; • каждый узел работает со своей копией приложения; • узлы делят общий пул других ресурсов, таких как накопители на дисках и, возможно, накопители на лентах В отличие от кластеров, в МРР-системах узлы не делят ресурсы для хранения. Это главное отличие между кластерными SMP-системами и традиционными МРР-системами. Лекция 6. Организация связей в ЭВМ Рассмотрим сначала типовую ЭВМ. Классически она состоит из АЛУ, управления, памяти, периферийных устройств. Это здорово, но они ведь еще должны между собой взаимодействовать. В вычислительной системе, состоящей из множества подсистем, необходим механизм для их взаимодействия. Эти подсистемы должны быстро и эффективно обмениваться данными. Например, процессор, с одной стороны, должен быть связан с памятью, с другой стороны, необходима связь процессора с устройствами ввода/вывода. Одним из простейших механизмов, позволяющих организовать взаимодействие различных подсистем, является единственная центральная шина, к которой подсоединяются все подсистемы. Доступ к такой шине разделяется между всеми подсистемами. Подобная организация имеет два основных преимущества: низкая стоимость и универсальность. Поскольку такая шина является единственным местом подсоединения для разных устройств, новые устройства могут быть легко добавлены, и одни и те же периферийные устройства можно даже применять в разных вычислительных системах, использующих однотипную шину. Стоимость такой организации получается достаточно низкой, поскольку для реализации множества путей передачи информации используется единственный набор линий шины, разделяемый множеством устройств. Главным недостатком организации с единственной шиной является то, что шина создает узкое горло, ограничивая, возможно, максимальную пропускную способность ввода/вывода. Если весь поток ввода/вывода должен проходить через центральную шину, такое ограничение пропускной способности весьма реально. В суперкомпьютерах, где необходимые скорости ввода/вывода очень высоки из-за высокой производительности процессора, одним из главных вопросов разработки является создание системы нескольких шин, способной удовлетворить все запросы. Одна из причин больших трудностей, возникающих при разработке шин, заключается в том, что максимальная скорость шины главным образом лимитируется физическими факторами: длиной шины и количеством подсоединяемых устройств (и, следовательно, нагрузкой на шину). Эти физические ограничения не позволяют произвольно ускорять шины. Требования быстродействия (малой задержки) системы ввода/вывода и высокой пропускной способности являются противоречивыми. В современных крупных системах используется целый комплекс взаимосвязанных шин, каждая из которых обеспечивает упрощение взаимодействия различных подсистем, высокую пропускную способность, избыточность (для увеличения отказоустойчивости) и эффективность. (мезонинная организация) Традиционно шины делятся на шины, обеспечивающие организацию связи процессора с памятью, и шины ввода/вывода. Шины ввода/вывода могут иметь большую протяженность, поддерживать подсоединение многих типов устройств, и обычно следуют одному из шинных стандартов. Шины процессор-память, с другой стороны, сравнительно короткие, обычно высокоскоростные и соответствуют организации системы памяти для обеспечения максимальной пропускной способности канала память-процессор. На этапе разработки системы, для шины процессор-память заранее известны все типы и параметры устройств, которые должны соединяться между собой, в то время как разработчик шины ввода/вывода должен иметь дело с устройствами, различающимися по задержке и пропускной способности. Как уже было отмечено, с целью снижения стоимости некоторые компьютеры имеют единственную шину для памяти и устройств ввода/вывода. Такая шина часто называется системной. Персональные компьютеры раньше строились на основе одной системной шины в стандартах ISA. Необходимость сохранения баланса производительности по мере роста быстродействия микропроцессоров привела к двухуровневой организации шин в персональных компьютерах на основе локальной шины. Локальной шиной называется шина, электрически выходящая непосредственно на контакты микропроцессора. Она обычно объединяет процессор, память, схемы буферизации для системной шины и ее контроллер, а также некоторые вспомогательные схемы. Типичными примерами локальных шин является PCI. Главное устройство шины - это устройство, которое может инициировать транзакции чтения или записи. ЦП, например, всегда является главным устройством шины. Шина имеет несколько главных устройств, если имеется несколько ЦП или когда устройства ввода/вывода могут инициировать транзакции на шине. Если имеется несколько таких устройств, то требуется схема арбитража, чтобы решить, кто следующий захватит шину. bus masters и bus slaves. Bus masters - это устройства, способные управлять работой шины, т.е инициировать запись/чтение и т.д. Bus slaves - соответственно, устройства, которые могут только отвечать на запросы. Используются два типа шин, отличающиеся способом коммутации: шины с коммутацией цепей (circuit-switched bus) и шины с коммутацией пакетов (packet-switched bus), получившие свои названия по аналогии со способами коммутации в сетях передачи данных. Шина с коммутацией пакетов при наличии нескольких главных устройств шины обеспечивает значительно большую пропускную способность по сравнению с шиной с коммутацией цепей за счет разделения транзакции на две логические части: запроса шины и ответа. Такая методика получила название "расщепления" транзакций (split transaction). (В некоторых системах такая возможность называется шиной соединения/разъединения (connect/disconnect) или конвейерной шиной (pipelined bus). Транзакция чтения разбивается на транзакцию запроса чтения, которая содержит адрес, и транзакцию ответа памяти, которая содержит данные. Каждая транзакция теперь должна быть помечена (тегирована) соответствующим образом, чтобы ЦП и память могли сообщить что есть что. Шина с коммутацией цепей не делает расщепления транзакций, любая транзакция на ней есть неделимая операция. Главное устройство запрашивает шину, после арбитража помещает на нее адрес и блокирует шину до окончания обслуживания запроса. Большая часть этого времени обслуживания при этом тратится не на выполнение операций на шине (например, на задержку выборки из памяти). Таким образом, в шинах с коммутацией цепей это время просто теряется. Расщепленные транзакции делают шину доступной для других главных устройств пока память читает слово по запрошенному адресу. Это, правда, также означает, что ЦП должен бороться за шину для посылки данных, а память должна бороться за шину, чтобы вернуть данные. Таким образом, шина с расщеплением транзакций имеет более высокую пропускную способность, но обычно она имеет и большую задержку, чем шина, которая захватывается на все время выполнения транзакции. Транзакция называется расщепленной, поскольку произвольное количество других пакетов или транзакций могут использовать шину между запросом и ответом. Последний вопрос связан с выбором типа синхронизации и определяет является ли шина синхронной или асинхронной. Если шина синхронная, то она включает сигналы синхронизации, которые передаются по линиям управления шины, и фиксированный протокол, определяющий расположение сигналов адреса и данных относительно сигналов синхронизации. Поскольку практически никакой дополнительной логики не требуется для того, чтобы решить, что делать в следующий момент времени, эти шины могут быть и быстрыми, и дешевыми. Однако они имеют два главных недостатка. Все на шине должно происходить с одной и той же частотой синхронизации, поэтому из-за проблемы перекоса синхросигналов, синхронные шины не могут быть длинными. Обычно шины процессор-память синхронные. Асинхронная шина, с другой стороны, не тактируется. Вместо этого обычно используется старт-стопный режим передачи и протокол "рукопожатия" (handshaking) между источником и приемником данных на шине. Эта схема позволяет гораздо проще приспособить широкое разнообразие устройств и удлинить шину без беспокойства о перекосе сигналов синхронизации и о системе синхронизации. Если может использоваться синхронная шина, то она обычно быстрее, чем асинхронная, из-за отсутствия накладных расходов на синхронизацию шины для каждой транзакции. Выбор типа шины (синхронной или асинхронной) определяет не только пропускную способность, но также непосредственно влияет на емкость системы ввода/вывода в терминах физического расстояния и количества устройств, которые могут быть подсоединены к шине. Асинхронные шины по мере изменения технологии лучше масштабируются. Шины ввода/вывода обычно асинхронные. Лекция 7. Коммуникационная сеть Коммуникационная сеть обеспечивает передачу информации между вычислительными узлами и узлами ввода/вывода. Временные задержки при передаче данных по линиям связи могут оказаться существенными (по сравнению с быстродействием процессоров), и, как результат, коммуникационная трудоемкость алгоритма оказывает заметное влияние на выбор параллельных способов решения задач. 7.1. Топологии сети передачи данных Структура линий коммутации между процессорами вычислительной системы ( топология сети передачи данных ) определяется, как правило, с учетом возможностей эффективной технической реализации. Немаловажную роль при выборе структуры сети играет и анализ интенсивности информационных потоков при параллельном решении наиболее распространенных вычислительных задач. К числу типовых топологий обычно относят следующие схемы коммуникации процессоров (см. рис. 1.7): • полный граф ( completely-connected graph или clique ) – система, в которой между любой парой процессоров существует прямая линия связи. Такая топология обеспечивает минимальные затраты при передаче данных, однако является сложно реализуемой при большом количестве процессоров; • линейка ( linear array или farm ) – система, в которой все процессоры перенумерованы по порядку и каждый процессор, кроме первого и последнего, имеет линии связи только с двумя соседними (с предыдущим и последующим) процессорами. Такая схема является, с одной стороны, просто реализуемой, c другой стороны, соответствует структуре передачи данных при решении многих вычислительных задач (например, при организации конвейерных вычислений); • кольцо ( ring ) – данная топология получается из линейки процессоров соединением первого и последнего процессоров линейки; • звезда ( star ) – система, в которой все процессоры имеют линии связи с некоторым управляющим процессором. Данная топология является эффективной, например, при организации централизованных схем параллельных вычислений; • решетка ( mesh ) – система, в которой граф линий связи образует прямоугольную сетку (обычно двух- или трехмерную). Подобная топология может быть достаточно просто реализована и, кроме того, эффективно использована при параллельном выполнении многих численных алгоритмов (например, при реализации методов анализа математических моделей, описываемых дифференциальными уравнениями в частных производных); • гиперкуб ( hypercube ) – данная топология представляет собой частный случай структуры решетки, когда по каждой размерности сетки имеется только два процессора (т.е. гиперкуб содержит 2N процессоров при размерности N ). Такой вариант организации сети передачи данных достаточно широко распространен на практике и характеризуется следующим рядом отличительных признаков: Рис. 1.7. Примеры топологий многопроцессорных вычислительных систем - два процессора имеют соединение, если двоичные представления их номеров имеют только одну различающуюся позицию; - в N -мерном гиперкубе каждый процессор связан ровно с N соседями; - N -мерный гиперкуб может быть разделен на два ( N–1 )-мерных гиперкуба (всего возможно N различных таких разбиений); - кратчайший путь между двумя любыми процессорами имеет длину, совпадающую с количеством различающихся битовых значений в номерах процессоров (данная величина известна как расстояние Хэмминга ). При рассмотрении вопроса следует учесть, что схема линий передачи данных может реализовываться на аппаратном уровне, а может быть обеспечена на основе имеющейся физической топологии при помощи соответствующего программного обеспечения. Введение виртуальных (программно-реализуемых) топологий способствует мобильности разрабатываемых параллельных программ и снижает затраты на программирование. Для построения кластерной системы во многих случаях используют коммутатор ( switch ), через который процессоры кластера соединяются между собой. В этом случае топология сети кластера представляет собой полный граф (рис. 1.7), в соответствии с которым передача данных может быть организована между любыми двумя процессорами сети. При этом, однако, одновременность выполнения нескольких коммуникационных операций является ограниченной – в любой момент времени каждый процессор может принимать участие только в одной операции приема-передачи данных. Как результат, параллельно могут выполняться только те коммуникационные операции, в которых взаимодействующие пары процессоров не пересекаются между собой. В качестве основных характеристик топологии сети передачи данных наиболее широко используется следующий ряд показателей: • диаметр – показатель, определяемый как максимальное расстояние между двумя процессорами сети (под расстоянием обычно понимается величина кратчайшего пути между процессорами). Эта величина может характеризовать максимально необходимое время для передачи данных между процессорами, поскольку время передачи обычно прямо пропорционально длине пути; • связность ( connectivity ) – показатель, характеризующий наличие разных маршрутов передачи данных между процессорами сети. Конкретный вид данного показателя может быть определен, например, как минимальное количество дуг, которое надо удалить для разделения сети передачи данных на две несвязные области; • ширина бинарного деления ( bisection width ) – показатель, определяемый как минимальное количество дуг, которое надо удалить для разделения сети передачи данных на две несвязные области одинакового размера; • стоимость – показатель, который может быть определен, например, как общее количество линий передачи данных в многопроцессорной вычислительной системе. Для сравнения в таблице 1.1 приводятся значения перечисленных показателей для различных топологий сети передачи данных. Таблица 1.1. Характеристики топологий сети передачи данных (p – количество процессоров) Топология Диаметр Ширина Связность бисекции Стоимость Полный граф 1 p2/4 p–1 p(p–1)/2 Звезда 2 1 1 p–1 Полное двоичное дерево 2log((p+1)/2) 1 1 p–1 Линейка p–1 1 1 p–1 Кольцо 2 2 p Решетка N=2 2 Решетка-тор N=2 4 2p Гиперкуб log p p/2 log p (p log p)/2 7.2 Факторы, снижающие производительность параллельных компьютеров Анализ факторов, снижающих реальную производительность компьютеров, начнем с обсуждения известного закона Амдала. Смысл его сводится к тому, что время работы программы определяется ее самой медленной частью. В самом деле, предположим, что одна половина некоторой программы - это сугубо последовательные вычисления. Закон Амдала и его следствия Предположим, что в вашей программе доля операций, которые нужно выполнять последовательно, равна f, где 0<=f<=1 (при этом доля понимается не по статическому числу строк кода, а по числу операций в процессе выполнения). Крайние случаи в значениях f соответствуют полностью параллельным (f=0) и полностью последовательным (f=1) программам. Так вот, для того, чтобы оценить, какое ускорение S может быть получено на компьютере из 'p' процессоров при данном значении f, можно воспользоваться законом Амдала: Если 9/10 программы исполняется параллельно, а 1/10 по-прежнему последовательно, то ускорения более, чем в 10 раз получить в принципе невозможно вне зависимости от качества реализации параллельной части кода и числа используемых процессоров (ясно, что 10 получается только в том случае, когда время исполнения параллельной части равно 0). Посмотрим на проблему с другой стороны: а какую же часть кода надо ускорить (а значит и предварительно исследовать), чтобы получить заданное ускорение? Ответ можно найти в следствии из закона Амдала. F=(p-s)/(s*(p-1)) Ex: p=100, s=50 => f=1/99. Отсюда первый вывод - прежде, чем основательно переделывать код для перехода на параллельный компьютер (а любой суперкомпьютер, в частности, является таковым) надо основательно подумать. Если, оценив заложенный в программе алгоритм, вы поняли, что доля последовательных операций велика, то на значительное ускорение рассчитывать явно не приходится и нужно думать о замене отдельных компонент алгоритма. В ряде случаев последовательный характер алгоритма изменить не так сложно. Допустим, что в программе есть следующий фрагмент для вычисления суммы n чисел: s = 0 For i = 1 to n s = s + a(i) End По своей природе он строго последователен, так как на i-й итерации цикла требуется результат с (i-1)-й и все итерации выполняются одна за одной. Имеем 100% последовательных операций, а значит и никакого эффекта от использования параллельных компьютеров. Вместе с тем, выход очевиден. Поскольку в реальных программ нет существенной разницы, в каком порядке складывать числа, выберем иную схему сложения. Сначала найдем сумму пар соседних элементов: a(1)+a(2), a(3)+a(4), a(5)+a(6) и т.д. Заметим, что при такой схеме все пары можно складывать одновременно! На следующих шагах будем действовать абсолютно аналогично, получив вариант параллельного алгоритма. Iter=log2 (n) Казалось бы в данном случае все проблемы удалось разрешить. Но представьте, что доступные вам процессоры разнородны по своей производительности. Значит, будет такой момент, когда кто-то из них еще трудится, а кто-то уже все сделал и бесполезно простаивает в ожидании. Если разброс в производительности компьютеров большой, то и эффективность всей системы при равномерной загрузке процессоров будет крайне низкой. Но пойдем дальше и предположим, что все процессоры одинаковы. Проблемы кончились? Опять нет! Процессоры выполнили свою работу, но результат-то надо передать другому для продолжения процесса суммирования... а на передачу уходит время... и в это время процессоры опять простаивают... Словом, заставить параллельную вычислительную систему или супер-ЭВМ работать с максимальной эффективность на конкретной программе это, прямо скажем, задача не из простых, поскольку необходимо тщательное согласование структуры программ и алгоритмов с особенностями архитектуры параллельных вычислительных систем. Для массивно-параллельных компьютеров он играет еще большую роль, чем для векторно-конвейерных. В самом деле, в таблице 1 показано, на какое максимальное ускорение работы программы можно рассчитывать в зависимости от доли последовательных вычислений и числа доступных процессоров. Предполагается, что параллельная секция может быть выполнена без каких-либо дополнительных накладных расходов. Так как в программе всегда присутствует инициализация, ввод/вывод и некоторые сугубо последовательные действия, то недооценивать данный фактор никак нельзя - практически вся программа должна исполняться в параллельном режиме, что можно обеспечить только после анализа всей (!) программы. Число ПЭ Доля последовательных вычислений 50% 25% 10% 5% 2% 2 1.33 1.60 1.82 1.90 1.96 8 1.78 2.91 4.71 5.93 7.02 32 1.94 3.66 7.80 12.55 19.75 512 1.99 3.97 9.83 19.28 45.63 2048 2.00 3.99 9.96 19.82 48.83 Табл. 1. Максимальное ускорение работы программы в зависимости от доли последовательных вычислений и числа используемых процессоров. Если взять в качестве примера компьютер с распределенной памятью, то взаимодействие процессоров, в основном, осуществляется посредством передачи сообщений друг другу. Отсюда два других замедляющих фактора - время инициализации посылки сообщения (латентность) и собственно время передачи сообщения по сети. Максимальная скорость передачи достигается на больших сообщениях, когда латентность, возникающая лишь в начале, не столь заметна на фоне непосредственно передачи данных. Возможность асинхронной посылки сообщений и вычислений. Если или аппаратура, или программное обеспечение не поддерживают возможности проводить вычислений на фоне пересылок, то возникнут неизбежные накладные расходы, связанные с ожиданием полного завершения взаимодействия параллельных процессов. Для достижения эффективной параллельной обработки необходимо добиться равномерной загрузки всех процессоров. Если равномерности нет, то часть процессоров неизбежно будет простаивать, ожидая остальных, хотя в этот момент они могли бы выполнять полезную работу. Иногда равномерность получается автоматически, например, при обработке прямоугольных матриц достаточно большого размера, однако уже при переходе к треугольным матрицам добиться хорошей равномерности не так просто. Если один процессор должен вычислить некоторые данные, которые нужны другому процессору, и если второй процесс первым дойдет до точки приема соответствующего сообщения, то он с неизбежностью будет простаивать, ожидая передачи. Для того чтобы минимизировать время ожидание прихода сообщения первый процесс должен отправить требуемые данные как можно раньше, отложив независящую от них работу на потом, а второй процесс должен выполнить максимум работы, не требующей ожидаемой передачи, прежде, чем выходить на точку приема сообщения. К сожалению, на работу каждой конкретной программы сказываются в той или иной мере все эти факторы одновременно, дополнительно усугубляя ситуацию с эффективностью параллельных программ. Однако в отличие от векторно-конвейерных компьютеров все изложенные здесь факторы, за исключением быть может последнего, могут снизить производительность не в десятки, а в сотни и даже тысячи раз по сравнению с пиковыми показателями производительности компьютера. Добиться на этих компьютерах, в принципе, можно многого, но усилий это может потребовать во многих случаях очень больших. Лекция 8. Кластерные системы Важным параметром является степень связности. Она убывает таким образом: SMP->MPP->Кластеры->Мета-компьютеры Вообще изначально кластеризация использовалась для обеспечения высокой готовности вычислительных систем, и сам термин появился в 1983. Сразу оговоримся, в компьютерной литературе понятие «кластер» употребляется в различных значениях, в частности, «кластерная» технология используется для повышения надежности серверов баз данных или web-серверов. Компания DEC первой анонсировала концепцию кластерной системы в 1983 году, определив ее как группу объединенных между собой вычислительных машин, представляющих собой единый узел обработки информации. По существу VAX-кластер представляет собой слабосвязанную многомашинную систему с общей внешней памятью, обеспечивающую единый механизм управления и администрирования. VAX-кластер обладает следующими свойствами: Разделение ресурсов. Компьютеры VAX в кластере могут разделять доступ к общим ленточным и дисковым накопителям. Все компьютеры VAX в кластере могут обращаться к отдельным файлам данных как к локальным. Высокая готовность. Если происходит отказ одного из VAX-компьютеров, задания его пользователей автоматически могут быть перенесены на другой компьютер кластера. Если в системе имеется несколько контроллеров HSC и один из них отказывает, другие контроллеры HSC автоматически подхватывают его работу. Высокая пропускная способность. Ряд прикладных систем могут пользоваться возможностью параллельного выполнения заданий на нескольких компьютерах кластера. Удобство обслуживания системы. Общие базы данных могут обслуживаться с единственного места. Прикладные программы могут инсталлироваться только однажды на общих дисках кластера и разделяться между всеми компьютерами кластера. Расширяемость. Увеличение вычислительной мощности кластера достигается подключением к нему дополнительных VAX-компьютеров. Дополнительные накопители на магнитных дисках и магнитных лентах становятся доступными для всех компьютеров, входящих в кластер. Работа VAX-кластера определяется двумя главными компонентами. Первым компонентом является высокоскоростной механизм связи, а вторым - системное программное обеспечение, которое обеспечивает клиентам прозрачный доступ к системному сервису. Физически связи внутри кластера реализуются с помощью трех различных шинных технологий с различными характеристиками производительности. Согласно определению, сформулированному Aberdeen Group, кластер это многомашинный комплекс, который: • выглядит с точки зрения пользователя как единая система; • обеспечивает высокую надежность (готовность); • имеет общую файловую систему; • обладает свойством роста производительности при добавлении ресурсов (масштабируемостью); • гибко перестраивается; • управляется/администрируется как единая система.     Далеко не все имеющиеся сегодня на рынке решения, которые их производители называют кластерами, удовлетворяют всем пунктам этого определения.     Сторого говоря, на 100% вышеперечисленному лпределению соответствует на сегодняшний день только кластер на основе операционной системы OpenVMS.     В отличие от систем с "горячим резервированием" (hot tandby), все компьютеры в кластере выполнют полезную работу - таким образом затраты на дополнительное оборудование являются платой не только за надежность, но и за производительность.     С другой стороны, в отличие от отказоустойчивых систем (foult-tolerant systems), систем, построенных на основе архитектуры SMP или MPP, каждый компьютер в кластере остается относительно независимым, т.е. его можно остановить и выклюсить (например, для проведения профилактических работ или установки дополнительного оборудования), не нарушая работоспособность кластера в целом. Давайте сделаем небольшое отступление, и рассмотрим, что такое высокая готовность системы. Основные определения Одной из основных проблем построения вычислительных систем во все времена остается задача обеспечения их продолжительного функционирования. Эта задача имеет три составляющих: надежность, готовность и удобство обслуживания. Все эти три составляющих предполагают, в первую очередь, борьбу с неисправностями системы, порождаемыми отказами и сбоями в ее работе. Эта борьба ведется по всем трем направлениям, которые взаимосвязаны и применяются совместно. Повышение надежности основано на принципе предотвращения неисправностей путем снижения интенсивности отказов и сбоев за счет применения электронных схем и компонентов с высокой и сверхвысокой степенью интеграции, снижения уровня помех, облегченных режимов работы схем, обеспечения тепловых режимов их работы, а также за счет совершенствования методов сборки аппаратуры. Единицей измерения надежности является среднее время наработки на отказ (MTBF - Mean Time Between Failure). Повышение готовности предполагает подавление в определенных пределах влияния отказов и сбоев на работу системы с помощью средств контроля и коррекции ошибок, а также средств автоматического восстановления вычислительного процесса после проявления неисправности, включая аппаратурную и программную избыточность, на основе которой реализуются различные варианты отказоустойчивых архитектур. Повышение готовности - есть способ борьбы за снижение времени простоя системы. Единицей измерения здесь является коэффициент готовности, который определяет вероятность пребывания системы в работоспособном состоянии в любой произвольный момент времени. Статистически коэффициент готовности определяется как MTBF/(MTBF+MTTR), где MTTR (Mean Time To Repair) - среднее время восстановления (ремонта), т.е. среднее время между моментом обнаружения неисправности и моментом возврата системы к полноценному функционированию. Таким образом, основные эксплуатационные характеристики системы существенно зависят от удобства ее обслуживания, в частности от ремонтопригодности, контролепригодности и т.д. В литературе по вычислительной технике употребляется термин "системы высокой готовности", "системы высокой степени готовности", "системы с высоким коэффициентом готовности". Все эти термины по существу являются синонимами, однако как и многие термины в области вычислительной техники, термин "высокая готовность" понимается по-разному отдельными поставщиками и потребителями вычислительных систем. Совершенно аналогично, некоторые слова, связанные с термином "высокая готовность", такие, например, как "кластеризация", также употребляются в различных значениях. Важно иметь стандартный набор определений для того, чтобы предложения различных поставщиков можно было сравнивать между собой на основе одинаковых терминов. Ниже приведены общепринятые в настоящее время определения, которые мы будем использовать для различных типов систем, свойством которых является та или иная форма снижения планового и непланового времени простоя: • Высокая Готовность (High Availability). Настоящие конструкции с высоким коэффициентом готовности для минимизации планового и непланового времени простоя используют обычную компьютерную технологию. При этом конфигурация системы обеспечивает ее быстрое восстановление после обнаружения неисправности, для чего в ряде мест используются избыточные аппаратные и программные средства. Длительность задержки, в течение которой программа, отдельный компонент или система простаивает, может находиться в диапазоне от нескольких секунд до нескольких часов, но более часто в диапазоне от 2 до 20 минут. Обычно системы высокой готовности хорошо масштабируются, предлагая пользователям большую гибкость, чем другие типы избыточности. • Эластичность к отказам (Fault Resiliency). Ряд поставщиков компьютерного оборудования делит весь диапазон систем высокой готовности на две части, при этом в верхней его части оказываются системы эластичные к отказам. Ключевым моментом в определении эластичности к отказам является более короткое время восстановления, которое позволяет системе быстро откатиться назад после обнаружения неисправности. • Устойчивость к отказам (Fault Tolerance). Отказоустойчивые системы имеют в своем составе избыточную аппаратуру для всех функциональных блоков, включая процессоры, источники питания, подсистемы ввода/вывода и подсистемы дисковой памяти. Если соответствующий функциональный блок неправильно функционирует, всегда имеется горячий резерв. В наиболее продвинутых отказоустойчивых системах избыточные аппаратные средства можно использовать для распараллеливания обычных работ. Время восстановления после обнаружения неисправности для переключения отказавших компонентов на избыточные для таких систем обычно меньше одной секунды. • Непрерывная готовность (Continuous Availability). Вершиной линии отказоустойчивых систем являются системы, обеспечивающие непрерывную готовность. Продукт с непрерывной готовностью, если он работает корректно, устраняет любое время простоя как плановое, так и неплановое. Разработка такой системы охватывает как аппаратные средства, так и программное обеспечение и позволяет проводить модернизацию (upgrade) и обслуживание в режиме on-line. Дополнительным требованием к таким системам является отсутствие деградации в случае отказа. Время восстановления после отказа не превышает одной секунды. • Устойчивость к стихийным бедствияи (Disaster Tolerance). Широкий ряд продуктов и услуг связан с обеспечением устойчивости к стихийным бедствиям. Иногда устойчивость к стихийным бедствиям рассматривается в контексте систем высокой готовности. Смысл этого термина в действительности означает возможность рестарта или продолжения операций на другой площадке, если основное месторасположение системы оказывается в нерабочем состоянии из-за наводнения, пожара или землетрясения. В простейшем случае, продукты, устойчивые к стихийным бедствиям, могут просто представлять собой резервные компьютеры, расположенные вне основного местоположения системы, сконфигурированные по спецификациям пользователя и доступные для использования в случае стихийного бедствия на основной площадке. В более сложных случаях устойчивость к стихийным бедствиям может означать полное (зеркальное) дублирование системы вне основного местоположения, позволяющее принять на себя работу немедленно после отказа системы на основной площадке. Все упомянутые типы систем высокой готовности имеют общую цель - минимизацию времени простоя. Имеется два типа времени простоя компьютера: плановое и неплановое. Минимизация каждого из них требует различной стратегии и технологии. Плановое время простоя обычно включает время, принятое руководством, для проведения работ по модернизации системы и для ее обслуживания. Неплановое время простоя является результатом отказа системы или компонента. Хотя системы высокой готовности возможно больше ассоциируются с минимизацией неплановых простоев, они оказываются также полезными для уменьшения планового времени простоя. Возможно наибольшим виновником планового времени простоя является резервное копирование данных. Некоторые конфигурации дисковых подсистем высокой готовности, особенно системы с зеркальными дисками, позволяют производить резервное копирование данных в режиме on-line. Следующим источником снижения планового времени простоя является организация работ по обновлению (модернизации) программного обеспечения. Сегодня некоторые отказоустойчивые системы и все системы с непрерывной готовностью позволяют производить модернизацию программного обеспечения в режиме on-line. В общем случае, неплановое время простоя прежде всего снижается за счет использования надежных частей, резервных магистралей или избыточного оборудования. Однако даже в этом случае система может требовать достаточно большого планового времени простоя. Специальное программное обеспечение является существенной частью систем высокой готовности. При обнаружении неисправности системы оно обеспечивает управление конфигурацией аппаратных средств и программного обеспечения, а также в случае необходимости процедурами начальной установки, и перестраивает где надо структуры данных. Высокая готовность не дается бесплатно. Общая стоимость подобных систем складывается из трех составляющих: начальной стоимости системы, издержек планирования и реализации, а также системных накладных расходов. Для реализации системы высокой готовности пользователи должны в начале закупить собственно систему (или системы), включающую один или несколько процессоров в зависимости от требуемой вычислительной мощности и предполагаемой конфигурации, дополнительное программное обеспечение и дополнительное дисковое пространство. Чтобы реализовать конфигурацию системы высокой готовности наиболее эффективным способом, особенно при использовании кластерных схем, требуется достаточно большое предварительное планирование. Например, чтобы иметь возможность переброски критичного приложения в случае отказа одного процессора на другой, пользователи должны определить, какие приложения являются наиболее критичными, проанализировать все возможные отказы и составить подробные планы восстановления на все случаи отказов. Накладные расходы систем высокой готовности связаны с необходимостью поддержки довольно сложных программных продуктов, обеспечивающих высокую готовность. Для обеспечения дублирования записей на зеркальные диски в случае отсутствия специальных, предназначенных для этих целей процессоров, требуется поддержка дополнительной внешней памяти. Стоимость системы высокой готовности в значительной степени зависит от выбранной конфигурации и ее возможностей. Ниже приведена некоторая информация, позволяющая грубо оценить различные типы избыточности. Высокая Готовность. Дополнительная стоимость систем высокой готовности меняется в пределах от 10 до 100 процентов, обычно стремясь к середине этого диапазона. Для того, чтобы снизить стоимость системы, следует тщательно оценивать действительно необходимый уровень готовности (т.е. осуществлять выбор между высокой готовностью, устойчивостью к отказам и/или устойчивостью к стихийным бедствиям) и вкладывать деньги только за обеспечение безопасности наиболее критичных для деятельности компании приложений и данных. Для чего нужны кластеры? Классические суперкомпьютеры всегда ассоциировались с чем-то большим: размеры, гигантская производительность, немереная память и, конечно же, колоссальная стоимость [1]. Первым серьезным прорывом в уменьшении отношения цена/производительность стало появление компьютеров с массовым параллелизмом. Использование серийных микропроцессоров позволило не только гибко менять мощность установки в зависимости от потребностей и возможностей, но и значительно удешевить производство. Примерами суперкомпьютеров этого класса могут служить Intel Paragon, IBM SP, Сray T3D/T3E и ряд других. Единственным способом взаимодействия процессоров в рамках подобных систем было их общение через некоторую коммуникационную среду, объединяющую процессоры в единую вычислительную установку. Например, в компьютерах семейства Сray T3D/T3E все процессоры объединены специальными высокоскоростными каналами в трехмерный тор, в котором каждый вычислительный узел имеет шесть непосредственных соседей. В компьютере IBM SP/2 взаимодействие процессоров идет через иерархическую систему переключателей, потенциально обеспечивающей непосредственное соединение каждого процессора с любым другим. Однако все уникальные решения, да еще и с рекордными характеристиками обычно недешевы, поэтому и стоимость подобных систем никак не могла быть сравнима со стоимостью систем, находящихся в массовом производстве. Шло время, и прогресс в области сетевых технологий сделал свое дело: на рынке появились недорогие, но эффективные коммуникационные решения. Это и предопределило появление кластерных вычислительных систем, фактически являющихся одним из направлений развития компьютеров с массовым параллелизмом (MPP — massive parallel processing). Если говорить кратко, то вычислительный кластер — это совокупность компьютеров, объединенных в рамках некоторой сети для решения одной задачи В качестве вычислительных узлов обычно используются доступные на рынке однопроцессорные компьютеры, двух- или четырехпроцессорные SMP-серверы. Каждый узел работает под управлением своей копии операционной системы, в качестве которой чаще всего используются стандартные операционные системы: Linux, NT, Solaris и т.п. Состав и мощность узлов может меняться даже в рамках одного кластера, давая возможность создавать неоднородные системы. Выбор конкретной коммуникационной среды определяется многими факторами: особенностями класса решаемых задач, доступным финансированием, необходимостью последующего расширения кластера и т.п. Возможно включение в конфигурацию специализированных компьютеров, например, файл-сервера, и, как правило, предоставлена возможность удаленного доступа на кластер через Internet. Ясно, что простор для творчества при проектировании кластеров огромен. Рассматривая крайние точки, кластером можно считать как пару ПК, связанных локальной 10-мегабитной сетью Ethernet, так и вычислительную систему, создаваемую в рамках проекта Cplant в Национальной лаборатории Sandia: 1400 рабочих станций на базе процессоров Alpha, связанных высокоскоростной сетью Myrinet. Чтобы немного почувствовать масштаб и технологию существующих систем, сделаем краткий обзор наиболее интересных современных кластерных установок. Следует иметь в виду, что производительность персональных компьютеров на базе процессоров Intel в последние годы также значительно выросла. Такие компьютеры стали создавать серьезную конкуренцию рабочим станциям на базе RISC, особенно по показателю цена/производительность. Одновременно стала приобретать все большую популярность ОС Linux - бесплатно распространяемая версия UNIX. При этом в научных организациях и университетах, как правило, имеются энтузиасты бесплатного распространяемого ПО и специалисты ("гуру") по ОС Linux. Возникла идея создавать параллельные вычислительные системы (кластеры) из общедоступных компьютеров на базе Intel и недорогих Ethernet-сетей, устанавливая на эти компьютеры Linux и одну из бесплатно распространяемых коммуникационных библиотек (PVM, а затем MPI). Оказалось, что на многих классах задач и при достаточном числе узлов такие системы дают производительность, сравнимую с суперкомпьютерной. История проекта Beowulf Проект возник в научно-космическом центре NASA - Goddard Space Flight Center (GSFC), точнее в созданном на его основе CESDIS (Center of Excellence in Space Data and Information Sciences). Проект Beowulf начался летом 1994 года сборкой в GSFC 16-процессорного кластера (на процессорах 486DX4/100MHz, 16MB памяти и 3 сетевых адаптера на каждом узле, 3 "параллельных" Ethernet-кабеля по 10Mbit). Данный кластер, который и был назван "Beowulf", создавался как вычислительный ресурс проекта Earth and Space Sciences Project (ESS). Есть картинка с изображением этого первого Beowulf-а. Далее в GSFC и других подразделениях NASA были собраны другие, более мощные кластеры. Например, кластер theHIVE (Highly-parallel Integrated Virtual Environment) содержит 64 узла по 2 процессора Pentium Pro/200MHz и 4GB памяти в каждом, 5 коммутаторов Fast Ethernet. Общая стоимость этого кластера составляет примерно $210 тыс. Доступна информация о производительности theHIVE на различных приложениях и тестах. old Этот кластер состоит из четырех подкластеров E, B, G, и DL, объединяя 332 процессора и два выделенных хост-узла. Все узлы данного кластера работают под управлением RedHat Linux. В рамках проекта Beowulf был разработан ряд высокопроизводительных и специализированных сетевых драйверов (в частности, драйвер для использования нескольких Ethernet-каналов одновременно). Официальная страница проекта Beowulf - www.beowulf.org. Откуда возник термин "Beowulf"? Изначательно термин "Beowulf" возник как собственное имя Linux-кластера в GSFC. Затем он стал применяться ко всем аналогичным кластерным системам (Beowulf-кластер, кластер "а-ля" Beowulf). Avalon, суперкомпьютер на базе Linux В 1998 году в Лос-аламосской национальной лаборатории астрофизик Michael Warren и другие ученые из группы теоретической астрофизики построили суперкомпьютер Avalon, который представляет из себя Linux-кластер на базе процессоров DEC Alpha/533MHz. Avalon первоначально состоял из 68 процессоров, затем был расширен до 140. В каждом узле установлено 256MB оперативной памяти, EIDE-жесткий диск на 3.2GB, сетевой адаптер от Kingston (общая стоимость узла - $1700). Узлы соединены с помощью 4-х 36-портовых коммутаторов Fast Ethernet и расположенного "в центре" 12-портового коммутатора Gigabit Ethernet от 3Com. Общая стоимость Avalon - $313 тыс., а его производительность по LINPACK (47.7 GFLOPS) позволила ему занять 114 место в 12-й редакции списка Top500 (рядом с 152-процессорной системой IBM SP2). 70-процессорная конфигурация Avalon по многим тестам показала такую же производительность, как 64-процессорная система SGI Origin2000/195MHz стоимость которой превышает $1 млн. В настоящее время Avalon активно используется в астрофизических, молекулярных и других научных вычислениях. На конференции SC'98 создатели Avalon представили доклад, озаглавленный "Avalon: An Alpha/Linux Cluster Achieves 10 Gflops for $150k" и заслужили премию по показателю цена/производительность ("1998 Gordon Bell Price/Performance Prize"). Mike Warren на фоне своего детища Как построить Beowulf? (Некоторые практические рекомендации по построению параллельных кластеров) Кластер состоит из отдельных машин (узлов) и объединяющей их сети (коммутатора). Кроме ОС, необходимо установить и настроить сетевые драйверы, компиляторы, ПО поддержки параллельного программирования и распределения вычислительной нагрузки. 1. Узлы кластера. Подходящим выбором в данный момент являются системы на базе процессоров Intel: Pentium II или Pentium II Xeon - или однопроцессорные ПК, или SMP-сервера с небольшим числом процессоров (2-4, возможно до 6). По некоторым причинам оптимальным считается построение кластеров на базе двухпроцессорных систем, несмотря на то, что в этом случае настройка кластера будет несколько сложнее (главным образом потому, что доcтупны относительно недорогие материнские платы для 2 процессоров Pentium II). Стоит установить на каждый узел 64-128MB оперативной памяти (для двухпроцессорных систем 64-256MB). Одну из машин следует выделить в качестве центральной (головной) куда следует установить достаточно большой жесткий диск, возможно более мощный процессор и больше памяти, чем на остальные (рабочие) узлы. Имеет смысл обеспечить (защищенную) связь этой машины с внешним миром. При комплектации рабочих узлов вполне возможно отказаться от жестких дисков - эти узлы будут загружать ОС через сеть с центральной машины, что, кроме экономии средств, позволяет сконфигурировать ОС и все необходимое ПО только 1 раз (на центральной машине). Если эти узлы не будут одновременно использоваться в качестве пользовательских рабочих мест, нет необходимости устанавливать на них видеокарты и мониторы. Возможна установка узлов в стойки (rackmounting), что позволит уменьшить место, занимаемое узлами, но будет стоить несколько дороже. Возможна организация кластеров на базе уже существующих сетей рабочих станций, т.е. рабочие станции пользователей могут использоваться в качестве узлов кластера ночью и в выходные дни. Системы такого типа иногда называют COW (Cluster of Workstations). Количество узлов следует выбирать исходя из необходимых вычислительных ресурсов и доступных финансовых средств. Следует понимать, что при большом числе узлов придется также устанавливать более сложное и дорогое сетевое оборудование. 2. Сеть. В простейшем случае используется один сегмент Ethernet (10Mbit/sec на витой паре). Однако дешевизна такой сети, вследствие коллизий оборачивается большими накладными расходами на межпроцессорные обмены; а хорошую производительность такого кластера следует ожидать только на задачах с очень простой параллельной структурой и при очень редких взаимодействиях между процессами (например, перебор вариантов). Для получения хорошей производительности межпроцессорных обменов используют полнодуплексный Fast Ethernet на 100Mbit/sec. При этом для уменьшения числа коллизий или устанавливают несколько "параллельных" сегментов Ethernet, или соединяют узлы кластера через коммутатор (switch). Более дорогостоящим, но также популярным вариантом являются использование коммутаторов типа Myrinet (1.28Gbit/sec, полный дуплекс). Менее популярными, но также реально используемыми при построении кластеров сетевыми технологиями являются технологии сLAN, SCI и Gigabit Ethernet. Однако большие накладные расходы на передачу сообщений в рамках Fast Ethernet приводят к серьезным ограничениям на спектр задач, которые можно эффективно решать на таком кластере. Если от кластера требуется большая универсальность, то нужно переходить на другие, более производительные коммуникационные технологии. Рис. 3. Латентность и пропускная способность канала Какими же числовыми характеристиками выражается производительность коммуникационных сетей в кластерных системах? Основных характеристик две: латентность – время начальной задержки при посылке сообщений и пропускная способность сети, определяющая скорость передачи информации по каналам связи (рис. 3). При этом важны не столько пиковые характеристики, заявляемые производителем, сколько реальные, достигаемые на уровне пользовательских приложений, например, на уровне MPI-приложений. В частности, после вызова пользователем функции посылки сообщения Send() сообщение последовательно пройдет через целый набор слоев, определяемых особенностями организации программного обеспечения и аппаратуры, прежде, чем покинуть процессор – отсюда и вариации на тему латентности. Кстати, наличие латентности определяет и тот факт, что максимальная скорость передачи по сети не может быть достигнута на сообщениях с небольшой длиной. Сравнительные характеристики коммуникационных технологий SCI Myrinet cLAN ServerNet Fast Ethernet Латентность (MPI) 5,6 мкс 17 мкс 30 мкс 13 мкс 170 мкс Пропускная способность(MPI) 80 Мбайт/c 40 Мбайт/c 100 Мбайт/c 180 Мбайт/c 10 Мбайт/c Пропускная способность (аппаратная) 400 Мбайт/c 160 Мбайт/c 150 Мбайт/c н/д 12,5 Мбайт/c Реализация MPI ScaMPI HPVM, MPICH-GM и др. MPI/Pro MVICH MPICH Производитель Dolphin Myricom Giganet Compaq Intel, 3Com и др. Лекция 9. Особенности ОС мультипроцессорных систем ОС - Системное ПО, осуществляющее управление ресурсами системы. А именно: 1) Процессами 2) Памятью 3) Устройствами ОС для многопроцессорной ЭВМ выполняет те же задачи. Ее функции усложняются из за необходимости обеспечивать разделение ресурсов между множеством процессов и поддержкой многопроцессорности. Очевидно, что при программировании для любой ЭВМ важно знать, как устроена его ОС. А именно, в случае ОС многопроцессорных систем – как осуществляется управление процессами, разделение времени, синхронизация; разделение ресурсов, осуществление к ним параллельного доступа. Можно 3 вида ОС многопроцессорных систем: 1) Сетевые 2) Распределенные 3) ОС мультипроцессорных систем При этом ОС мультипроцессорных (SMP) систем обычно очень похожи на ОС однопроцессорных машин, в идеале неотличимы. Пример – Windows NT, которая может выполняться как на однопроцессорных машинах, так и на многопроцессорных. При этом используются одни и те же программы, меняется только то, что разные процессы начинают выполняться на разных физических процессорах, но для программиста это незаметно. В ОС распределенных системах (MPP) это принципиально невозможно, так как отсутствует общая память, так что приемы программирования должны быть принципиально другими, и здесь уже (обычно) не получается автоматически распараллеливать программы. Для взаимодействия между процессорными блоками системы используются сообщения. И, наконец, сетевые ОС предназначены для предоставления доступа к ресурсам независимых компьютеров в пределах одной сети. Понятие процесса – выполнение программы. Компоненты процесса – выполняющаяся программа, ее данные, ее ресурсы (память), состояние. При переключении процессов требуется выгрузить всю сопутствующую информацию текущего процесса и загрузить информацию нового. Это долго. Как следствие, возникают так называемые «легкие» процессы – «нити», которые разделяют ресурсы (память), и выполняются в рамках одного процесса. В случае наличия нескольких физических процессоров легковесных процессы естественно могут выполняться на разных процессорах. Собственно, так обычно и организован параллелизм в SMP системах. История многозадачности на однопроцессорных ЭВМ – не вытесняющая (кооперативная) и вытесняющая многозадачность. Многопоточность. В таких ОС синхронизация процессов обычно осуществляется с помощью использования разделяемой памяти и основывается на атомарности операций чтения/записи в память. Различают 2 вида синхронизации: 1) с помощью критических секций; 2) координацией процессов. Основная задача, выполняемая критическими секциями – исключение так называемых «гонок» в исполняемом коде, когда несколько процессов могут одновременно модифицировать и/или обращаться к какому либо ресурсу. С помощью КС мы можем определить интервалы кода, которые делаются доступны только одному процессу в каждый отдельный момент времени. Если в однопроцессорной ЭВМ для исключения КС достаточно запретить прерывания и переключение задач, то в многопроцессорных ЭВМ для этого используются специальные алгоритмы. Все они должны удовлетворять требованиям: • в любой момент времени только один процесс может находиться внутри критического интервала; • если ни один процесс не находится в критическом интервале, то любой процесс, желающий войти в критический интервал, должен получить разрешение без какой либо задержки; • ни один процесс не должен бесконечно долго ждать разрешения на вход в критический интервал (если ни один процесс не будет находиться внутри критического интервала бесконечно); Семафоры. Предложены Дейкстрой. Основываются на неделимых операциях установки и снятия семафора. Семафоры работают для любого числа процессов. Семафор - неотрицательная целая переменная, которая может изменяться и проверяться только посредством двух функций: 1) P - функция запроса семафора P(s): [if (s == 0) <заблокировать текущий процесс>; else s = s-1;] 2) V - функция освобождения семафора V(s): [if (s == 0) <разблокировать один из заблокированных процессов>; s = s+1;]. Реализация семафоров в мультипрограммном режиме для: • блокировки внешних прерываний; • запрета переключения на другие процессы; • переменной и очереди ожидающих процессов в ОС. Для многопроцессорной ЭВМ первые два способа не годятся. Для реализации третьего способа достаточно команды TSL и возможности объявлять прерывание указанному процессору. Блокирование процесса и переключение на другой - не эффективно, если семафор захватывается на очень короткое время. Ожидание освобождения таких семафоров может быть реализовано в ОС посредством циклического опроса значения семафора. Недостатки такого "активного ожидания" - бесполезная трата времени, нагрузка на общую память, и возможность фактически заблокировать работу процесса, находящегося в критическом интервале. Если произведенный объект используется многими, то семафоры не годятся. Планирование процессоров очень сильно влияет на производительность мультипроцессорной системы. Можно выделить следующие главные причины деградации производительности: 1. Накладные расходы на переключение процессора. Они определяются не только переключениями контекстов процессов, но и (при переключении на процессы другого приложения) перемещениями страниц виртуальной памяти, а также порчей кэша (информация в кэше другому приложению не нужна и будет заменена). 2. Переключение на другой процесс в тот момент, когда текущий процесс выполнял критическую секцию, а другие процессы активно ожидают входа в критическую секцию. В этом случае потери будут велики (хотя вероятность прерывания выполнения коротких критических секций мала). Применяются следующие стратегии борьбы с деградацией производительности. 1. Совместное планирование, при котором все процессы одного приложения (неблокированные) одновременно выбираются на процессоры и одновременно снимаются с них (для сокращения переключений контекста). 2. Планирование, при котором находящиеся в критической секции процессы не прерываются, а активно ожидающие входа в критическую секцию процессы не выбираются до тех пор, пока вход в секцию не освободится. 3. Процессы планируются на те процессоры, на которых они выполнялись в момент их снятия (для борьбы с порчей кэша). При этом может нарушаться балансировка загрузки процессоров. 9.1 Коммутация в распределенных системах. Все компьютеры в распределенной системе связаны между собой коммуникационной сетью. Коммуникационные сети подразделяются на широкомасштабные (Wide Area Networks, WANs) и локальные (Local Area Networks, LANs). Широкомасштабные сети WAN состоит из коммуникационных ЭВМ, связанных между линии, собой коммуникационными линиями (телефонные радиолинии, спутниковые каналы, оптоволокно) и обеспечивающих транспортировку сообщений. Обычно используется техника store-and-forward, когда следующий с ссобщения передаются из одного компьютера в промежуточной буферизацией. Коммутация пакетов или коммутация линий. Коммутация линий (телефонные разговоры) требует резервирования линий на время всего сеанса общения двух устройств. Пакетная коммутация основана на разбиении сообщений в пункте отправления на порции (пакеты), посылке пакетов по адресу назначения, и сборке сообщения из пакетов в пункте назначения. При этом линии используются эффективнее, сообщения могут передаваться быстрее, но требуется работа по разбиению и сборке сообщений, а также возможны задержки (для передачи речи или музыки такой метод не годится). MPP с распределенной памятью может рассматриваться как частный случай локальной сети. 1. Физические среды для передачи сигнала. 2. Основные стандарты. 3. Иерархия OSI (open system interconnection). Прикладной уровень – набор протоколов прикладного уровня Представительный уровень - изм. Форму преставления информации, не изменяя ее содерж Сеансовый уровень - организация сеансов, синхронизация Транспортный уровень – надежность передачи сообщений, качество сервиса Сетевой уровень – объединение нескольких сетей, маршрутизация, глобальная адресация Канальный уровень – биты в кадры, ошибки Физический уровень – физические характеристикии сети 9.2 Синхронизация в распределенных системах. Синхронизация времени, абсолютная невозможна, но достаточно определить для всех событий отношение предшествия. Серверы времени, атомные часы. Основное – часы не должны ходить назад, так что надо их замедлять или ускорять для проведения коррекции. Выбор координатора: Многие распределенные алгоритмы требуют, чтобы один из процессов выполнял функции координатора, инициатора или некоторую другую специальную роль. Выбор такого специального процесса будем называть выбором координатора. При этом очень часто бывает не важно, какой именно процесс будет выбран. Можно считать, что обычно выбирается процесс с самым большим уникальным номером. Могут применяться разные алгоритмы, имеющие одну цель - если процедура выборов началась, то она должна закончиться согласием всех процессов относительно нового координатора. Централизованный алгоритм Все процессы запрашивают у координатора разрешение на вход в критическую секцию и ждут этого разрешения. Координатор обслуживает запросы в порядке поступления. Получив разрешение процесс входит в критическую секцию. При выходе из нее он сообщает об этом координатору. Количество сообщений на одно прохождение критической секции - 3. Недостатки алгоритма - обычные недостатки централизованного алгоритма (крах координатора или его перегрузка сообщениями). Алгоритм с круговым маркером Все процессы составляют логическое кольцо, когда каждый знает, кто следует за ним. По кольцу циркулирует маркер, дающий право на вход в критическую секцию. Получив маркер (посредством сообщения точка-точка) процесс либо входит в критическую секцию (если он ждал разрешения) либо переправляет маркер дальше. После выхода из критической секции маркер переправляется дальше, повторный вход в секцию при том же маркере не разрешается. Проблемы: • Если маркер потеряется, то его надо регенерировать. Обнаружить потерю тяжело (время прихода неизвестно). • Если какой-то процесс перестанет функционировать, то алгоритм не работает. Однако восстановление проще, чем в других случаях. Наличие квитанций позволит обнаружить такой процесс в момент передачи маркера (если поломка произошла вне критического интервала). Переставший функционировать процесс должен быть исключен из логического кольца, для этого придется каждому знать текущую конфигурацию кольца. 9.3 Распределенная общая память (DSM - Distributed Shared Memory) Традиционно распределенные вычисления базируются на модели передачи сообщений, в которой данные передаются от процессора к процессору в виде сообщений. Удаленный вызов процедур фактически является той же самой моделью (или очень близкой). DSM - виртуальное адресное пространство, разделяемое всеми узлами (процессорами) распределенной системы. Программы получают доступ к данным в DSM примерно так же, как они работают с данными в виртуальной памяти традиционных ЭВМ. В системах с DSM данные перемещаются между локальными памятями разных компьютеров аналогично тому, как они перемещаются между оперативной и внешней памятью одного компьютера. Достоинства DSM 1. В модели передачи сообщений программист обеспечивает доступ к разделяемым данным посредством явных операций посылки и приема сообщений. При этом приходится квантовать алгоритм, обеспечивать своевременную смену информации в буферах, преобразовывать индексы массивов. Все это сильно усложняет программирование и отладку. DSM скрывает от программиста пересылку данных и обеспечивает ему абстракцию разделяемой памяти, к использованию которой он уже привык на мультипроцессорах. Программирование и отладка с использованием DSM гораздо проще. 2. В модели передачи сообщений данные перемещаются между двумя различными адресными пространствами. Это делает очень трудным передачу сложных структур данных между процессами. Более того, передача данных по ссылке и передача структур данных, содержащих указатели, является в общем случае делом сложным и дорогостоящим. DSM же позволяет передавать данные по ссылке, что упрощает разработку распределенных приложений. 3. Программы, написанные для мультипроцессоров с общей памятью, могут в принципе без каких-либо изменений выполняться на DSM-системах (по крайней мере, они могут быть легко перенесены на DSM-системы).По существу, DSM-системы преодолевают архитектурные ограничения мультипроцессоров и сокращают усилия, необходимые для написания программ для распределенных систем. Обычно они реализуются программно-аппаратными средствами, но в последние годы появилось несколько коммерческих MPP с DSM, реализованной аппаратно (Convex SPP, KSR1). Алгоритмы реализации DSM При реализации DSM центральными являются следующие вопросы. 1. как поддерживать информацию о расположении удаленных данных. 2. как снизить при доступе к удаленным данным коммуникационные задержки и большие накладные расходы, связанные с выполнением коммуникационных протоколов. 3. как сделать разделяемые данные доступными одновременно на нескольких узлах для того, чтобы повысить производительность системы. Рассмотрим четыре основных алгоритма реализации DSM. Алгоритм с центральным сервером Все разделяемые данные поддерживает центральный сервер. Он возвращает данные клиентам по их запросам на чтение, по запросам на запись он корректирует данные и посылает клиентам в ответ квитанции. Клиенты могут использовать тайм-аут для посылки повторных запросов при отсутствии ответа сервера. Дубликаты запросов на запись могут распознаваться путем нумерации запросов. Если несколько повторных обращений к серверу остались без ответа, приложение получит отрицательный код ответа (это обеспечит клиент). Алгоритм прост в реализации, но сервер может стать узким местом. Чтобы избежать этого, разделяемые данные могут быть распределены между несколькими серверами. В этом случае клиент должен уметь определять, к какому серверу надо обращаться при каждом доступе к разделяемой переменной. Посылка запросов сразу всем серверам нежелательна, поскольку не снижает нагрузку на серверы. Лучшее решение - распределить данные в зависимости от их адресов и использовать функцию отображения для определения нужного сервера.  Миграционный алгоритм В отличие от предыдущего алгоритма, когда запрос к данным направлялся в место их расположения, в этом алгоритме меняется расположение данных - они перемещаются в то место, где потребовались. Это позволяет последовательные обращения к данным осуществлять локально. Миграционный алгоритм позволяет обращаться к одному элементу данных в любой момент времени только одному узлу. Обычно мигрирует целиком страницы или блоки данных, а не запрашиваемые единицы данных. Это позволяет воспользоваться присущей приложениям локальностью доступа к данным для снижения стоимости миграции. Однако, такой подход приводит к трэшингу, когда страницы очень часто мигрируют между узлами при малом количестве обслуживаемых запросов. Некоторые системы позволяют задать время, в течение которого страница насильно удерживается в узле для того, чтобы успеть выполнить несколько обращений к ней до миграции ее в другой узел. Для определения места расположения блоков данных миграционный алгоритм может использовать сервер, отслеживающий перемещения блоков, либо воспользоваться механизмом подсказок в каждом узле. Возможна и широковещательная рассылка запросов. Алгоритм размножения для чтения Предыдущий алгоритм позволял обращаться к разделяемым данным в любой момент времени только процессам в одном узле (в котором эти данные находятся). Данный алгоритм расширяет миграционный алгоритм механизмом размножения блоков данных, позволяя либо многим узлам иметь возможность одновременного доступа по чтению, либо одному узлу иметь возможность читать и писать данные (протокол многих читателей и одного писателя). Производительность повышается за счет возможности одновременного доступа по чтению, но запись требует серьезных затрат для уничтожения всех устаревших копий блока данных или их коррекции. При использовании такого алгоритма требуется отслеживать расположение всех блоков данных и их копий. Например, каждый собственник блока может отслеживать расположение его копий. Данный алгоритм может снизить среднюю стоимость доступа по чтению тогда, когда количество чтений значительно превышает количество записей. Алгоритм полного размножения Этот алгоритм является расширением предыдущего алгоритма. Он позволяет многим узлам иметь одновременный доступ к разделяемым данным на чтение и запись (протокол многих читателей и многих писателей). Поскольку много узлов могут писать данные параллельно, требуется для поддержания согласованности данных контролировать доступ к ним. Одним из способов обеспечения консистентности данных является использование специального процесса для упорядочивания модификаций памяти. Все узлы, желающие модифицировать разделяемые данные должны посылать свои модификации этому процессу. Он будет присваивать каждой модификации очередной номер и рассылать его широковещательно вместе с модификацией всем узлам, имеющим копию модифицируемого блока данных. Каждый узел будет осуществлять модификации в порядке возрастания их номеров. Разрыв в номерах полученных модификаций будет означать потерю одной или нескольких модификаций. В этом случае узел может запросить недостающие модификации. Все перечисленные алгоритмы являются неэффективными. Добиться эффективности можно только изменив семантику обращений к памяти.   9.4 Модели консистентности Модель консистентности представляет собой некоторый договор между программами и памятью, в котором указывается, что при соблюдении программами определенных правил работа модуля памяти будет корректной, если же требования к программе будут нарушены, то память не гарантирует правильность выполнения операций чтения/записи. В этой главе рассматриваются основные модели консистентности используемые в системах с распределенной памятью.  Строгая консистентность Модель консистентности, удовлетворяющая условию: Операция чтения ячейки памяти с адресом X должна возвращать значение, записанное самой последней операцией записи с адресом X, называется моделью строгой консистентности. Указанное выше условие кажется довольно естественным и очевидным, однако оно предполагает наличие в системе понятия абсолютного времени для определения наиболее последней операции записи. Последовательная консистентность Строгая консистентность представляет собой идеальную модель для программирования, но ее, к сожалению программистов, невозможно реализовать для распределенных систем. Однако, практический опыт показывает, что в некоторых случаях можно обходиться и более слабыми моделями. Все эти методы опираются на то, что должна соблюдаться последовательность определенных событий записи и чтения. Последовательную консистентность впервые определил Lamport в 1979 г. По его определению, модель последовательной консистентности памяти должна удовлетворять следующему условию: Результат выполнения должен быть тот-же, как если бы операторы всех процессоров выполнялись бы в некоторой последовательности, в которой операторы каждого индивидуального процессора расположены в порядке, определяемом программой этого процессора. Это определение означает, что при параллельном выполнении, все процессы должны видеть одну и ту же последовательность записей в память. Последовательная консистентность не гарантирует, что операция чтения возвратит значение, записанное другим процессом наносекундой или даже минутой раньше, в этой модели только точно гарантируется, что все процессы знают последовательность всех записей в память. Причинная консистентность Причинная модель консистентности памяти представляет собой более слабую модель по сравнению с последовательной моделью, поскольку в ней не всегда требуется, чтобы все процессы видели одну и ту же последовательность записей в память, а проводится различие между потенциально зависимыми операциями записи, и Рассмотрим пример. Предположим, что процесс P1 модифицировал переменную x, затем процесс P2 прочитал x и модифицировал y. В этом случае модификация x и модификация y потенциально причинно зависимы, так как новое значение y могло зависеть от прочитанного значения переменной x. С другой стороны, если два процесса одновременно изменяют значения различных переменных, то между этими событиями нет причинной связи. Операции, которые причинно не зависят друг от друга называются параллельными. Причинная модель консистентности памяти определяется следующим условием: Последовательность операций записи, которые потенциально причинно зависимы, должна наблюдаться всеми процессами  Слабая консистентность Предложенная в 1986 г. (Dubois et al.) модель слабой консистентности, основана на выделении среди переменных специальных синхронизационных переменных и описывается следующими правилами: 1. Доступ к синхронизационным переменным определяется моделью последовательной консистентности; 2. Доступ к синхронизационным переменным запрещен (задерживается), пока не выполнены все предыдущие операции записи; 3. Доступ к данным (запись, чтение) запрещен, пока не выполнены все предыдущие обращения к синхронизационным переменным. Первое правило определяет, что все процессы видят обращения к синхронизационным переменным в определенном (одном и том же) порядке. Второе правило гарантирует, что выполнение процессором операции обращения к синхронизационной переменной возможно только после выталкивания конвейера (полного завершения выполнения на всех процессорах всех предыдущих операций записи переменных, выданных данным процессором). Третье правило определяет, что при обращении к обычным (не синхронизационным) переменным на чтение или запись, все предыдущие обращения к синхронизационным переменным должны быть выполнены полностью. Выполнив синхронизацию перед обращением к общей переменной, процесс может быть уверен, что получит правильное значение этой переменной. В отличие от предыдущих моделей консистентности, модель слабой консистентности ориентирована на консистентность групповых операций чтения/записи. Эта модель наиболее эффективна для приложений, в которых отдельные (изолированные) обращения к общим переменным встречаются довольно редко.   9.5 Обеспечение надежности в распределенных системах Отказом системы называется поведение системы, не удовлетворяющее ее спецификациям. Последствия отказа могут быть различными. Отказ системы может быть вызван отказом (неверным срабатыванием) каких-то ее компонентов (процессор, память, устройства ввода/вывода, линии связи, или программное обеспечение). Отказ компонента может быть вызван ошибками при конструировании, при производстве или программировании. Он может быть также вызван физическим повреждением, изнашиванием оборудования, некорректными входными данными, ошибками оператора, и многими другими причинами. Отказы могут быть случайными, периодическими или постоянными. Случайные отказы (сбои) при повторении операции исчезают. Причиной такого сбоя может служить, например, электромагнитная помеха от проезжающего мимо трамвая. Другой пример - редкая ситуация в последовательности обращений к операционной системе от разных задач. Периодические отказы повторяются часто в течение какого-то времени, а затем могут долго не происходить. Примеры - плохой контакт, некорректная работа ОС после обработки аварийного завершения задачи. Постоянные (устойчивые) отказы не прекращаются до устранения их причины - разрушения диска, выхода из строя микросхемы или ошибки в программе. Восстановление после отказа. Восстановление может быть прямым (без возврата к прошлому состоянию) и возвратное. Прямое восстановление основано на своевременном обнаружении сбоя и ликвидации его последствий путем приведения некорректного состояния системы в корректное. Такое восстановление возможно только для определенного набора заранее предусмотренных сбоев. При возвратном восстановлении происходит возврат процесса (или системы) из некорректного состояния в некоторое из предшествующих корректных состояний. При этом возникают следующие проблемы. (1) Потери производительности, вызванные запоминанием состояний, восстановлением запомненного состояния и повторением ранее выполненной работы, могут быть слишком высоки. (2) Нет гарантии, что сбой снова не повторится после восстановления. (3) Для некоторых компонентов системы восстановление в предшествующее состояние может быть невозможно (торговый автомат). Тем не менее этот подход является более универсальным и применяется гораздо чаще первого. Дальнейшее рассмотрение будет ограничено только данным подходом. Для восстановления состояния в традиционных ЭВМ применяются два метода (и их комбинация), основанные на промежуточной фиксации состояния либо ведении журнала выполняемых операций. Они различаются объемом запоминаемой информацией и временем, требуемым для восстановления. Применение подобных методов в распределенных системах наталкивается на следующие трудности. Сообщения-сироты и эффект домино. На рисунке показаны три процесса (X,Y,Z), взаимодействующие через сообщения. Вертикальные черточки показывают на временной оси моменты запоминания состояния процесса для восстановления в случае отказа. Стрелочки соответствуют сообщениям и показывают моменты их отправления и получения. Если процесс X сломается, то он может быть восстановлен с состояния x3 без какого-либо воздействия на другие процессы. Предположим, что процесс Y сломался после посылки сообщения m и был возвращен в состояние y2. В этом случае получение сообщения m зафиксировано в x3, а его посылка не отмечена в y2. Такая ситуация, возникшая из-за несогласованности глобального состояния, не должна допускаться (пример - сообщение содержит сумму, переводимую с одного счета на другой). Сообщение m в таком случае называется сообщением-сиротой. Процесс X должен быть возвращен в предыдущее состояние x2 и конфликт будет ликвидирован. Предположим теперь, что процесс Z сломается и будет восстановлен в состояние z2. Это приведет к откату процесса Y в y1, а затем и процессов X и Z в начальные состояния x1 и y1. Этот эффект известен как эффект домино. Потеря сообщений. Предположим, что контрольные точки x1 и y1 зафиксированы для восстановления процессов X и Y, соответственно. Если процесс Y сломается после получения сообщения m, и оба процесса будут восстановлены (x1,y1), то сообщение m будет потеряно (его потеря будет неотличима от потери в канале). Проблема бесконечного восстановления. Процесс Y сломался до получения сообщения n1 от X. Когда Y вернулся в состояние y1, в нем не оказалось записи о посылке сообщения m1. Поэтому X должен вернуться в состояние x1. После отката Y посылает m2 и принимает n1 (сообщение-призрак). Процесс X после отката к x1 посылает n2 и принимает m2. Однако X после отката уже не имеет записи о посылке n1. Поэтому Y должен повторно откатиться к y1. Теперь X должен откатиться к x1, поскольку он принял m2, о посылке которого в Y нет записи. Эта ситуация будет повторяться бесконечно. Консистентное множество контрольных точек. Описанные выше трудности показывают, что глобальная контрольная точка, состоящая из произвольной совокупности локальных контрольных точек, не обеспечивает восстановления взаимодействующих процессов. Для распределенных систем запоминание согласованного глобального состояния является серьезной теоретической проблемой. Множество контрольных точек называется строго консистентным, если во время его фиксации никаких обменов между процессами не было. Оно соответствует понятию строго консистентного глобального состояния, когда все посланные сообщения получены и нет никаких сообщений в каналах связи. Множество контрольных точек называется консистентным, если для любой зафиксированной операции приема сообщения, соответствующая операция посылки также зафиксирована (нет сообщений-сирот). Простой метод фиксации консистентного множества контрольных точек - фиксация локальной контрольной точки после каждой операции посылки сообщения. При этом посылка сообщения и фиксация должны быть единой неделимой операцией (транзакцией). Множество последних локальных контрольных точек является консистентным (но не строго консистентным). Чтобы избежать потерь сообщений при восстановлении с использованием консистентного множества контрольных точек необходимо повторить отправку тех сообщений, квитанции о получении которых стали недействительными в результате отката. Используя временные метки сообщений можно распознавать сообщения-призраки и избежать бесконечного восстановления. Синхронная фиксация контрольных точек и восстановление Ниже описываются алгоритмы создания консистентного множества контрольных точек и использования их для восстановления без опасности бесконечного зацикливания. Алгоритм создания консистентного множества контрольных точек. К распределенной системе алгоритм предъявляет следующие требования. (1) Процессы взаимодействуют посредством посылки сообщений через коммуникационные каналы. (2) Каналы работают по алгоритму FIFO. Коммуникационные протоколы точка-точка гарантируют невозможность пропажи сообщений из-за ошибок коммуникаций или отката к контрольной точке. (Другой способ обеспечения этого - использование стабильной памяти для журнала посылаемых сообщений и фиксации идентификатора последнего полученного по каналу сообщения). Алгоритм создает в стабильной памяти два вида контрольных точек - постоянные и пробные. Постоянная контрольная точка - это локальная контрольная точка, являющаяся частью консистентной глобальной контрольной точки. Пробная контрольная точка - это временная контрольная точка, которая становится постоянной только в случае успешного завершения алгоритма. Алгоритм исходит из того, что только один процесс инициирует создание множества контрольных точек, а также из того, что никто из участников не сломается во время работы алгоритма. Алгоритм выполняется в две фазы. 1-ая фаза. Инициатор фиксации (процесс Pi) создает пробную контрольную точку и просит все остальные процессы сделать то же самое. При этом процессу запрещается посылать неслужебные сообщения после того, как он сделает пробную контрольную точку. Каждый процесс извещает Pi о том, сделал ли он пробную контрольную точку. Если все процессы сделали пробные контрольные точки, то Pi принимает решение о превращении пробных точек в постоянные. Если какой-либо процесс не смог сделать пробную точку, то принимается решение об отмене всех пробных точек. 2-ая фаза. Pi информирует все процессы о своем решении. В результате либо все процессы будут иметь новые постоянные контрольные точки, либо ни один из процессов не создаст новой постоянной контрольной точки. Только после выполнения принятого процессом Pi решения все процессы могут посылать сообщения. Корректность алгоритма очевидна, поскольку созданное всеми множество постоянных контрольных точек не может содержать не зафиксированных операций посылки сообщений. Оптимизация: если процесс не посылал сообщения с момента фиксации предыдущей постоянной контрольной точки, то он может не создавать новую. Алгоритм отката (восстановления). Алгоритм предполагает, что его инициирует один процесс и он не будет выполняться параллельно с алгоритмом фиксации. Выполняется в две фазы. 1-ая фаза. Инициатор отката спрашивает остальных, готовы ли они откатываться. Когда все будут готовы к откату, то он принимает решение об откате. 2-ая фаза. Pi сообщает всем о принятом решении. Получив это сообщение, каждый процесс поступает указанным образом. С момента ответа на опрос готовности и до получения принятого решения процессы не должны посылать сообщения (нельзя же посылать сообщение процессу, который уже мог успеть откатиться). Оптимизация: если процесс не обменивался сообщениями с момента фиксации предыдущей постоянной контрольной точки, то он может к ней не откатываться. Асинхронная фиксация контрольных точек и восстановление. Синхронная фиксация упрощает восстановление, но связана с большими накладными расходами: 1. Дополнительные служебные сообщения для реализации алгоритма. 2. Синхронизационная задержка - нельзя посылать неслужебные сообщения во время работы алгоритма. Если отказы редки, то указанные потери совсем не оправданы. Фиксация может производиться асинхронно. В этом случае множество контрольных точек может быть неконсистентным. При откате происходит поиск подходящего консистентного множества путем поочередного отката каждого процесса в ту точку, в которой зафиксированы все посланные им и полученные другими сообщения (для ликвидации сообщений-сирот). Алгоритм опирается на наличие в стабильной памяти для каждого процесса журнала, отслеживающего номера посланных и полученных им сообщений, а также на некоторые предположения об организации взаимодействия процессов, необходимые для исключения эффекта домино (например, организация приложения по схеме сообщение-реакция-ответ). Отказоустойчивость. Изложенные выше методы восстановления после отказов для некоторых систем непригодны (управляющие системы, транзакции в on-line режиме) из-за прерывания нормального функционирования. Чтобы избежать эти неприятности, создают системы, устойчивые к отказам. Такие системы либо маскируют отказы, либо ведут себя в случае отказа заранее определенным образом (пример - изменения, вносимые транзакцией в базу данных, становятся невидимыми при отказе). Два механизма широко используются при обеспечении отказоустойчивости - протоколы голосования и протоколы принятия коллективного решения. Протоколы голосования служат для маскирования отказов (выбирается правильный результат, полученный всеми исправными исполнителями). Протоколы принятия коллективного решения подразделяются на два класса. Во-первых, протоколы принятия единого решения, в которых все исполнители являются исправными и должны либо все принять, либо все не принять заранее предусмотренное решение. Примерами такого решения являются решение о завершении итерационного цикла при достижении всеми необходимой точности, решение о реакции на отказ (этот протокол уже знаком нам - он использовался для принятия решения об откате всех процессов к контрольным точкам). Во-вторых, протоколы принятия согласованных решений на основе полученных друг от друга данных. При этом необходимо всем исправным исполнителям получить достоверные данные от остальных исправных исполнителей, а данные от неисправных исполнителей проигнорировать. Лекция 10. Программирование для распределенных систем Стремительное развитие наук, как фундаментальных, так и прикладных, использующих сложные реалистические (многомерные, многопараметрические) математические модели или требующих сложной, но быстрой обработки информации, а также быстрый технологический прогресс, привело к тому, что значительно возросла потребность в применении мощных вычислительных средств. К таким отраслям относятся: 1. квантовая физика: физика элементарных частиц, ядерная физика, квантовая теория поля; 2. статистическая физика; 3. физика молекул (исследование и предсказание молекулярных свойств материалов); 4. физика плазмы (моделирование поведения плазмы на ЭВМ); 5. квантовая химия (структура молекул и кристаллов, химические реакции); 6. науки о Земле: физика атмосферы, метеорология, климатология (предсказание погоды и изменения климата), геофизика (движение Земной коры и землетрясения), физика океана; 7. биология, экология (прогнозирование развития экосистем); 8. экономика и эконометрия: вычислительная экономика (новая, развивающаяся наука о применении компьютерного моделирования к исследованию сложных, реалистичных моделей экономических процессов), макроэкономика , теория массового обслуживания (например моделирование развития транспортной системы страны, грузопотоков и пассажиропотоков) и теория оптимального управления, финансовая деятельность (моделирование рынка ценных бумаг, банковской деятельности); 9. социальные науки (моделирование демографической ситуации в стране, миграции и занятости населения, социального поведения); 10. математическая лингвистика: распознавание речи, анализ текста и автоматический перевод; 11. информатика: ведение баз данных, распознавание образов, распределенные вычислительные системы; 12. механика сплошных сред: гидродинамика и газодинамика (кораблестроение и самолетостроение, течения, турбостроение), теория сопротивления материалов (устойчивость конструкций и их нагрузочная способность); 13. баллистика (наведение баллистических снарядов и управление реактивным движением); 14. медицина, фармацевтика (моделирование лекарственных препаратов); 15. промышленность, в том числе автомобиле- и авиастроение, нефте- и газодобыча, дизайн, другие. Во всех вышеперечисленных отраслях часто возникают вычислительные задачи и задачи обработки информации, требующие больших затрат вычислительных ресурсов. Следует иметь в виду, однако, что эффективное использование суперкомпьютера требует достаточно высокой квалификации пользователя-программиста. Программирование на суперкомпьютере - это далеко не всегда простая задача и простой перенос программы на, скажем, многопроцессорную вычислительную систему может не дать ожидаемого выигрыша в производительности программы. Может случиться и так, что в результате такого переноса программа будет работать медленнее, чем на компьютере с традиционной архитектурой. В настоящее время существуют два основных подхода к программированию вычислений, эти подходы в литературе часто называют парадигмами программирования. Прежде чем обсудить эти подходы, сделаем несколько замечаний общего характера. Развитие фундаментальных и прикладных наук, технологий требует применения все более мощных и эффективных методов и средств обработки информации. В качестве примера можно привести разработку реалистических математических моделей, которые часто оказываются настолько сложными, что не допускают точного аналитического их исследования. Единственная возможность исследования таких моделей, их верификации (то есть подтверждения правильности) и использования для прогноза - компьютерное моделирование, применение методов численного анализа. Другая важная проблема - обработка больших объемов информации в режиме реального времени. Все эти проблемы могут быть решены лишь на достаточно мощной аппаратной базе, с применением эффективных методов программирования. Мы являемся свидетелями быстрого прогресса вычислительной техники. Производительность современных компьютеров на много порядков превосходит производительность первых ЭВМ и продолжает возрастать заметными темпами. Увеличиваются и другие ресурсы, такие как объем и быстродействие оперативной и постоянной памяти, скорость передачи данных между компонентами компьютера и т.д. Совершенствуется архитектура ЭВМ. Вместе с тем следует заметить, что уже сейчас прогресс в области микроэлектронных компонент сталкивается с ограничениями, связанными с фундаментальными законами природы. Вряд ли можно надеяться на то, что в ближайшее время основной прогресс в быстродействии электронно-вычислительных машин будет достигнут лишь за счет совершенствования их элементной базы. Переход на качественно новый уровень производительности потребовал от разработчиков ЭВМ и новых архитектурных решений. 10.1 Две модели программирования: последовательная и параллельная Традиционная архитектура ЭВМ была последовательной. Это означало, что в любой момент времени выполнялась только одна операция и только над одним операндом. Совокупность приемов программирования, структур данных, отвечающих последовательной архитектуре компьютера, называется моделью последовательного программирования. Ее основными чертами являются применение стандартных языков программирования (ФОРТРАН-77, ФОРТРАН-90, С/С++, …), достаточно простая переносимость программ с одного компьютера на другой и невысокая производительность. Появление в середине шестидесятых первого компьютера класса суперЭВМ, разработанного в фирме CDC знаменитым Сеймуром Крэем, ознаменовало рождение новой - векторной архитектуры. Основная идея, положенная в основу новой архитектуры, заключалась в распараллеливании процесса обработки данных, когда одна и та же операция применяется одновременно к массиву (вектору) значений. В этом случае можно надеяться на определенный выигрыш в скорости вычислений. Идея параллелизма оказалась плодотворной и нашла воплощение на разных уровнях функционирования компьютера. Более подробное обсуждение аппаратной реализации параллельной обработки информации можно найти во второй главе, здесь же упомянем конвейерную обработку, многопроцессорность и т.д. Основными особенностями модели параллельного программирования являются высокая эффективность программ, применение специальных приемов программирования и, как следствие, более высокая трудоемкость программирования, проблемы с переносимостью программ. Две парадигмы параллельного программирования В настоящее время существуют два основных подхода к распараллеливанию вычислений. Это параллелизм данных и параллелизм задач. В англоязычной литературе соответствующие термины - data parallel (параллельная обработка данных) и message passing (передача сообщений). В основе обоих подходов лежит распределение вычислительной работы по доступным пользователю процессорам параллельного компьютера. При этом приходится решать разнообразные проблемы. Прежде всего это достаточно равномерная загрузка процессоров, так как если основная вычислительная работа будет ложиться на один из процессоров, мы приходим к случаю обычных последовательных вычислений и в этом случае никакого выигрыша за счет распараллеливания задачи не будет. Сбалансированная работа процессоров - это первая проблема, которую следует решить при организации параллельных вычислений. Другая и не менее важная проблема - скорость обмена информацией между процессорами. Если вычисления выполняются на высокопроизводительных процессорах, загрузка которых достаточно равномерная, но скорость обмена данными низкая, основная часть времени будет тратиться впустую на ожидание информации, необходимой для дальнейшей работы данного процессора. Рассматриваемые парадигмы программирования различаются методами решения этих двух основных проблем. Разберем более подробно параллелизм данных и параллелизм задач. Параллелизм данных Основная идея подхода, основанного на параллелизме данных, заключается в том, что одна операция выполняется сразу над всеми элементами массива данных. Различные фрагменты такого массива обрабатываются на векторном процессоре или на разных процессорах параллельной машины. Распределением данных между процессорами занимается программа. Векторизация или распараллеливание в этом случае чаще всего выполняется уже на этапе компиляции - перевода исходного текста программы в машинные команды. Роль программиста в этом случае обычно сводится к заданию опций векторной или параллельной оптимизации компилятору, директив параллельной компиляции, использованию специализированных языков для параллельных вычислений. Наиболее распространенными языками для параллельных вычислений являются Высокопроизводительный ФОРТРАН (High Performance FORTRAN) и параллельные версии языка C (это, например, C*). Более детальное описание рассматриваемого подхода к распараллеливанию содержит указание на следующие его основные особенности: • Обработкой данных управляет одна программа; • Пространство имен является глобальным, то есть для программиста существует одна единственная память, а детали структуры данных, доступа к памяти и межпроцессорного обмена данными от него скрыты; • Слабая синхронизация вычислений на параллельных процессорах, то есть выполнение команд на разных процессорах происходит, как правило, независимо и только лишь иногда производится согласование выполнения циклов или других программных конструкций - их синхронизация. Каждый процессор выполняет один и тот же фрагмент программы, но нет гарантии, что в заданный момент времени на всех процессорах выполняется одна и та же машинная команда; • Параллельные операции над элементами массива выполняются одновременно на всех доступных данной программе процессорах. Видим, таким образом, что в рамках данного подхода от программиста не требуется больших усилий по векторизации или распараллеливанию вычислений. Даже при программировании сложных вычислительных алгоритмов можно использовать библиотеки подпрограмм, специально разработанных с учетом конкретной архитектуры компьютера и оптимизированных для этой архитектуры. Подход, основанный на параллелизме данных, базируется на использовании при разработке программ базового набора операций: • операции управления данными; • операции над массивами в целом и их фрагментами; • условные операции; • операции приведения; • операции сдвига; • операции сканирования; • операции, связанные с пересылкой данных. Рассмотрим эти базовые наборы операций. Управление данными. В определенных ситуациях возникает необходимость в управлении распределением данных между процессорами. Это может потребоваться, например, для обеспечения равномерной загрузки процессоров. Чем более равномерно загружены работой процессоры, тем более эффективной будет работа компьютера. Операции над массивами Аргументами таких операций являются массивы в целом или их фрагменты (сечения), при этом данная операция применяется одновременно (параллельно) ко всем элементам массива. Примерами операций такого типа являются вычисление поэлементной суммы массивов, умножение элементов массива на скалярный или векторный множитель и т.д. Операции могут быть и более сложными - вычисление функций от массива, например. Условные операции Эти операции могут выполняться лишь над теми элементами массива, которые удовлетворяют какому-то определенному условию. В сеточных методах это может быть четный или нечетный номер строки (столбца) сетки или неравенство нулю элементов матрицы. Операции приведения Операции приведения применяются ко всем элементам массива (или его сечения), а результатом является одно единственное значение, например, сумма элементов массива или максимальное значение его элементов. Операции сдвига Для эффективной реализации некоторых параллельных алгоритмов требуются операции сдвига массивов. Примерами служат алгоритмы обработки изображений, конечно-разностные алгоритмы и некоторые другие. Операции сканирования Операции сканирования еще называются префиксными/суффиксными операциями. Префиксная операция, например, суммирование выполняется следующим образом. Элементы массива суммируются последовательно, а результат очередного суммирования заносится в очередную ячейку нового, результирующего массива, причем номер этой ячейки совпадает с числом просуммированных элементов исходного массива. Операции пересылки данных Это, например, операции пересылки данных между массивами разной формы (то есть имеющими разную размерность и разную протяженность по каждому измерению) и некоторые другие. При программировании на основе параллелизма данных часто используются специализированные языки - CM FORTRAN, C*, FORTRAN+, MPP FORTRAN, Vienna FORTRAN, а также HIGH PERFORMANCE FORTRAN (HPF). HPF основан на языке программирования ФОРТРАН 90, что связано с наличием в последнем удобных операций над массивами (см., например, М.Меткалф и Дж.Рид Описание языка программирования ФОРТРАН 90, М."Мир", 1995). Параллелизм задач Стиль программирования, основанный на параллелизме задач подразумевает, что вычислительная задача разбивается на несколько относительно самостоятельных подзадач и каждый процессор загружается своей собственной подзадачей. Компьютер при этом представляет собой MIMD - машину. Аббревиатура MIMD обозначает в известной классификации архитектур ЭВМ компьютер, выполняющий одновременно множество различных операций над множеством, вообще говоря, различных и разнотипных данных. Для каждой подзадачи пишется своя собственная программа на обычном языке программирования, обычно это ФОРТРАН или С. Чем больше подзадач, тем большее число процессоров можно использовать, тем большей эффективности можно добиться. Важно то, что все эти программы должны обмениваться результатами своей работы, практически такой обмен осуществляется вызовом процедур специализированной библиотеки. Программист при этом может контролировать распределение данных между процессорами и подзадачами и обмен данными. Очевидно, что в этом случае требуется определенная работа для того, чтобы обеспечить эффективное совместное выполнение различных программ. По сравнению с подходом, основанным на параллелизме данных, данный подход более трудоемкий, с ним связаны следующие проблемы: • повышенная трудоемкость разработки программы и ее отладки; • на программиста ложится вся ответственность за равномерную загрузку процессоров параллельного компьютера; • программисту приходится минимизировать обмен данными между задачами, так как пересылка данных - наиболее "времяемкий" процесс; • повышенная опасность возникновения тупиковых ситуаций, когда отправленная одной программой посылка с данными не приходит к месту назначения. Привлекательными особенностями данного подхода являются большая гибкость и большая свобода, предоставляемая программисту в разработке программы, эффективно использующей ресурсы параллельного компьютера и, как следствие, возможность достижения максимального быстродействия. Примерами специализированных библиотек являются библиотеки MPI (Message Passing Interface) и PVM (Parallel Virtual Machines). Эти библиотеки являются свободно распространяемыми и существуют в исходных кодах. Библиотека MPI разработана в Аргоннской Национальной Лаборатории (США), а PVM - разработка Окриджской Национальной Лаборатории, университетов штата Теннеси и Эмори (Атланта). 10.2 Векторизация и распараллеливания программ Целью программиста не должно быть получение правильного результата вычислений любой ценой, но получение правильного результата наибыстрейшим, оптимальным способом. Если программа предназначена для однократного использования, то лучше написать ее как можно проще, не оптимизируя ее быстродействие и используемую память, чтобы потратить минимум усилий на тестирование и отладку. Если программа предназначена для частого использования или время ее работы будет гораздо больше времени ее написания и отладки, то не следует жалеть труда на оптимизацию ее быстродействия. Но в результате программа, оптимальная для одного типа ЭВМ, окажется совсем не оптимальной для машин других типов. Методы программирования для скалярных, векторных и параллельных ЭВМ коренным образом отличаются друг от друга. Конечно, любая программа, написанная на языке высокого уровня, может быть оттранслирована в коды любой ЭВМ и исполнена как на скалярной, так и на векторной или параллельной машинах и результаты работы всех программ скорее всего будут близкими. Эту оговорку надо принять во внимание. Дело в том, что результат действий с вещественными числами может изменяться при изменении последовательности операций, т.к. в компьютерной арифметике закон сочетаний не всегда выполняется: (A+B)+C не всегда равно A+(B+C). Самый простой вариант попробовать ускорить имеющуюся программу - это воспользоваться встроенными в транслятор (обычно с ФОРТРАНа или Си) средствами векторизации или распараллеливания. При этом никаких изменений в программу вносить не придется. Однако вероятность существенного ускорения (в разы или десятки раз) невелика. Трансляторы с ФОРТРАНа и Си векторизуют и распараллеливают программы очень аккуратно и при любых сомнениях в независимости обрабатываемых данных оптимизация не проводится. Поэтому, кстати, и не приходится ожидать ошибок от компиляторов, если программист явно не указывает компилятору выполнить векторную или параллельную оптимизацию какой-либо части программы. Второй этап работы с такой программой - анализ затрачиваемого времени разными частями программы и определение наиболее ресурсопотребляющих частей. Последующие усилия должны быть направлены именно на оптимизацию этих частей. В программах наиболее затратными являются циклы и усилия компилятора направлены прежде всего на векторизацию и распараллеливание циклов. Диагностика компилятора поможет установить причины, мешающие векторизовать и распараллелить циклы. Возможно, что простыми действиями удастся устранить эти причины. Это может быть простое исправление стиля программы, перестановка местами операторов (цикла и условных), разделение одного цикла на несколько, удаление из критических частей программы лишних операторов (типа операторов отладочной печати). Небольшие усилия могут дать здесь весьма существенный выигрыш в быстродействии. Третий этап - замена алгоритма вычислений в наиболее критичных частях программы. Способы написания оптимальных (с точки зрения быстродействия) программ существенно отличаются в двух парадигмах программирования - в последовательной и в параллельной (векторной). Поэтому программа, оптимальная для скалярного процессора, с большой вероятностью не может быть векторизована или распараллелена. В то же время специальным образом написанная программа для векторных или параллельных ЭВМ будет исполняться на скалярных машинах довольно медленно. Замена алгоритма в наиболее критических частях программы может привести к серьезному ускорению программы при относительно небольших потраченных усилиях. Дополнительные возможности предоставляют специальные векторные и параллельные библиотеки подпрограмм. Используя библиотечные функции, которые оптимизированы для конкретной ЭВМ, можно упростить себе задачу по написанию и отладке программы. Единственный недостаток данного подхода состоит в том, что программа может стать не переносимой на другие машины (даже того же класса), если на них не окажется аналогичной библиотеки. Написание программы "с нуля" одинаково сложно (или одинаково просто) для машин любых типов. Этот способ является идеальным для разработки эффективных, высокопроизводительных векторных или параллельных программ. Начинать надо с изучения специфики программирования для векторных и параллельных ЭВМ, изучения алгоритмов, которые наиболее эффективно реализуются на ЭВМ данных типов. После этого надо проанализировать поставленную задачу и определить возможность применения векторизуемых и распараллеливаемых алгоритмов для решения конкретной задачи. Возможно, что придется переформулировать какие-то части задачи, чтобы они решались с применением векторных или параллельных алгоритмов. Программа, специально написанная для векторных или параллельных ЭВМ, даст наибольшее ускорение при ее векторизации и распараллеливании. Программист должен быть готов к поддержанию одновременно нескольких (двух или даже трех) версий программы, предназначенных для работы на ЭВМ разного типа. Это, наверно, самая существенная из всех "издержек", связанная с одновременным использованием скалярных, векторных и параллельных машин. Применение разных языков программирования Перенос готовой программы и оптимизация ее для исполнения на машине другого типа может потребовать весьма существенных усилий по исправлению стиля написаниния программы. Опыт показывает, что проще всего адаптировать к исполнению на векторных или параллельных машинах программы на ФОРТРАНе. Конструкции ФОРТРАНа для организации циклов и для работы с массивами менее разнообразны, чем в Си, а именно циклы по обработке массивов и являются главными объектами оптимизации при векторизации или распараллеливанию программ. Более того, в ФОРТРАНе-90 встроенные функции по работе с массивами и арифметические операции с массивами имеют довольно эффективную реализацию в виде стандартных библиотечных векторных или параллельных функций или даже в виде генерируемого компилятором объектного кода (in line). Больше всего трудностей появится при переносе программ на Си, оптимальным образом написанных для скалярных процессоров. Следующие программы на Си показывают два принципиально разных подхода к операциям с массивами. Традиционный Си'шный подход к копированию массивов является недопустимым при написании векторных или параллельных программ. Приведенный ниже цикл не может быть векторизован или распараллелен из-за того, что значение ссылок на элементы массивов вычисляются рекуррентно (зависят от значений на предыдущем шаге): float x[100], a[100]; register float *xx = x, *aa = a; register int i; for( i=0; i<100; i++){ *xx++ = *aa++ }; В то время как другой цикл, написанный совсем не в традициях Си, может быть распараллелен и векторизован: float x[100], a[100]; register int i; for( i=0; i<100; i++){ x[i] = a[i]; } Сравните: ФОРТРАН-77 позволяет единственным способом записать цикл и этот способ совпадает со вторым, нетрадиционным способом в Си: real x(100), a(100) do i=1, 100 x(i) = a(i) enddo ФОРТРАН-90 разрешает применить векторную операцию присваивания, что упрощает анализ программы транслятором: real x(100), a(100) x = a ! векторное присваивание Приведенный пример показывает, что существенным фактором при адаптации готовых программ к работе на параллельных и векторных ЭВМ является не только стиль написания программы, но и сам использованный язык программирования. Различие и сходство между распараллеливанием и векторизацией программ Сходство алгоритмов - параллелизм данных Из архитектуры векторных и параллельных компьютеров следует самое важное свойство алгоритмов, которые могут быть эффективно исполнены на таких ЭВМ: эти алгоритмы должны включать одинаковые действия над некоторыми наборами данных (массивами), при этом результаты действий над любой частью данных не должны зависеть от результатов действий над другой их частью. Иначе говоря, последовательность вычисления величин не должна играть роль в данном алгоритме. Это есть параллелизм данных. Простейшим примером такого алгоритма может служить следующий фрагмент программы: real x(100), a(100), b(100) do i=1, 100 x(i) = real(i)**2 + 3.0 a(i) = b(i) + x(i) enddo float x[100], a[100], b[100]; for( i=0; i<100; i++ ){ x[i] = (i+1.0) * (i+1.0) + 3.0; a[i] = b[i] + x[i]; } или в другой интерпретации: real x(100), a(100), b(100) do i=100, 1, -1 x(i) = real(i)**2 + 3.0 a(i) = b(i) + x(i) enddo Предположим, что i=5, тогда тело цикла примет вид: x(5) = 5.0**2 + 3.0 a(5) = b(5) + x(5) x[4] = (4+1.0) * (4+1.0) + 3.0; a[4] = b[4] + x[4]; Порядок исполнения этих двух опраторов не может быть изменен, но 5-ые элементы массивов a и x могут быть вычислены независимо от 4-го, 6-го и других элементов. Приведенная программа удовлетворяет основному требованию к векторизуемым алгоритмам - для любого значения i вычисления можно проводить независимо. Предположим, что у нас имеется векторная ЭВМ с векторными регистрами длиной 100 элементов. Тогда весь цикл по всем элементам массива записывается как линейная (не циклическая) последовательность команд, а каждая команда оперирует со всеми 100 элементами массива. Выигрыш в увеличении быстродействия программ (по отношению к быстродействию не векторизованных программ на той же ЭВМ) может ожидаться равным числу элементов в массиве. Теперь представим, что у нас есть 100-процессорная параллельная ЭВМ и каждый процессор имеет прямой доступ ко всем элементам массивов. Тогда для каждого скалярного процессора можно написать программу для вычисления значений a(i) и x(i) для одного конкретного i, совпадающего с номером процессора (процессор должен знать свой номер). В принципе такая программа также, как и векторная, будет линейной. Т.к. у нас в распоряжении 100 процессоров, то после исполнения каждым процессором своих команд все 100 элементов массивов a и x будут вычислены. Очевидно, что это будет в 100 раз быстрее, чем вычисление всех элементов одним процессором. Различие алгоритмов - параллелизм действий Можно проследить аналогию в векторной и параллельной реализациях предыдущей программы. Каждая машинная команда вызывает действия сразу над 100 числами: в векторной программе явно выполняются операции над всеми элементами регистра, в параллельной программе каждый из 100 процессоров выполняет более или менее синхронно одинаковые машинные команды, оперирует со своими собственными регистрами и в результате выполняются действия одновременно со 100 числами. Справедливо следующее утверждение: алгоритм, который можно векторизовать, можно и распараллелить. Обратное утверждение не всегда верно. В многопроцессорной ЭВМ каждый процессор исполняет свой поток команд. В общем случае для каждого из процессоров параллельной ЭВМ можно составить свою программу, не повторяющую программы для других процессоров. Предположим, что скалярная величина y есть сумма двух функций: y = F(x) + G(x), тогда один процессор может считать значение F(x), а второй - G(x). После счета достаточно сложить полученные два числа, чтобы получить требуемое значение y. Такой параллелизм действий может быть достигнут (и так поступают наиболее часто) в единой программе для обоих процессоров: if( номер_процессора .eq. 1 ) then y1 = F(x) else if( номер_процессора .eq. 2 ) then y2 = G(x) endif ждать завершения работы обоих процессоров if( номер_процессора .eq. 1 ) then y = y1 + y2 endif Параллельность действий (т.е. применение параллельных ЭВМ) дает большое преимущество в программировании перед чистой параллельностью данных (применением векторных ЭВМ). Даже простой вызов подпрограммы приводит к невозможности векторизации цикла, в то время, как распараллеливание возможно: real a(100) do i=1, 100 call proc( a(i) ) enddo Теперь мы подробнее рассмотрим преимущества при векторизации и распараллеливании программ. Векторные машины и векторные программы Предельное быстродействие векторных программ (Вспомним закон Амдалла) Мы будем здесь рассматривать машины с векторными регистрами. Векторный процессор выполняет математические операции сразу над всеми элементами векторного регистра. Если число элементов регистра равно 128, то операция над всеми 128 числами выполняется в векторном режиме так же быстро, как над одним числом в скалярном режиме. Это и есть теоретический предел повышения быстродействия программ при их векторизации. Однако необходимо учесть, что любая векторная операция требует больше машинных тактов для своего исполнения, чем такая же скалярная операция. С другой стороны циклическое N-кратное исполнение скалярной команды для обработки массива требует исполнения еще нескольких команд, организующих собственно цикл. В результате исполнение векторной команды может оказаться эффективнее более, чем в 128 раз. Для простоты мы будем считать, что предельное повышение эффективности векторных программ равно числу элементов в векторном регистре ЭВМ. Чаще всего это 128 или 256. Две части программы - скалярная и векторная В любой программе существуют две части - векторизуемая и не векторизуемая. Например алгоритмы построения последовательностей, заданных рекуррентным отношением, нельзя векторизовать - каждый последующий элемент зависит от предыдущих и, соответственно, не может быть вычислен ни ранее, ни одновременно с предыдущими. Другие невекторизуемые части программ - ввод/вывод, вызов подпрограмм или функций, организация циклов, разветвленные алгоритмы, работа со скалярными величинами. Это довольно широкий класс подзадач, он гораздо шире класса векторизуемых алгоритмов. При векторизации программ на самом деле ускоряется выполнение только части программы (большей или меньшей). Поэтому каждую программу можно представить такой упрощенной диаграммой (если собрать все векторизуемые и все скалярные части в единые блоки): +-----------------------+ | 1 начало | +-----------------------+ | --->-------------| | | | +-----------------------+ | | 2 векторизуемая часть | | +-----------------------+ | | | +-----------------------+ | | 3 скалярная часть | | +-----------------------+ | | | +----------------------+ ---<--| 4 организация цикла | +----------------------+ | | +-----------------------+ | 5 окончание | +-----------------------+ Для начала исполним программу в полностью скалярном варианте (т.е. умышленно не будем векторизовать исполняемый код). Обозначим время исполнения каждой из частей программы через T1...T5. Тогда полное время работы невекторизуемых частей программы будет равно Tскал = T1 + T3 + T4 + T5 а полное время работы программы будет T' = Tскал + T2 Далее перетранслируем программу в режиме векторизации. Единственная часть программы, которая ускорит свое выполнение, будет 2-ая. Предположим, что мы достигли ускорения работы этой части в N раз. Тогда полное время работы всей программы составит T" = Tскал + T2/N А выигрыш в эффективности работы всей программы будет T' Tскал + T2 Р = ---- = -------------- , T" Tскал + T2/N что совсем не равно N. Положим, что Tскал = 1с, T2 = 99с, а N=100 (очень хороший показатель для 128-элементных векторных процессоров). В нашем варианте эффективность P будет всего (1+99)/(1+99/100)=50 или 40% от предельно возможных 128 раз, а при Tскал = 2с значение P будет (2+98)/(2+98/100)=34 (27%). Хотя само по себе увеличение быстродействия программы в 50 или даже в 30 раз при ее векторизации является очень большим (вычисления будут занимать 1 сутки вместо 1 месяца), но предельно возможное ускорение в 128 (или 256) раз не может быть достигнуто на векторных ЭВМ. Практика показывает, что хорошим показателем увеличения быстродействия P можно считать уже значения 6-10, что соответствует времени исполнения скалярной части всего 10-15% от полного времени исполнения программы (T2 составляет 85-90%). Дополнительные затраты на организацию векторных вычислений во время работы программы Для работы на векторных ЭВМ наиболее удобными являются массивы с длиной, равной длине векторного регистра. Однако это благое пожелание очень редко выполняется. Более того, часто число элементов массива вообще не кратно 128. Рассмотрим простейший цикл, который можно векторизовать. Пусть дан массив m длины 128, который надо заполнить по следующему алгоритму: do i=1, 128 m(i) = i enddo Мы будем пользоваться командами ассемблера несуществующей ЭВМ, но вполне отражающими смысл операций с векторными регистрами. Приведенный цикл можно записать на ассемблере так: SETLEN #128 ; установить используемое число ; элементов в векторных регистрах (во всех) SETINC #4 ; установить смещение к последующему ; элементу массива в памяти (4 байта) SETNUM v0 ; записать в элементы векторного регистра v0 ; их номера начиная с нуля и кончая 127 ADD #1, v0 ; добавить 1 к каждому элементу регистра v0 SAVE v0, m ; записать элементы v0 в ОЗУ в последовательные ; слова (смещение = 4) начиная с адреса m Обратите внимание на 3 команду - каждый элемент векторного регистра "знает" свой номер. Это делает очень простым вычисление переменной цикла: после добавления 1 значение переменной получается записанным в соответствующий элемент вектора. Всего 5 последовательных команд векторного процессора выполняют цикл из 128 повторений. Здесь нет ни команд сравнения, ни условных переходов. Теперь увеличим размер массива и число повторений цикла до N: do i=1, N m(i) = i enddo Для правильной работы процессора мы обязаны установить число используемых элементов вектора не более, чем 128. Если N будет произвольным, то нам придется превратить данный одинарный цикл в двойной: 1 do inc=0, N-1, 128 2 NN = min0( 128, N-inc ) 3 do i=1, NN m(inc+i) = inc+i enddo enddo Цикл с меткой 1 выполняает "разбиение" массива на подмассивы длиной 128 элементов. Переменная inc имеет смысл смещения от первого элемента массива к очередному подмассиву: 0, 128, 256... Переменная NN определяет длину подмассива. Обычно она равна 128, но последний подмассив может иметь меньшую длину, если N не кратно 128. Функция min0 выбора минимального значения в операторе с меткой 2 выдает значение не 128 только для последнего подмассива. Внутренний цикл с меткой 3 практически эквивалентен циклу из предыдущего примера. Имеется только 3 отличия: число элементов в векторном регистре равно NN, к параметру цикла дополнительно надо добавлять значение inc, регистр записывать в память начиная не с начала массива, а c элемента с номером i+inc Этот цикл может быть записан в машинных командах примерно так: SETLEN NN ; число элементов в векторных регистрах = NN SETINC #4 ; смещение к последующему элементу массива SETNUM v0 ; записать в элементы векторного регистра v0 ; их номера начиная с нуля и кончая 127 ADD #1, v0 ; добавить 1 к каждому элементу регистра v0 ADD inc, v0 ; добавить значение переменной inc MOVE inc, r0 ; записать в скалярный регистр r0 значение ; переменной inc - смещение от первого ; элемента массива к m(inc+1) MUL #4, r0 ; умножить на 4 - смещение в байтах ADD #m, r0 ; добавить адрес массива m, получается адрес ; элемента m(inc+1) SAVE v0, @r0 ; записать элементы v0 в ОЗУ в последовательные ; слова (смещение = 4) начиная с адреса, ; хранящегося в регистре r0 По отношению к простейшему циклу добавились одна векторная и три скалярных команды. Однако это только внутренний цикл. Охвытывающий его цикл с меткой 1 и вычисление NN создадут дополнительный код, который будет выполняться столько же раз, сколько и код для внутреннего цикла. Транслятор всегда будет создавать охватывающий цикл, если N есть переменная, а не константа со значением от 1 до 128 (для 128-элементного векторного регистра). Из сказанного выше следует, что эффективность векторной программы будет невысокой при работе с небольшими массивами, длина которых заранее неизвестна. Циклы с тремя повторениями могут в векторном режиме выполняться медленнее, чем в скалярном на той же машине. Ограниченное число векторных регистров Число векторных регистров в процессоре обычно гораздо меньше, чем число скалярных регистров (обычно 8 регистров). Это накладывает ограничения на сложность выражений (т.е. число массивов и скалярных переменных), стоящих в теле векторизуемого цикла. К числу непосредственно массивов (индексированных переменных) добавляется сам параметр цикла. В предыдущем параграфе было показано, что простая скалярная переменная i "векторизуется" и преобразуется в массив из 128 (или NN) последовательных значений параметра цикла. Очевидно, что любые арифметические выражения, содержащие i, в дополнение к индексированным этой переменной элементам массивов, и все скалярные переменные, зависящие от i, тоже должны быть векторизованы. Число используемых векторных переменных легко может превысить число векторных регистров даже в выражениях, явным образом содержащих только один-два массива. В такой ситуации транслятор создает в ОЗУ дополнительные 128-элементные массивы для сохранения промежуточных значений векторных регистров и при вычислении сложных выражений предусматривает сохранение и загрузку векторных регистров из этих временных массивов. Перезагрузка регистров (swaping) может стать фактором, существенно замедляющим программу. Не следует опасаться, что все используемые массивы программа будет постоянно хранить в векторных регистрах. Опртимизатор транслятора может переставлять местами строки исходного текста так, чтобы не изменить смысл программы, но в то же время уменьшить число используемых регистров. Например, написанный программистом код do i=1, N x(i) = i y(i) = 0.0 z(i) = x(i)+1.0 enddo может быть изменен на такой: do i=1, N 1 x(i) = i 2 z(i) = x(i)+1.0 3 y(i) = 0.0 enddo Но во втором примере после векторизации i в операторе 1 и добавлении к этому вектору 1.0 в операторе 2 можно повторно использовать тот же векторный регистр для векторизации нуля в операторе 3. В этом варианте цикла один векторный регистр будет использован последовательно для разных целей. Ограничения на используемые операторы в векторизуемых циклах Существенным ограничением на конструкции, которые могут применяться в векторизуемых циклах, является использование только операторов присваивания и арифметических выражений. Никакие команды перехода (условные ветвления, вызовы подпрограмм и функций, циклические операторы или безусловные переходы) не могут быть использованы в теле векторизуемого цикла. Из перечисленных запретов существует только два исключения. Первое - использование встроенных (INTRINSIC) в транслятор арифметических функций. Большинство таких функций реализуются в библиотеках языка, а некоторые транслируются в последовательности машинных команд. Обычно каждая функция имеет две реализации - скалярную и векторную. Про встроенные функции транслятор "знает" все, чтобы сгенерировать векторный код. Примером может служить функция cos(x). Скалярная реализация может брать свой аргумент в определенном регистре (например, r0) и возвращать значение в том же регистре сохраняя прежними значения остальных регистров. Векторный выриант может брать аргумент в векторном регистре (например, v0) и там же оставлять результат. Поэтому транслятору достаточно вычислить массив аргументов в заданном регистре, вызвать библиотечную функцию и далее работать с полученным массивом значений. Важное замечание для любителей Си. В этом языке все математические функции внешние - они описываются в файле math.h и содержаться в дополнительной (!) библиотеке libm.a - и они не векторизуются (чаще всего). В ФОРТРАНе большое число функций (в т.ч. и комплексного аргумента) встроены. Разработчики программного обеспечения для векторных ЭВМ обычно расширяют стандартный набор функций, что позволяет использовать их в векторизуемых циклах. Второе исключение - использование условного оператора присваивания if( x(i) .lt. 0.0 ) z(i) = 0.0 Все арифметические операции (в т.ч. и само присваивание) будут выполняться не над всем векторным регистром, а только над теми его элементами, для которых было справедливо вычисленное логическое выражение (маскируемые операции). Команда сравнения устанавливает маску для каждого элемента вектора: "истина", если элемент вектора меньше нуля, и "ложь", если элемент больше или равен нулю. Команда присваивания не затронет те элементы массива z, для которых маска равна "ложь". Векторный процессор будет исполнять команды для вычисления арифметического выражения и команду присваивания, даже если не будет ни одного значения маски "истина". Это важное примечание. Команды исполнения по маске всегда будут занимать процессорное время. Использование векторных операций и функций ФОРТРАНа-90 Векторные операции и функции - это, пожалуй, единственные конструкции ФОРТРАНа-90, которые нашли быстрое и эффективное воплощение в ФОРТРАНе для векторных ЭВМ. Практически на всех машинах (векторных и скалярных) стандартным сейчас является ФОРТРАН-77. Поэтому векторные операции и функции являются для него расширением и их использование в программах может привести к несовместимости исходного кода между различными ЭВМ. Параллельные ЭВМ и параллельные программы Предельное быстродействие параллельных программ При работе на параллельных ЭВМ пользователь имеет возможность запускать программу или на всех процессорах сразу, или на ограниченном их числе. Поскольку все процессоры в параллельных ЭВМ одинаковые (в составе параллельной ЭВМ могут работать еще и специализированные процессоры ввода/вывода, но на них счет не проводится), то можно ожидать, что программа будет выполняться во столько раз быстрее, сколько процессоров будут проводить вычисления. Три части программы - параллельная, последовательная и обмен данными Как и в векторных программах, в любом параллельном алгоритме присутствуют параллельная и последовательная части. В отличие от векторизации, внутренние циклы, ветвящиеся алгоритмы, вызовы подпрограмм и функций не являются препятствием для распараллеливания программы. При распараллеливании программы могут быть оптимизированы внешние, самые всеобъемлющие циклы. Однако любые рекуррентные вычисления, ввод/вывод, вычисления, понижающие размерность массивов (вплоть до скаляра), не могут быть (полностью) распараллелены. Исполнение разных частей программы разными процессорами или, если быть точнее, разными процессами вносит дополнительный обязательный фрагмент в программу, а именно, обмен данными между процессами. Современные параллельные ЭВМ исполняют разные копии одинаковой программы в качестве отдельных задач, т.е. процессов. Каждый процесс может иметь свои локальные данные и глобальные данные, к которым есть доступ у всех процессов. Результаты, сосчитанные в одних процессах, в определенные моменты должны передаваться в другой или другие процессы для дальнейшей работы. Это процесс обмена данными. Рассмотрим такой фрагмент программы, который будет исполняться на 4-х процессорной ЭВМ: real x(4) 1 do i=1,4 x(i) = func(i) enddo 2 s = 0.0 3 do i=1,4 s = s + x(i) enddo Все 4 элемента массива x можно вычислить параллельно в цикле с меткой 1. При этом вообще цикл не понадобится, т.к. у нас число процессов будет равно числу искомых элементов массива. Переменную s должен инициализовать только один процесс - это последовательный фрагмент программы. Цикл с меткой 3 должен также выполняться одним процессом. Для этого надо сначала передать этому процессу все значения x(i), i=1..4, из других процессов. Этот цикл не сможет начаться раньше, чем будет вычислен и передан последний (не по номеру, а по времени) элемент массива x. Т.е. главный процесс (проводящий суммирование) будет ожидать завершение передачи элементов массива всеми остальными процессами. Время обмена данными зависит от архитектуры параллельной ЭВМ. Оно может быть равно нулю для многопроцессорных рабочих станций с общей оперативной памятью и организацией распараллеливания в пределах одного процесса или составлять значительную величину при обмене в кластерах ЭВМ, связанных компьютерной сетью. Выводы: объем данных, предназначенных для обмена, должен быть по возможности меньше, а последовательная часть программы должна быть как можно быстрее. Часто это удается совмещать путем проведения частичных вычислений в параллельном режиме (например, вычисление частичных сумм) с последующей передачей промежуточных результатов в главный процесс (например, для вычисления полной суммы). Синхронизация процессов, равномерность загрузки процессов Еще один важный фактор, влияющий на ускорение работы параллельных программ, есть равномерность загрузки процессов. При обсуждении предыдущей программы было сказано, что главный процесс перед началом исполнения цикла 3 должен получить все элементы массива x. Даже если собственно время обмена данными будет равно нулю, то все равно цикл не сможет начаться до окончания вычисления последнего (не по номеру, а по времени) из элементов x. За этим следит одна из важнейших частей параллельного алгоритма, которая часто называется "барьером" и осуществляет синхронизацию процессов. Предположим, что время вычисления x(i) будет равно 1, 2, 3 и 4 секундам для соответствующих i. Тогда самое последнее значение x(4) будет получено через 4 с после начала вычислений, а цикл 3 не сможет начаться ранее этого времени. Если к концу предыдущей программы дописать такой распараллеливаемый фрагмент: 4 do i=1,4 x(i) = x(i)/s enddo то, несмотря на незагруженность трех процессов исполнением цикла 3, они не смогут продолжить работу до его (цикла) окончания и рассылки главным процессом значения s во все процессы. Перед циклом 4 неявно запрограммирована синхронизация всех процессов, которая может привести к их простою. Предположим, что главным процессом у нас является третий. Тогда первый процесс после завершения вычисления x(1) (на это у него уйдет 1 секунда) перейдет в режим ожидания значения s: 3 с для завершения вычисления x(4) плюс время обмена элементами массива плюс время вычисления суммы третьим процессом и, наконец, плюс время получения s. Важный вывод из сказанного выше - программист должен распределить вычислительную работу как можно более равномерно между всеми процессами. Средства распараллеливания в трансляторах и параллельные библиотеки Так называемые "высокопроизводительные ФОРТРАН и Си" (high-performance FORTRAN and C - HPF and HPC) являются новыми стандартами на компиляторы для параллельных суперкомпьютеров. Эти языки полностью совместимы с "обычными" ФОРТРАН-77 и Си/Си++. Обычная программа может быть без каких-либо изменений оттранслирована для супер-ЭВМ и исполнена на любом числе процессоров. Однако такой простейший подход приведет к тому, что каждый из процессов на супер-ЭВМ будет полностью от начала и до конца исполнять всю программу без какого-либо реального распараллеливания. Для распараллеливания программы с помощью HPF или HPC надо вставлять специальные комментарии (прагмы), не влияющие на смысл программы, но указывающие транслятору как разместить данные (наиболее важно для массивов) и как распараллелить циклы по обработке этих массивов. При трансляции на других машинах эти прагмы не будут восприниматься трансляторами и как-либо влиять на результирующий машинный код. Программы являются переносимыми на обычные скалярные ЭВМ. HPF или HPC реализуют концепцию параллелизма данных. Приведем здесь простейший пример прагм на диалекте MPP Fortran для ЭВМ Cray-T3D: c описания: real x(1024) CDIR$ SHARED X(:BLOCK) c действия: CDIR$ DOSHARED (I) ON X(I) do i=1,1024 x(i) = func(i) enddo Первая прагма (комментарий, начинающиеся с символов "CDIR$" в первой колонке) в разделе описаний указывает компилятору, что элементы массива x должны быть распределены между процессами. Вторая прагма указывает, что действия по выполнению цикла должны быть распределены между процессами так, как были распределены элементы массива. Т.е. каждый процесс будет обрабатывать только свои локальные элементы массива. В ЭВМ Cray-T3D каждый процесс (=процессор) может обращаться к любым элементам распределенных (shared) массивов, но обращение к элементам, хранящимся в памяти самого процессора, очень эффективно (как к любым своим локальным переменным), а обращение к элементам, хранящимся в памяти других процессоров, требует заметного времени. Поэтому все циклы по обработке распределенных иассивов должны быть аналогичным образом распределены между процессорами. При написании прагм программист может и не знать, что в машинный код попадают дополнительные команды, направленные на распараллеливание программы, пересылку данных и синхронизацию процессов. Программист пишет параллельную программу почти так же, как обычную последовательную. Он может подразумевать, что везде выполняется один процесс, но только в некоторых циклах этот процесс будет выполнять меньше работы, чем последовательная версия. Принципиально другой подход к распределению данных и работы между процессами - использование специальных распараллеливающих библиотек. При использовании библиотек программист может реализовать любую (или сразу обе!) концепцию параллельного программирования - распределение данных или распределение действий. Все переменные являются локальными и программа (процесс) не имеет доступ к переменным других процессов. Программист должен явно писать обращения к подпрограммам из библиотеки для передачи и приема данных, синхронизации, распределения вычислительной работы. В то же время явное использование вызовов подпрограмм позволяет оптимально и более гибко писать программу. Библиотеки распараллеливающих подпрограмм (например MPI или PVM) являются переносимыми и позволяют использовать в качестве "супер-ЭВМ" даже кластеры ЭВМ, соединенных компьютерной сетью. Однако выбор между распараллеливанием с помощью транслятора (проще написать или адаптировать программу, но есть вероятность, что у других параллельных машин будет другой диалект языка) или библиотеки (более быстродействующие программы, переносимость между всеми супер-ЭВМ, на которых есть данные библиотеки, но программы труднее писать) надо делать исходя из конкретных задач и имеющихся (в наличии или в перспективе) супер-ЭВМ. 10.3 Классы задач с эффективной векторизацией или распараллеливанием Здесь мы опишем лишь некоторые задачи, которые можно эффективно решать на супер-ЭВМ. Сначала мы коснемся математических моделей, встречающихся во многих научных и инженерных задачах, а потом в качестве примера приведем пару научных задач, с которыми авторы непосредственно имели дело. Конечно, мы не будем приводить исходные тексты программ, но укажем схематично только главные черты параллельных алгоритмов для этих задач Обработка массивов Одномерные массивы Данные задачи встречаются довольно часто. Если значения элементов массива определяются довольно сложным выражением, а вычислять их надо многократно, то векторизация или распараллеливание цикла для вычисления элементов массива может оказаться очень эффективным. В отдельный параграф мы вынесли решение систем дифференциальных уравнений, что по своей сути также является обработкой массивов функций, производных и т.д. Но на самом деле эффективными могут также быть вычисления сверток, сумм, функций от каждого элемента массива и т.п. Конечно, не имеет смысл векторизовать или распараллеливать действия над короткими массивами кроме тех случаев, когда собственно вычисления каждого элемента занимают большое время. Двумерные массивы При исполнении вложенных циклов эффективно распараллеливаются самые внешние циклы, а векторизуются только внутренние. Однако практически все действия с матрицами (сложение, умножение, умножение на вектор, прямое произведение) могут быть выполнены быстро на супер-ЭВМ. Многие алгоритмы линейной алгебры (но не все) могут быть эффективно векторизованы и распараллелены. Некоторые библиотеки подпрограмм (например, LINPACK) существуют для параллельных и векторных машин. Совершенно неэффективно использовать векторные ЭВМ для работы с матрицами размерности 3x3. Но можно переписать алгоритм для одновременной обработки нескольких (к примеру 1000) матриц - обращение, поиск собственных чисел и т.д. При увеличении размера матриц растет эффективность работы программы, но растет и размер требуемой памяти для хранения матриц. При работе на векторной машине с небольшим (128-512 Мбайт) объемом ОЗУ это может стать причиной общего снижения ее быстродействия из-за частого обращения к дискам при записи/чтении данных. Вычисления в узлах сеток и решеток Инженерные и научные задачи Во многих областях знания встречаются задачи, которые сводятся к вычислению эволюции объектов, расположенных в дискретных точках и взаимодействующих с ближайшими соседями. Простейшей и, наверно, наиболее широко известной такой задачей является игра "жизнь". Все игровое поле представляет двумерную сетку с квадратными ячейками. Каждая ячейка может быть в одном из двух состояний - пустая или живая. Если на каком-то шаге игры вокруг пустого поля существуют ровно три живых поля, то на следующем шаге игры в этом поле рождается жизнь. Если число живых полей вокруг пустого поля не равно трем, то в это поле остается пустым. Если вокруг живого поля существуют два или три других живых поля, то жизнь в в этом поле продолжается. Если число живых соседей отлично от двух или трех, то поле погибает (становится пустым). Начальная конфигурация выбирается произвольным образом. Игровое поле может иметь края или может быть свернуто в тор. Вот пример конфигурации: o o o Обозначения: o x o Oo - пустые O X O Xx - живые o x o O - родится, o - останется o o o X - останется, x - погибнет Пример игры реализован в качестве заставки на дисплее в системе OpenWindows на ЭВМ фирмы SUN. В игре "жизнь" реализуется простейшая модель "взаимодействия" пустых и живых ячеек с соседями, определяющая эволюцию всей системы. Но вот пример из физики, который по способу построения модели идентичен этой игре. Модель магнетиков Изинга представляет собой набор спинов (элементарных магнитов), расположенных в узлах решетки и взаимодействующих только с ближайшими соседями. Вычислительный интерес представляют трех- и четырехмерные модели. Двумерные Изинговские магнетики с анизотропными (зависящими от направления) дальними взаимодействиями (т.е. не только с ближайшими, но и через один узел) также нельзя исследовать аналитически, а только с помощью компьютерных экспериментов. Очевидно, что алгоритм построения эволюции Изинговских магнетиков будет во многом идентичен алгоритму игры. Многие молекулярные модели сплошных сред в статистической физике также строятся по принципу узлов (или ячеек) в кубической решетке. Молекулы могут располагаться только в ячейках. Взаимодействия молекул в соседних ячейках определяют их ориентацию (для несферических молекул) и предпочтительность (вероятность) переходов в соседние пустые ячейки. В задачах по динамике сплошных сред (аэро- и гидродинамика) также применяется метод разбиения всего пространства на маленькие объемы и моделирование перемещения этих объемов при движении твердых тел или при обтекании средой неподвижных предметов. Инженерные расчеты по распределению нагрузок в сложных конструкциях также могут проводиться в рамках сеточных моделей. Всю конструкцию можно рассматривать как сетку, в которой каждое соединение узлов имеет определенные силовые коэффициенты и предельные нагрузки на сжатие и растяжение. Таким образом можно моделировать деформацию конструкции и предельную нагрузочную способность при приложении силы в любой точке конструкции. Лекция 11. Моделирование и анализ параллельных вычислений При разработке параллельных алгоритмов решения сложных научно-технических задач принципиальным моментом является анализ эффективности использования параллелизма, состоящий обычно в оценке получаемого ускорения процесса вычислений (сокращения времени решения задачи). Формирование подобных оценок ускорения может осуществляться применительно к выбранному вычислительному алгоритму (оценка эффективности распараллеливания конкретного алгоритма). Другой важный подход состоит в построении оценок максимально возможного ускорения процесса решения задачи конкретного типа (оценка эффективности параллельного способа решения задачи). В данной лекции рассматривается модель вычислений в виде графа "операции – операнды", которая может использоваться для описания существующих информационных зависимостей в выбираемых алгоритмах решения задач, и приводятся оценки эффективности максимально возможного параллелизма, которые могут быть получены в результате анализа имеющихся моделей вычислений. Примеры применения излагаемого теоретического материала приводятся в лекциях 6 – 11 настоящего учебного пособия при изучении параллельных методов решения ряда сложных вычислительно трудоемких задач. 11.1. Модель вычислений в виде графа "операции – операнды" Для описания существующих информационных зависимостей в выбираемых алгоритмах решения задач может быть использована модель в виде графа "операции – операнды" (см., например, [2, 22]). Для уменьшения сложности излагаемого материала при построении модели будет предполагаться, что время выполнения любых вычислительных операций является одинаковым и равняется 1 (в тех или иных единицах измерения). Кроме того, принимается, что передача данных между вычислительными устройствами выполняется мгновенно без каких-либо затрат времени (что может быть справедливо, например, при наличии общей разделяемой памяти в параллельной вычислительной системе). Анализ коммуникационной трудоемкости параллельных алгоритмов приводится в следующей лекции. Представим множество операций, выполняемых в исследуемом алгоритме решения вычислительной задачи, и существующие между операциями информационные зависимости в виде ациклического ориентированного графа G = (V, R), Рис. 2.1. Пример вычислительной модели алгоритма в виде графа "операции – операнды" где V = {1,...,|V|} есть множество вершин графа, представляющих выполняемые операции алгоритма, а R есть множество дуг графа (при этом дуга r = (i, j) принадлежит графу только в том случае, если операция j использует результат выполнения операции i ). Для примера на рис. 2.1 показан граф алгоритма вычисления площади прямоугольника, заданного координатами двух противолежащих углов. Как можно заметить по приведенному примеру, для выполнения выбранного алгоритма решения задачи могут быть использованы разные схемы вычислений и построены соответственно разные вычислительные модели. Как будет показано далее, разные схемы вычислений обладают различными возможностями для распараллеливания и, тем самым, при построении модели вычислений может быть поставлена задача выбора наиболее подходящей для параллельного исполнения вычислительной схемы алгоритма. В рассматриваемой вычислительной модели алгоритма вершины без входных дуг могут использоваться для задания операций ввода, а вершины без выходных дуг – для операций вывода. Обозначим через V множество вершин графа без вершин ввода, а через d(G) — диаметр (длину максимального пути) графа. 11.2. Описание схемы параллельного выполнения алгоритма Операции алгоритма, между которыми нет пути в рамках выбранной схемы вычислений, могут быть выполнены параллельно (для вычислительной схемы на рис. 2.1, например, параллельно могут быть реализованы сначала все операции умножения, а затем первые две операции вычитания). Возможный способ описания параллельного выполнения алгоритма может состоять в следующем (см., например, [2, 22]). Пусть p есть количество процессоров, используемых для выполнения алгоритма. Тогда для параллельного выполнения вычислений необходимо задать множество ( расписание ) в котором для каждой операции указывается номер используемого для выполнения операции процессора Pi и время начала выполнения операции ti. Для того чтобы расписание было реализуемым, необходимо выполнение следующих требований при задании множества Hp: 1. , т.е. один и тот же процессор не должен назначаться разным операциям в один и тот же момент; 2. , т.е. к назначаемому моменту выполнения операции все необходимые данные уже должны быть вычислены. 11.3. Определение времени выполнения параллельного алгоритма Вычислительная схема алгоритма G совместно с расписанием Hp может рассматриваться как модель параллельного алгоритма Ap(G,Hp), исполняемого с использованием p процессоров. Время выполнения параллельного алгоритма определяется максимальным значением времени, применяемым в расписании Для выбранной схемы вычислений желательно использование расписания, обеспечивающего минимальное время исполнения алгоритма Уменьшение времени выполнения может быть обеспечено и путем подбора наилучшей вычислительной схемы Оценки Tp(G,Hp), Tp(G) и Tp могут быть применены в качестве показателей времени выполнения параллельного алгоритма. Кроме того, для анализа максимально возможного параллелизма можно определить оценку наиболее быстрого исполнения алгоритма Оценку можно рассматривать как минимально возможное время выполнения параллельного алгоритма при использовании неограниченного количества процессоров (концепция вычислительной системы с бесконечным количеством процессоров, обычно называемой паракомпьютером, широко применяется при теоретическом анализе параллельных вычислений). Оценка T1 определяет время выполнения алгоритма при использовании одного процессора и представляет, тем самым, время выполнения последовательного варианта алгоритма решения задачи. Построение подобной оценки является важной задачей при анализе параллельных алгоритмов, поскольку она необходима для определения эффекта использования параллелизма (ускорения времени решения задачи). Очевидно, что где , напомним, есть количество вершин вычислительной схемы без вершин ввода. Важно отметить, что если при определении оценки ограничиться рассмотрением только одного выбранного алгоритма решения задачи и использовать величину то получаемые при такой оценке показатели ускорения будут характеризовать эффективность распараллеливания выбранного алгоритма. Для оценки эффективности параллельного решения исследуемой вычислительной задачи время последовательного решения следует определять с учетом различных последовательных алгоритмов, т.е. использовать величину где операция минимума берется по множеству всех возможных последовательных алгоритмов решения данной задачи. Приведем без доказательства теоретические положения, характеризующие свойства оценок времени выполнения параллельного алгоритма (см. [22]). Теорема 1. Минимально возможное время выполнения параллельного алгоритма определяется длиной максимального пути вычислительной схемы алгоритма, т.е. Теорема 2. Пусть для некоторой вершины вывода в вычислительной схеме алгоритма существует путь из каждой вершины ввода. Кроме того, пусть входная степень вершин схемы (количество входящих дуг) не превышает 2. Тогда минимально возможное время выполнения параллельного алгоритма ограничено снизу значением где n есть количество вершин ввода в схеме алгоритма. Теорема 3. При уменьшении числа используемых процессоров время выполнения алгоритма увеличивается пропорционально величине уменьшения количества процессоров, т.е. Теорема 4. Для любого количества используемых процессоров справедлива следующая верхняя оценка для времени выполнения параллельного алгоритма Теорема 5. Времени выполнения алгоритма, которое сопоставимо с минимально возможным временем , можно достичь при количестве процессоров порядка , а именно, При меньшем количестве процессоров время выполнения алгоритма не может превышать более чем в 2 раза наилучшее время вычислений при имеющемся числе процессоров, т.е. Приведенные утверждения позволяют дать следующие рекомендации по правилам формирования параллельных алгоритмов: 1. при выборе вычислительной схемы алгоритма должен использоваться граф с минимально возможным диаметром (см. теорему 1); 2. для параллельного выполнения целесообразное количество процессоров определяется величиной (см. теорему 5); 3. время выполнения параллельного алгоритма ограничивается сверху величинами, приведенными в теоремах 4 и 5. Для вывода рекомендаций по формированию расписания по параллельному выполнению алгоритма приведем доказательство теоремы 4. Доказательство теоремы 4. Пусть есть расписание для достижения минимально возможного времени выполнения . Для каждой итерации , выполнения расписания обозначим через количество операций, выполняемых в ходе итерации Расписание выполнения алгоритма с использованием p процессоров может быть построено следующим образом. Выполнение алгоритма разделим на шагов; на каждом шаге следует выполнить все операций, которые выполнялись на итерации расписания . Эти операции могут быть выполнены не более чем за итераций при использовании p процессоров. Как результат, время выполнения алгоритма Tp может быть оценено следующим образом Доказательство теоремы дает практический способ построения расписания параллельного алгоритма. Первоначально может быть построено расписание без учета ограниченности числа используемых процессоров ( расписание для паракомпьютера ). Затем, согласно схеме вывода теоремы, может быть построено расписание для конкретного количества процессоров. 11.4. Показатели эффективности параллельного алгоритма Ускорение ( speedup ), получаемое при использовании параллельного алгоритма для p процессоров, по сравнению с последовательным вариантом выполнения вычислений определяется величиной Sp(n)=T1(n)/Tp(n), т.е. как отношение времени решения задач на скалярной ЭВМ к времени выполнения параллельного алгоритма (величина n применяется для параметризации вычислительной сложности решаемой задачи и может пониматься, например, как количество входных данных задачи). Эффективность ( efficiency ) использования параллельным алгоритмом процессоров при решении задачи определяется соотношением Ep(n)=T1(n)/(pTp(n))=Sp(n)/p (величина эффективности определяет среднюю долю времени выполнения алгоритма, в течение которой процессоры реально задействованы для решения задачи). Из приведенных соотношений можно показать, что в наилучшем случае Sp(n)=p и Ep(n)=1. При практическом применении данных показателей для оценки эффективности параллельных вычислений следует учитывать два важных момента: • При определенных обстоятельствах ускорение может оказаться больше числа используемых процессоров Sp(n)>p - в этом случае говорят о существовании сверхлинейного (superlinear) ускорения. Несмотря на парадоксальность таких ситуаций ( ускорение превышает число процессоров), на практике сверхлинейное ускорение может иметь место. Одной из причин такого явления может быть неодинаковость условий выполнения последовательной и параллельной программ. Например, при решении задачи на одном процессоре оказывается недостаточно оперативной памяти для хранения всех обрабатываемых данных и тогда становится необходимым использование более медленной внешней памяти (в случае же использования нескольких процессоров оперативной памяти может оказаться достаточно за счет разделения данных между процессорами). Еще одной причиной сверхлинейного ускорения может быть нелинейный характер зависимости сложности решения задачи от объема обрабатываемых данных. Так, например, известный алгоритм пузырьковой сортировки характеризуется квадратичной зависимостью количества необходимых операций от числа упорядочиваемых данных. Как результат, при распределении сортируемого массива между процессорами может быть получено ускорение, превышающее число процессоров (более подробно данный пример рассматривается в "Параллельные методы сортировки" ). Источником сверхлинейного ускорения может быть и различие вычислительных схем последовательного и параллельного методов, • При внимательном рассмотрении можно обратить внимание, что попытки повышения качества параллельных вычислений по одному из показателей (ускорению или эффективности) могут привести к ухудшению ситуации по другому показателю, ибо показатели качества параллельных вычислений являются часто противоречивыми. Так, например, повышение ускорения обычно может быть обеспечено за счет увеличения числа процессоров, что приводит, как правило, к падению эффективности. И наоборот, повышение эффективности достигается во многих случаях при уменьшении числа процессоров (в предельном случае идеальная эффективность Ep(n)=1 легко обеспечивается при использовании одного процессора). Как результат, разработка методов параллельных вычислений часто предполагает выбор некоторого компромиссного варианта с учетом желаемых показателей ускорения и эффективности. При выборе надлежащего параллельного способа решения задачи может оказаться полезной оценка стоимости (cost) вычислений, определяемой как произведение времени параллельного решения задачи и числа используемых процессоров Cp=pTp. В связи с этим можно определить понятие стоимостно-оптимального (cost-optimal) параллельного алгоритма как метода, стоимость которого является пропорциональной времени выполнения наилучшего последовательного алгоритма. Далее для иллюстрации введенных понятий в следующем пункте будет рассмотрен учебный пример решения задачи вычисления частных сумм для последовательности числовых значений. Кроме того, данные показатели будут использоваться для характеристики эффективности всех рассматриваемых в лекциях 6 – 11 параллельных алгоритмов при решении ряда типовых задач вычислительной математики. 11.5. Учебный пример. Вычисление частных сумм последовательности числовых значений Рассмотрим для демонстрации ряда проблем, возникающих при разработке параллельных методов вычислений, сравнительно простую задачу нахождения частных сумм последовательности числовых значений где n есть количество суммируемых значений (данная задача известна также под названием prefix sum problem ). Изучение возможных параллельных методов решения данной задачи начнем с еще более простого варианта ее постановки – с задачи вычисления общей суммы имеющегося набора значений (в таком виде задача суммирования является частным случаем общей задачи редукции ) 11.5.1. Последовательный алгоритм суммирования Традиционный алгоритм для решения этой задачи состоит в последовательном суммировании элементов числового набора S=0, S=S+x1,... Вычислительная схема данного алгоритма может быть представлена следующим образом (см. рис. 2.2): G1=(V1,R1), где V1={v01,...,v0n, v11,...,v1n} есть множество операций (вершины v01,...,v0n обозначают операции ввода, каждая вершина v1i, 1<=i<=n, соответствует прибавлению значения xi к накапливаемой сумме S ), а R1={(v0i,v1i),(v1i,v1i+1), 1<=i<=n–1} есть множество дуг, определяющих информационные зависимости операций. Рис. 2.2. Последовательная вычислительная схема алгоритма суммирования Как можно заметить, данный "стандартный" алгоритм суммирования допускает только строго последовательное исполнение и не может быть распараллелен. 11.5.2. Каскадная схема суммирования Параллелизм алгоритма суммирования становится возможным только при ином способе построения процесса вычислений, основанном на использовании ассоциативности операции сложения. Получаемый новый вариант суммирования (известный в литературе как каскадная схема ) состоит в следующем (см. рис. 2.3): • на первой итерации каскадной схемы все исходные данные разбиваются на пары, и для каждой пары вычисляется сумма их значений; • далее все полученные суммы также разбиваются на пары, и снова выполняется суммирование значений пар и т.д. Данная вычислительная схема может быть определена как граф (пусть n=2k ) G2(V2,R2), Рис. 2.3. Каскадная схема алгоритма суммирования где V2={(vi1,...,vli), 0<=i<=k, 1<=li<=2-1n} есть вершины графа ( (v01,...v0n) - операции ввода, (v1l,...,v1n/2) - операции суммирования первой итерации и т.д.), а множество дуг графа определяется соотношениями: R2={(vi-1,2j-1vij),(vi-1,2jvij), 1<=i<=k, 1<=j<=2-in}. Как нетрудно оценить, количество итераций каскадной схемы оказывается равным величине k=log2n, а общее количество операций суммирования Kпосл=n/2+n/4+...+1=n–1 совпадает с количеством операций последовательного варианта алгоритма суммирования. При параллельном исполнении отдельных итераций каскадной схемы общее количество параллельных операций суммирования является равным Kпар=log2n. Поскольку считается, что время выполнения любых вычислительных операций является одинаковым и единичным, то T1=Kпосл, Tp=Kпар, поэтому показатели ускорения и эффективности каскадной схемы алгоритма суммирования можно оценить как Sp=T1/Tp=(n–1)/log2n, Ep=T1/pTp=(n–1)/(plog2n)=(n–1)/((n/2)log2n), где p=n/2 есть необходимое для выполнения каскадной схемы количество процессоров. Анализируя полученные характеристики, можно отметить, что время параллельного выполнения каскадной схемы совпадает с оценкой для паракомпьютера в теореме 2. Однако при этом эффективность использования процессоров уменьшается при увеличении количества суммируемых значений 11.5.3. Модифицированная каскадная схема Получение асимптотически ненулевой эффективности может быть обеспечено, например, при использовании модифицированной каскадной схемы (см. [22]). Для упрощения построения оценок можно предположить n=2k, k=2s. Тогда в новом варианте каскадной схемы все вычисления производятся в два последовательно выполняемых этапа суммирования (см. рис. 2.4): • на первом этапе вычислений все суммируемые значения подразделяются на (n/log2n ) групп, в каждой из которых содержится log2n элементов; далее для каждой группы вычисляется сумма значений при помощи последовательного алгоритма суммирования; вычисления в каждой группе могут выполняться независимо друг от друга (т.е. параллельно – для этого необходимо наличие не менее (n/log2n ) процессоров); • на втором этапе для полученных (n/log2n) сумм отдельных групп применяется обычная каскадная схема. Рис. 2.4. Модифицированная каскадная схема суммирования Тогда для выполнения первого этапа требуется log2n параллельных операций при использовании p1=(n/log2n) процессоров. Для выполнения второго этапа необходимо log2(n/log2n)<=log2n параллельных операций для p2=(n/log2n)/2 процессоров. Как результат, данный способ суммирования характеризуется следующими показателями: Tp=2log2n, p=(n/log2n). С учетом полученных оценок показатели ускорения и эффективности модифицированной каскадной схемы определяются соотношениями: Sp=T1/Tp=(n–1)/2log2n, Ep=T1/pTp=(n–1)/(2(n/log2n)log2n)=(n–1)/2n. Сравнивая данные оценки с показателями обычной каскадной схемы, можно отметить, что ускорение для предложенного параллельного алгоритма уменьшилось в 2 раза, однако для эффективности нового метода суммирования можно получить асимптотически ненулевую оценку снизу Можно отметить также, что данные значения показателей достигаются при количестве процессоров, определенном в теореме 5. Кроме того, необходимо подчеркнуть, что, в отличие от обычной каскадной схемы, модифицированный каскадный алгоритм является стоимостно-оптимальным, поскольку стоимость вычислений в этом случае Cp=pTp=(n/log2n)(2log2n) является пропорциональной времени выполнения последовательного алгоритма. 11.5.4. Вычисление всех частных сумм Вернемся к исходной задаче вычисления всех частных сумм последовательности значений и проведем анализ возможных способов последовательной и параллельной организации вычислений. Вычисление всех частных сумм на скалярном компьютере может быть получено при помощи обычного последовательного алгоритма суммирования при том же количестве операций (!) T1=n. При параллельном исполнении применение каскадной схемы в явном виде не приводит к желаемым результатам; достижение эффективного распараллеливания требует привлечения новых подходов (может быть, даже не имеющих аналогов при последовательном программировании) для разработки новых параллельно-ориентированных алгоритмов решения задач. Так, для рассматриваемой задачи нахождения всех частных сумм алгоритм, обеспечивающий получение результатов за log2n параллельных операций (как и в случае вычисления общей суммы), может состоять в следующем (см. рис. 2.5, а также [22]): • перед началом вычислений создается копия S вектора суммируемых значений (S=x); • далее на каждой итерации суммирования i, 1<=i<=log2n, формируется вспомогательный вектор Q путем сдвига вправо вектора S на 2i-1 позиций (освобождающиеся при сдвиге позиции слева устанавливаются в нулевые значения); итерация алгоритма завершается параллельной операцией суммирования векторов S и Q. Рис. 2.5. Схема параллельного алгоритма вычисления всех частных сумм (величины Si-j означают суммы значений от i до j элементов числовой последовательности) Всего параллельный алгоритм выполняется за log2n параллельных операций сложения. На каждой итерации алгоритма параллельно выполняются n скалярных операций сложения и, таким образом, общее количество скалярных операций определяется величиной Kпар=nlog2n (параллельный алгоритм содержит большее (!) количество операций по сравнению с последовательным способом суммирования). Необходимое количество процессоров определяется количеством суммируемых значений ( p=n ). С учетом полученных соотношений показатели ускорения и эффективности параллельного алгоритма вычисления всех частных сумм оцениваются следующим образом: Sp=T1/Tp=n/log2n, Ep=T1/pTp=n/(plog2n)=n/(nlog2n)=1/log2n. Как следует из построенных оценок, эффективность алгоритма также уменьшается при увеличении числа суммируемых значений, и при необходимости повышения величины этого показателя может оказаться полезной модификация алгоритма, как и в случае с обычной каскадной схемой. 11.6. Оценка максимально достижимого параллелизма Оценка качества параллельных вычислений предполагает знание наилучших (максимально достижимых) значений показателей ускорения и эффективности, однако получение идеальных величин Sp=p для ускорения и Ep=1 для эффективности может быть обеспечено не для всех вычислительно трудоемких задач. Так, для рассматриваемого учебного примера в предыдущем пункте минимально достижимое время параллельного вычисления суммы числовых значений составляет log2n. Определенное содействие в решении этой проблемы могут оказать теоретические утверждения, приведенные в начале данной лекции. В дополнение к ним рассмотрим еще ряд закономерностей, которые могут быть чрезвычайно полезны при построении оценок максимально достижимого параллелизма. 1. Закон Амдаля. Достижению максимального ускорения может препятствовать существование в выполняемых вычислениях последовательных расчетов, которые не могут быть распараллелены. Пусть f есть доля последовательных вычислений в применяемом алгоритме обработки данных, тогда в соответствии с законом Амдаля ( Amdahl ) ускорение процесса вычислений при использовании p процессоров ограничивается величиной Так, например, при наличии всего 10% последовательных команд в выполняемых вычислениях эффект использования параллелизма не может превышать 10-кратного ускорения обработки данных. В рассмотренном учебном примере вычисления суммы значений для каскадной схемы доля последовательных расчетов составляет f=log2n/n и, как результат, величина возможного ускорения ограничена оценкой S*=n/log2n. Закон Амдаля характеризует одну из самых серьезных проблем в области параллельного программирования (алгоритмов без определенной доли последовательных команд практически не существует). Однако часто доля последовательных действий характеризует не возможность параллельного решения задач, а последовательные свойства применяемых алгоритмов. Поэтому доля последовательных вычислений может быть существенно снижена при выборе более подходящих для распараллеливания методов. Следует отметить также, что рассмотрение закона Амдаля происходит в предположении, что доля последовательных расчетов f является постоянной величиной и не зависит от параметра n, определяющего вычислительную сложность решаемой задачи. Однако для большого ряда задач доля f=f(n) является убывающей функцией от n, и в этом случае ускорение для фиксированного числа процессоров может быть увеличено за счет увеличения вычислительной сложности решаемой задачи. Данное замечание может быть сформулировано как утверждение, что ускорение Sp=Sp(n) является возрастающей функцией от параметра n (данное утверждение часто именуется эффект Амдаля ). Так, например, для учебного примера вычисления суммы значений при использовании фиксированного числа процессоров p суммируемый набор данных может быть разделен на блоки размера n/p, для которых сначала параллельно могут быть вычислены частные суммы, а далее эти суммы можно сложить при помощи каскадной схемы. Длительность последовательной части выполняемых операций (минимально возможное время параллельного исполнения) в этом случае составляет Tp=(n/p)+log2p, что приводит к оценке доли последовательных расчетов как величины f=(1/p)+log2p/n. Как следует из полученного выражения, доля последовательных расчетов f убывает с ростом n и в предельном случае мы получаемом идеальную оценку максимально возможного ускорения S*=p. 2. Закон Густавсона – Барсиса. Оценим максимально достижимое ускорение исходя из имеющейся доли последовательных расчетов в выполняемых параллельных вычислениях: где и есть времена последовательной и параллельной частей выполняемых вычислений соответственно, т.е. С учетом введенной величины g можно получить что позволяет построить оценку для ускорения которая после упрощения приводится к виду закона Густавсона – Барсиса ( Gustafson – Barsis's law ) (см. [63]): Sp = g+(1–g)p = p+(1–p)g. Применительно к учебному примеру суммирования значений при использовании p процессоров время параллельного выполнения, как уже отмечалось выше, составляет Tp = (n/p)+log2p, что соответствует последовательной доле За счет увеличения числа суммируемых значений величина g может быть пренебрежимо малой, обеспечивая получение идеального возможного ускорения Sp=p. При рассмотрении закона Густавсона – Барсиса следует учитывать еще один важный момент. С увеличением числа используемых процессоров темп уменьшения времени параллельного решения задач может падать (после превышения определенного порога). Однако за счет уменьшения времени вычислений сложность решаемых задач может быть увеличена (так, например, для учебной задачи суммирования может быть увеличен размер складываемого набора значений). Оценку получаемого при этом ускорения можно определить при помощи сформулированных закономерностей. Такая аналитическая оценка тем более полезна, поскольку решение таких более сложных вариантов задач на одном процессоре может оказаться достаточно трудоемким и даже невозможным, например, в силу нехватки оперативной памяти. С учетом указанных обстоятельств оценку ускорения, получаемую в соответствии с законом Густавсона – Барсиса, еще называют ускорением масштабирования ( scaled speedup ), поскольку данная характеристика может показать, насколько эффективно могут быть организованы параллельные вычисления при увеличении сложности решаемых задач. 11.7. Анализ масштабируемости параллельных вычислений Целью применения параллельных вычислений во многих случаях является не только уменьшение времени выполнения расчетов, но и обеспечение возможности решения более сложных вариантов задач (таких постановок, решение которых не представляется возможным при использовании однопроцессорных вычислительных систем). Способность параллельного алгоритма эффективно использовать процессоры при повышении сложности вычислений является важной характеристикой выполняемых расчетов. Поэтому параллельный алгоритм называют масштабируемым ( scalable ), если при росте числа процессоров он обеспечивает увеличение ускорения при сохранении постоянного уровня эффективности использования процессоров. Возможный способ характеристики свойств масштабируемости состоит в следующем. Оценим накладные расходы ( total overhead ), которые имеют место при выполнении параллельного алгоритма T0=pTp–T1. Накладные расходы появляются за счет необходимости организации взаимодействия процессоров, выполнения некоторых дополнительных действий, синхронизации параллельных вычислений и т.п. Используя введенное обозначение, можно получить новые выражения для времени параллельного решения задачи и соответствующего ускорения: Применяя полученные соотношения, эффективность использования процессоров можно выразить как Последнее выражение показывает, что если сложность решаемой задачи является фиксированной ( T1=const ), то при росте числа процессоров эффективность, как правило, будет убывать за счет роста накладных расходов T0. При фиксации числа процессоров эффективность их использования можно улучшить путем повышения сложности решаемой задачи T1 (предполагается, что при росте параметра сложности n накладные расходы T0 увеличиваются медленнее, чем объем вычислений T1 ). Как результат, при увеличении числа процессоров в большинстве случаев можно обеспечить определенный уровень эффективности при помощи соответствующего повышения сложности решаемых задач. Поэтому важной характеристикой параллельных вычислений становится соотношение необходимых темпов роста сложности расчетов и числа используемых процессоров. Пусть E=const есть желаемый уровень эффективности выполняемых вычислений. Из выражения для эффективности можно получить Порождаемую последним соотношением зависимость n=F(p) между сложностью решаемой задачи и числом процессоров обычно называют функцией изоэффективности ( isoefficiency function ) (см. [51]). Покажем в качестве иллюстрации вывод функции изоэффективности для учебного примера суммирования числовых значений. В этом случае и функция изоэффективности принимает вид Как результат, например, при числе процессоров p=16 для обеспечения уровня эффективности E=0,5 (т.е. K=1 ) количество суммируемых значений должно быть не менее n=64. Или же, при увеличении числа процессоров с p до q (q>p) для обеспечения пропорционального роста ускорения (Sq/Sp)=(q/p) необходимо увеличить число суммируемых значений n в (qlog2q)/(plog2p) раз. Лекция 12: Оценка коммуникационной трудоемкости параллельных алгоритмов 12.1. Общая характеристика механизмов передачи данных 12.1.1. Алгоритмы маршрутизации Алгоритмы маршрутизации определяют путь передачи данных от процессора – источника сообщения до процессора, к которому сообщение должно быть доставлено. Среди возможных способов решения данной задачи различают: • оптимальные, определяющие всегда наикратчайшие пути передачи данных, и неоптимальные алгоритмы маршрутизации ; • детерминированные и адаптивные методы выбора маршрутов (адаптивные алгоритмы определяют пути передачи данных в зависимости от существующей загрузки коммуникационных каналов). К числу наиболее распространенных оптимальных алгоритмов относится класс методов покоординатной маршрутизации ( dimension-ordered routing ), в которых поиск путей передачи данных осуществляется поочередно для каждой размерности топологии сети коммуникации. Так, для двумерной решетки такой подход приводит к маршрутизации, при которой передача данных сначала выполняется по одному направлению (например, по горизонтали до достижения вертикали, на которой располагается процессор назначения), а затем данные передаются вдоль другого направления (данная схема известна под названием алгоритма XY-маршрутизации ). Для гиперкуба покоординатная схема маршрутизации может состоять, например, в циклической передаче данных процессору, определяемому первой различающейся битовой позицией в номерах процессоров — того, на котором сообщение располагается в данный момент времени, и того, на который оно должно быть передано. 12.1.2. Методы передачи данных Время передачи данных между процессорами определяет коммуникационную составляющую ( communication latency ) длительности выполнения параллельного алгоритма в многопроцессорной вычислительной системе. Основной набор параметров, описывающих время передачи данных, состоит из следующего ряда величин: • время начальной подготовки ( tн ) характеризует длительность подготовки сообщения для передачи, поиска маршрута в сети и т. п.; • время передачи служебных данных ( tс ) между двумя соседними процессорами (т.е. для процессоров, между которыми имеется физический канал передачи данных). К служебным данным может относиться заголовок сообщения, блок данных для обнаружения ошибок передачи и т. п.; • время передачи одного слова данных по одному каналу передачи данных ( tк ). Длительность подобной передачи определяется полосой пропускания коммуникационных каналов в сети. К числу наиболее распространенных методов передачи данных относятся два основных способа коммуникации (см., например, [51]). Первый из них ориентирован на передачу сообщений ( метод передачи сообщений или МПС ) как неделимых (атомарных) блоков информации ( store-and-forward routing или SFR ). При таком подходе процессор, содержащий сообщение для передачи, готовит весь объем данных для передачи, определяет процессор, которому следует направить данные, и запускает операцию пересылки данных. Процессор, которому направлено сообщение, в первую очередь осуществляет прием полностью всех пересылаемых данных и только затем приступает к пересылке принятого сообщения далее по маршруту. Время пересылки данных tпд для метода передачи сообщения размером m байт по маршруту длиной l определяется выражением: ( 3.1) При достаточно длинных сообщениях временем передачи служебных данных можно пренебречь и выражение для времени передачи данных может быть записано в более простом виде: ( 3.2) Второй способ коммуникации основывается на представлении пересылаемых сообщений в виде блоков информации меньшего размера – пакетов, в результате чего передача данных может быть сведена к передаче пакетов ( метод передачи пакетов или МПП ). При таком методе коммуникации ( cut-through routing или CTR ) принимающий процессор может осуществлять пересылку данных по дальнейшему маршруту непосредственно сразу после приема очередного пакета, не дожидаясь завершения приема данных всего сообщения. Время пересылки данных при использовании метода передачи пакетов определяется выражением: ( 3.3) Сравнивая полученные выражения, можно заметить, что в большинстве случаев метод передачи пакетов приводит к более быстрой пересылке данных; кроме того, данный подход снижает потребность в памяти для хранения пересылаемых данных при организации приема-передачи сообщений, а для передачи пакетов могут использоваться одновременно разные коммуникационные каналы. С другой стороны, реализация пакетного метода требует разработки более сложного аппаратного и программного обеспечения сети, может увеличить накладные расходы (время подготовки и время передачи служебных данных). Кроме того, при передаче пакетов возможно возникновение конфликтных ситуаций (дедлоков). 12.2. Анализ трудоемкости основных операций передачи данных При всем разнообразии выполняемых операций передачи данных при параллельных способах решения сложных научно-технических задач, определенные процедуры взаимодействия процессоров сети могут быть отнесены к числу основных коммуникационных действий, либо наиболее широко распространенных в практике параллельных вычислений, либо тех, к которым могут быть сведены многие другие процессы приема-передачи сообщений. Важно отметить также, что в рамках подобного базового набора для большинства операций коммуникации существуют процедуры, обратные по действию исходным операциям (так, например, операции передачи данных от одного процессора всем имеющимся процессорам сети соответствует операция приема в одном процессоре сообщений от всех остальных процессоров). Как результат, рассмотрение коммуникационных процедур целесообразно выполнять попарно, поскольку во многих случаях алгоритмы выполнения прямой и обратной операций могут быть получены исходя из одинаковых предпосылок. Рассмотрение основных операций передачи данных в этой лекции будет осуществляться на примере таких топологий сети, как кольцо, двумерная решетка и гиперкуб. Для двумерной решетки будет предполагаться также, что между граничными процессорами в строках и столбцах решетки имеются каналы передачи данных (т.е. топология сети представляет собой тор). Как и ранее, величина m будет означать размер сообщения в словах, значение p определяет количество процессоров в сети, а переменная N задает размерность топологии гиперкуба. 12.2.1. Передача данных между двумя процессорами сети Трудоемкость данной коммуникационной операции может быть получена путем подстановки длины максимального пути (диаметра сети) в выражения для времени передачи данных при разных методах коммуникации (см. п. 3.1.2) – см. табл. 3.1. Таблица 3.1. Время передачи данных между двумя процессорами Топология Передача сообщений Передача пакетов Кольцо Решетка-тор Гиперкуб 12.2.2. Передача данных от одного процессора всем остальным процессорам сети Операция передачи данных (одного и того же сообщения) от одного процессора всем остальным процессорам сети ( one-to-all broadcast или single-node broadcast ) является одним из наиболее часто выполняемых коммуникационных действий. Двойственная ей операция – прием на одном процессоре сообщений от всех остальных процессоров сети ( single-node accumulation ). Подобные операции используются, в частности, при реализации матрично-векторного умножения, решении систем линейных уравнений методом Гаусса, решении задачи поиска кратчайших путей и др. Простейший способ реализации операции рассылки состоит в ее выполнении как последовательности попарных взаимодействий процессоров сети. Однако при таком подходе большая часть пересылок является избыточной и возможно применение более эффективных алгоритмов коммуникации. Изложение материала будет проводиться сначала для метода передачи сообщений, затем – для пакетного способа передачи данных (см. п. 3.1.2). Передача сообщений. Для кольцевой топологии процессор – источник рассылки может инициировать передачу данных сразу двум своим соседям, которые, в свою очередь, приняв сообщение, организуют пересылку далее по кольцу. Трудоемкость выполнения операции рассылки в этом случае будет определяться соотношением: ( 3.4) Для топологии типа решетка-тор алгоритм рассылки может быть получен из способа передачи данных, примененного для кольцевой структуры сети. Так, рассылка может быть выполнена в виде двухэтапной процедуры. На первом этапе организуется передача сообщения всем процессорам сети, располагающимся на той же горизонтали решетки, что и процессор – инициатор передачи. На втором этапе процессоры, получившие копию данных на первом этапе, рассылают сообщения по своим соответствующим вертикалям. Оценка длительности операции рассылки в соответствии с описанным алгоритмом определяется соотношением: ( 3.5) Для гиперкуба рассылка может быть выполнена в ходе N-этапной процедуры передачи данных. На первом этапе процессор-источник сообщения передает данные одному из своих соседей (например, по первой размерности) – в результате после первого этапа есть два процессора, имеющих копию пересылаемых данных (данный результат можно интерпретировать также как разбиение исходного гиперкуба на два таких одинаковых по размеру гиперкуба размерности N-1, что каждый из них имеет копию исходного сообщения). На втором этапе два процессора, задействованные на первом этапе, пересылают сообщение своим соседям по второй размерности и т.д. В результате такой рассылки время операции оценивается при помощи выражения: ( 3.6) Сравнивая полученные выражения для длительности выполнения операции рассылки, можно отметить, что наилучшие показатели имеет топология типа гиперкуб; более того, можно показать, что данный результат является наилучшим для выбранного способа коммуникации с помощью передачи сообщений. Передача пакетов. Для топологии типа кольцо алгоритм рассылки может быть получен путем логического представления кольцевой структуры сети в виде гиперкуба. В результате на этапе рассылки процессор – источник сообщения передает данные процессору, находящемуся на расстоянии p/2 от исходного процессора. Далее, на втором этапе оба процессора, уже имеющие рассылаемые данные после первого этапа, передают сообщения процессорам, находящимся на расстоянии p/4, и т.д. Трудоемкость выполнения операции рассылки при таком методе передачи данных определяется соотношением: ( 3.7) (как и ранее, при достаточно больших сообщениях временем передачи служебных данных можно пренебречь). Для топологии типа решетка-тор алгоритм рассылки может быть получен из способа передачи данных, примененного для кольцевой структуры сети, в соответствии с тем же способом обобщения, что и в случае использования метода передачи сообщений. Получаемый в результате такого обобщения алгоритм рассылки характеризуется следующим соотношением для оценки времени выполнения: ( 3.8) Для гиперкуба алгоритм рассылки (и, соответственно, временные оценки длительности выполнения) при передаче пакетов не отличается от варианта для метода передачи сообщений. 12.2.3. Передача данных от всех процессоров всем процессорам сети Операция передачи данных от всех процессоров всем процессорам сети ( all-to-all broadcast или multinode broadcast ) является естественным обобщением одиночной операции рассылки, двойственная ей операция – прием сообщений на каждом процессоре от всех процессоров сети ( multinode accumulation ). Подобные операции широко используются, например, при реализации матричных вычислений. Возможный способ реализации операции множественной рассылки состоит в выполнении соответствующего набора операций одиночной рассылки. Однако такой подход не является оптимальным для многих топологий сети, поскольку часть необходимых операций одиночной рассылки потенциально может быть выполнена параллельно. Как и ранее, материал будет рассматриваться раздельно для разных методов передачи данных (см. п. 3.1.2). Передача сообщений. Для кольцевой топологии каждый процессор может инициировать рассылку своего сообщения одновременно (в каком-либо выбранном направлении по кольцу). В любой момент каждый процессор выполняет прием и передачу данных, завершение операции множественной рассылки произойдет через p-1 цикл передачи данных. Длительность выполнения операции рассылки оценивается соотношением: ( 3.9) Для топологии типа решетка-тор множественная рассылка сообщений может быть выполнена при помощи алгоритма, получаемого обобщением способа передачи данных для кольцевой структуры сети. Схема обобщения состоит в следующем. На первом этапе организуется передача сообщений раздельно по всем процессорам сети, располагающимся на одних и тех же горизонталях решетки (в результате на каждом процессоре одной и той же горизонтали формируются укрупненные сообщения размера , объединяющие все сообщения горизонтали). Время выполнения этапа: На втором этапе рассылка данных выполняется по процессорам сети, образующим вертикали решетки. Длительность этого этапа: Общая длительность операции рассылки определяется соотношением: ( 3.10) Для гиперкуба алгоритм множественной рассылки сообщений может быть получен путем обобщения ранее описанного способа передачи данных для топологии типа решетки на размерность гиперкуба N. В результате такого обобщения схема коммуникации состоит в следующем. На каждом этапе i, 1<=i<=N, выполнения алгоритма функционируют все процессоры сети, которые обмениваются своими данными со своими соседями по i -ой размерности и формируют объединенные сообщения. Время операции рассылки может быть получено при помощи выражения: ( 3.11) Передача пакетов. Применение более эффективного для кольцевой структуры и топологии типа решетка-тор метода передачи данных не приводит к какому-либо улучшению времени выполнения операции множественной рассылки, поскольку обобщение алгоритмов выполнения операции одиночной рассылки на случай множественной рассылки приводит к перегрузке каналов передачи данных (т.е. к существованию ситуаций, когда в один и тот же момент для передачи по одной и той же линии имеется несколько ожидающих пересылки пакетов данных). Перегрузка каналов приводит к задержкам при пересылках данных, что и не позволяет проявиться всем преимуществам метода передачи пакетов. Широко распространенным примером операции множественной рассылки является задача редукции ( reduction ), которая определяется в общем виде как процедура выполнения той или иной обработки данных, получаемых на каждом процессоре в ходе множественной рассылки (в качестве примера такой задачи может быть рассмотрена проблема вычисления суммы значений, находящихся на разных процессорах, и рассылки полученной суммы по всем процессорам сети). Способы решения задачи редукции могут состоять в следующем: • непосредственный подход заключается в выполнении операции множественной рассылки и последующей затем обработке данных на каждом процессоре в отдельности; • более эффективный алгоритм может быть получен в результате применения операции одиночного приема данных на отдельном процессоре, выполнения на этом процессоре действий по обработке данных и рассылки полученного результата обработки всем процессорам сети; • наилучший же способ решения задачи редукции состоит в совмещении процедуры множественной рассылки и действий по обработке данных, когда каждый процессор сразу же после приема очередного сообщения реализует требуемую обработку полученных данных (например, выполняет сложение полученного значения с имеющейся на процессоре частичной суммой). Время решения задачи редукции при таком алгоритме реализации в случае, например, когда размер пересылаемых данных имеет единичную длину (m=1) и топология сети имеет структуру гиперкуба, определяется выражением: ( 3.12) Другим типовым примером использования операции множественной рассылки является задача нахождения частных сумм последовательности значений Si (в англоязычной литературе эта задача известна под названием prefix sum problem ) ( 3.13) (будем предполагать, что количество значений совпадает с количеством процессоров, значение xi располагается на i -м процессоре и результат Sk должен получаться на процессоре с номером k ). Алгоритм решения данной задачи также может быть получен при помощи конкретизации общего способа выполнения множественной операции рассылки, когда процессор выполняет суммирование полученного значения (но только в том случае, если процессор – отправитель значения имеет меньший номер, чем процессор-получатель). 12.2.4. Обобщенная передача данных от одного процессора всем остальным процессорам сети Общий случай передачи данных от одного процессора всем остальным процессорам сети состоит в том, что все рассылаемые сообщения являются различными ( one-to-all personalized communication или single-node scatter ). Двойственная операция передачи для данного типа взаимодействия процессоров – обобщенный прием сообщений ( single-node gather ) на одном процессоре от всех остальных процессоров сети (отличие данной операции от ранее рассмотренной процедуры сборки данных на одном процессоре состоит в том, что обобщенная операция сборки не предполагает какого-либо взаимодействия сообщений (например, редукции) в процессе передачи данных). Трудоемкость операции обобщенной рассылки сопоставима со сложностью выполнения процедуры множественной передачи данных. Процессор – инициатор рассылки посылает каждому процессору сети сообщение размера m, и, тем самым, нижняя оценка длительности выполнения операции характеризуется величиной mtk(p–1). Проведем более подробный анализ трудоемкости обобщенной рассылки для случая топологии типа гиперкуб. Возможный способ выполнения операции состоит в следующем. Процессор – инициатор рассылки передает половину своих сообщений одному из своих соседей (например, по первой размерности) – в результате исходный гиперкуб становится разделенным на два гиперкуба половинного размера, в каждом из которых содержится ровно половина исходных данных. Далее, действия по рассылке сообщений могут быть повторены, и общее количество повторений определяется исходной размерностью гиперкуба. Длительность операции обобщенной рассылки может быть охарактеризована соотношением: ( 3.14) (как и отмечалась выше, трудоемкость операции совпадает с длительностью выполнения процедуры множественной рассылки). 12.2.5. Обобщенная передача данных от всех процессоров всем процессорам сети Обобщенная передача данных от всех процессоров всем процессорам сети ( total exchange ) представляет собой наиболее общий случай коммуникационных действий. Необходимость выполнения подобных операций возникает в параллельных алгоритмах быстрого преобразования Фурье, транспонирования матриц и др. Выполним краткую характеристику возможных способов выполнения обобщенной множественной рассылки для разных методов передачи данных (см. п. 3.1.2). Передача сообщений. Общая схема алгоритма для кольцевой топологии состоит в следующем. Каждый процессор производит передачу всех своих исходных сообщений своему соседу (в каком-либо выбранном направлении по кольцу). Далее процессоры осуществляют прием направленных к ним данных, затем среди принятой информации выбирают свои сообщения, после чего выполняют дальнейшую рассылку оставшейся части данных. Длительность выполнения подобного набора передач данных оценивается при помощи выражения: ( 3.15) Способ получения алгоритма рассылки данных для топологии типа решетка-тор является тем же самым, что и в случае рассмотрения других коммуникационных операций. На первом этапе организуется передача сообщений раздельно по всем процессорам сети, располагающимся на одних и тех же горизонталях решетки (каждому процессору по горизонтали передаются только те исходные сообщения, что должны быть направлены процессорам соответствующей вертикали решетки). После завершения этапа на каждом процессоре собираются p сообщений, предназначенных для рассылки по одной из вертикалей решетки. На втором этапе рассылка данных выполняется по процессорам сети, образующим вертикали решетки. Общая длительность всех операций рассылок определяется соотношением: ( 3.16) Для гиперкуба алгоритм обобщенной множественной рассылки сообщений может быть получен путем обобщения способа выполнения операции для топологии типа решетка на размерность гиперкуба N. В результате такого обобщения схема коммуникации состоит в следующем. На каждом этапе i, 1<=i<=N, выполнения алгоритма функционируют все процессоры сети, которые обмениваются своими данными со своими соседями по i -й размерности и формируют объединенные сообщения. При организации взаимодействия двух соседей канал связи между ними рассматривается как связующий элемент двух равных по размеру подгиперкубов исходного гиперкуба, и каждый процессор пары посылает другому процессору только те сообщения, что предназначены для процессоров соседнего подгиперкуба. Время операции рассылки может быть получено при помощи выражения: ( 3.17) (кроме затрат на пересылку, каждый процессор выполняет операций по сортировке своих сообщений перед обменом информацией со своими соседями). Передача пакетов. Как и в случае множественной рассылки, применение метода передачи пакетов не приводит к улучшению временных характеристик для операции обобщенной множественной рассылки. Рассмотрим как пример более подробно выполнение данной коммуникационной операции для сети с топологией типа гиперкуб. В этом случае рассылка может быть выполнена за p-1 итерацию. На каждой итерации все процессоры разбиваются на взаимодействующие пары процессоров, причем это разбиение на пары может быть выполнено таким образом, чтобы передаваемые между разными парами сообщения не использовали одни и те же пути передачи данных. Как результат, общая длительность операции обобщенной рассылки может быть определена в соответствии с выражением: ( 3.18) 12.2.6. Циклический сдвиг Частный случай обобщенной множественной рассылки есть процедура перестановки ( permutation ), представляющая собой операцию перераспределения информации между процессорами сети, в которой каждый процессор передает сообщение определенному неким способом другому процессору сети. Конкретный вариант перестановки – циклический q-сдвиг ( cirlular q-shift ), при котором каждый процессор i, 1<=i<=N, передает данные процессору с номером . Подобная операция сдвига используется, например, при организации матричных вычислений. Поскольку выполнение циклического сдвига для кольцевой топологии может быть обеспечено при помощи простых алгоритмов передачи данных, рассмотрим возможные способы выполнения данной коммуникационной операции только для топологий решетка-тор и гиперкуб при разных методах передачи данных (см. п. 3.1.2). Передача сообщений. Общая схема алгоритма циклического сдвига для топологии типа решетка-тор состоит в следующем. Пусть процессоры перенумерованы по строкам решетки от 0 до p-1. На первом этапе организуется циклический сдвиг с шагом по каждой строке в отдельности (если при реализации такого сдвига сообщения передаются через правые границы строк, то после выполнения каждой такой передачи необходимо осуществить компенсационный сдвиг вверх на 1 для процессоров первого столбца решетки). На втором этапе реализуется циклический сдвиг вверх с шагом для каждого столбца решетки. Общая длительность всех операций рассылок определяется соотношением: ( 3.19) Для гиперкуба алгоритм циклического сдвига может быть получен путем логического представления топологии гиперкуба в виде кольцевой структуры. Для получения такого представления установим взаимно-однозначное соответствие между вершинами кольца и гиперкуба. Необходимое соответствие может быть получено, например, при помощи известного кода Грея. Более подробное изложение механизма установки такого соответствия осуществляется в подразделе 3.3; для наглядности на рис. 3.1 приводится вид гиперкуба для размерности N=3 с указанием для каждого процессора гиперкуба соответствующей вершины кольца. Положительным свойством выбора такого соответствия является тот факт, что для любых двух вершин в кольце, расстояние между которыми равно l=2i для некоторого значения i, путь между соответствующими вершинами в гиперкубе содержит только две линии связи (за исключением случая i=0, когда путь в гиперкубе имеет единичную длину). Рис. 3.1. Схема отображения гиперкуба на кольцо (в кружках приведены номера процессоров гиперкуба) Представим величину сдвига q в виде двоичного кода. Количество ненулевых позиций кода определяет количество этапов в схеме реализации операции циклического сдвига. На каждом этапе выполняется операция сдвига с величиной шага, задаваемой наиболее старшей ненулевой позицией значения q (например, при исходной величине сдвига q=5=1012 на первом этапе выполняется сдвиг с шагом 4, на втором этапе шаг сдвига равен 1). Выполнение каждого этапа (кроме сдвига с шагом 1) состоит в передаче данных по пути, включающему две линии связи. Как результат, верхняя оценка для длительности выполнения операции циклического сдвига определяется соотношением: ( 3.20) Передача пакетов. Использование пересылки пакетов может повысить эффективность выполнения операции циклического сдвига для топологии гиперкуб. Реализация всех необходимых коммуникационных действий в этом случае может быть обеспечена путем отправления каждым процессором всех пересылаемых данных непосредственно процессорам назначения. Применение метода покоординатной маршрутизации (см. п. 12.1.1) позволит избежать коллизий при использовании линий передачи данных (в каждый момент времени для каждого канала будет существовать не более одного готового для отправки сообщения). Длина наибольшего пути при такой рассылке данных определяется как , где есть наибольшее целое значение j такое, что 2j есть делитель величины сдвига q. Тогда длительность операции циклического сдвига может быть охарактеризована при помощи выражения ( 3.21) (при достаточно больших размерах сообщений временем передачи служебных данных можно пренебречь и время выполнения операции может быть определено как ). 12.3. Методы логического представления топологии коммуникационной среды Как показало рассмотрение основных коммуникационных операций в подразделе 12.1, ряд алгоритмов передачи данных допускает более простое изложение при использовании вполне определенных топологий сети межпроцессорных соединений. Кроме того, многие методы коммуникации могут быть получены при помощи того или иного логического представления исследуемой топологии. Как результат, важным моментом при организации параллельных вычислений является возможность логического представления разнообразных топологий на основе конкретных (физических) межпроцессорных структур. Способы логического представления (отображения) топологий характеризуются следующими тремя основными характеристиками: • уплотнение дуг ( congestion ), выражаемое как максимальное количество дуг логической топологии, которые отображаются в одну линию передачи физической топологии; • удлинение дуг ( dilation ), определяемое как путь максимальной длины физической топологии, на который отображается дуга логической топологии; • увеличение вершин ( expansion ), вычисляемое как отношение количества вершин в логической и физической топологиях. Для рассматриваемых в рамках пособия топологий ограничимся изложением вопросов отображения топологий кольца и решетки на гиперкуб. Предлагаемые ниже подходы для логического представления топологий характеризуются единичными показателями уплотнения и удлинения дуг. 12.3.1. Представление кольцевой топологии в виде гиперкуба Установление соответствия между кольцевой топологией и гиперкубом может быть выполнено при помощи двоичного рефлексивного кода Грея G(i, N) ( binary reflected Gray code ), определяемого в соответствии с выражениями: ( 3.22) где i задает номер значения в коде Грея, а N есть длина этого кода. Для иллюстрации подхода в табл. 3.1 показывается отображение кольцевой топологии на гиперкуб для сети из p=8 процессоров. Важное свойство кода Грея: соседние значения G(i,N) и G(i+1,N) имеют только одну различающуюся битовую позицию. Как результат, соседние вершины в кольцевой топологии отображаются на соседние процессоры в гиперкубе. Таблица 3.1. Отображение кольцевой топологии на гиперкуб при помощи кода Грея Код Грея для N=1 Код Грея для N=2 Код Грея для N=3 Номера процессоров гиперкуба кольца 0 0 0 0 0 1 0 1 0 0 1 1 1 1 1 0 1 1 3 2 1 0 0 1 0 2 3 1 1 0 6 4 1 1 1 7 5 1 0 1 5 6 1 0 0 4 7 12.3.2. Отображение топологии решетки на гиперкуб Отображение топологии решетки на гиперкуб может быть выполнено в рамках подхода, использованного для кольцевой структуры сети. Тогда для отображения решетки на гиперкуб размерности N=r+s можно принять правило, что элементу решетки с координатами (i, j) соответствует процессор гиперкуба с номером: G(i,r)||G(j,s), где операция || означает конкатенацию кодов Грея. 12.4. Оценка трудоемкости операций передачи данных для кластерных систем Для кластерных вычислительных систем (см. п. 1.2.2) одним из широко применяемых способов построения коммуникационной среды является использование концентраторов ( hub ) или коммуникаторов ( switch ) для объединения процессорных узлов кластера в единую вычислительную сеть. В этих случаях топология сети кластера представляет собой полный граф, в котором, однако, имеются определенные ограничения на одновременность выполнения коммуникационных операций. Так, при использовании концентраторов передача данных в каждый текущий момент может выполняться только между двумя процессорными узлами; коммуникаторы могут обеспечивать взаимодействие нескольких непересекающихся пар процессоров. Другое часто применяемое решение при создании кластеров состоит в использовании метода передачи пакетов (часто реализуемого на основе стека протоколов TCP/IP) в качестве основного способа выполнения коммуникационных операций. Если выбрать для дальнейшего анализа кластеры данного распространенного типа (топология в виде полного графа, пакетный способ передачи сообщений), то трудоемкость операции коммуникации между двумя процессорными узлами может быть оценена в соответствии с выражением ( модель А ) ( 3.23) оценка подобного вида следует из соотношений для метода передачи пакетов при единичной длине пути передачи данных, т.е. при l=1. Отмечая возможность подобного подхода, вместе с этим можно заметить, что в рамках рассматриваемой модели время подготовки данных tн предполагается постоянным (не зависящим от объема передаваемых данных), время передачи служебных данных tс не зависит от количества передаваемых пакетов и т.п. Эти предположения не в полной мере соответствуют действительности, и временные оценки, получаемые в результате использования модели, могут не обладать необходимой точностью. С учетом приведенных замечаний, схема построения временных оценок может быть уточнена; в рамках новой расширенной модели трудоемкость передачи данных между двумя процессорами определяется в соответствии со следующими выражениями ( модель В ): ( 3.24) где есть количество пакетов, на которое разбивается передаваемое сообщение, величина Vmax определяет максимальный размер пакета, который может быть доставлен в сети (по умолчанию для операционной системы MS Windows в сети Fast Ethernet Vmax=1500 байт), а Vc есть объем служебных данных в каждом из пересылаемых пакетов (для протокола TCP/IP, ОС Windows 2000 и сети Fast Ethernet Vc=78 байт). Поясним также, что в приведенных соотношениях константа характеризует аппаратную составляющую латентности и зависит от параметров используемого сетевого оборудования, значение задает время подготовки одного байта данных для передачи по сети. Как результат, величина латентности ( 3.25) увеличивается линейно в зависимости от объема передаваемых данных. При этом предполагается, что подготовка данных для передачи второго и всех последующих пакетов может быть совмещена с пересылкой по сети предшествующих пакетов и латентность, тем самым, не может превышать величины: ( 3.26) Помимо латентности, в предлагаемых выражениях для оценки трудоемкости коммуникационной операции можно уточнить также правило вычисления времени передачи данных ( 3.27) что позволяет теперь учитывать эффект увеличения объема передаваемых данных при росте числа пересылаемых пакетов за счет добавления служебной информации (заголовков пакетов). Завершая анализ проблемы построения теоретических оценок трудоемкости коммуникационных операций, следует отметить, что для практического применения перечисленных моделей необходимо выполнить оценку значений параметров используемых соотношений. В этом отношении полезным может оказаться использование и более простых способов вычисления временных затрат на передачу данных – одной из известных схем подобного вида является подход, в котором трудоемкость операции коммуникации между двумя процессорными узлами кластера оценивается в соответствии с выражением: ( 3.28) это модель C, предложенная Хокни ( the Hockney model ) – см., например, [46]. Для проверки адекватности рассмотренных моделей реальным процессам передачи данных приведем результаты выполненных экспериментов в сети многопроцессорного кластера Нижегородского университета (компьютеры IBM PC Pentium 4 1300 MГц и сеть Fast Etherrnet). При проведении экспериментов для реализации коммуникационных операций использовалась библиотека MPI. Часть экспериментов была выполнена для оценки параметров моделей: • значение латентности tн для моделей A и C определялось как время передачи сообщения нулевой длины; • величина пропускной способности R оценивалась максимальным значением скорости передачи данных, наблюдавшимся в экспериментах, т.е. величиной и полагалось tк=1/R ; • значения величин и оценивались при помощи линейной аппроксимации времен передачи сообщений размера от 0 до Vmax. В ходе экспериментов осуществлялась передача данных между двумя узлами кластера, размер передаваемых сообщений варьировался от 0 до 8 Мб. Для получения более точных оценок выполнение каждой операции осуществлялось многократно (более 100 000 раз), после чего полученные результаты усреднялись. Для иллюстрации ниже приведен результат одного эксперимента, при проведении которого размер передаваемых сообщений изменялся от 2000 до 60 000 байт. В табл. 3.2 приводится ряд числовых данных по погрешности рассмотренных моделей трудоемкости коммуникационных операций (величина погрешности дается в виде относительного отклонения от реального времени выполнения операции передачи данных). Таблица 3.2. Погрешность моделей трудоемкости операций передачи данных (по результатам вычислительных экспериментов) Объем сообщения (байт) Время передачи (мкс) Погрешимость теоретической оценки времени передачи данных, % Модель A Модель B Модель C 2000 495 33,45 7,93 34,80 10000 1184 13,91 1,70 14,48 20000 2055 8,44 0,44 8,77 30000 2874 4,53 -1,87 4,76 40000 3758 4,04 -1,38 4,22 50000 4749 5,91 1,21 6,05 60000 5730 6,97 2,73 7,09 Как можно заметить по результатам проведенных экспериментов, оценки трудоемкости операций передачи данных по модели B имеют меньшую погрешность. Вместе с этим важно отметить, что для предварительного анализа временных затрат на выполнение коммуникационных операций точности модели C может оказаться достаточно. Кроме того, данная модель имеет наиболее простой вид среди всех рассмотренных. С учетом последнего обстоятельства, далее во всех последующих лекциях для оценки трудоемкости операций передачи данных будет применяться именно модель C (модель Хокни), при этом для модели будет использоваться форма записи, приведенная к обозначениям, которые приняты в работе Хокни [46]: ( 3.29) где есть латентность сети передачи данных (т.е. ), а обозначает пропускную способность сети (т.е. ). Лекция 13. Принципы разработки параллельных методов Разработка алгоритмов (а в особенности методов параллельных вычислений) для решения сложных научно-технических задач часто представляет собой значительную проблему. Для снижения сложности рассматриваемой темы оставим в стороне математические аспекты разработки и доказательства сходимости алгоритмов – эти вопросы в той или иной степени изучаются в ряде "классических" математических учебных курсов. Здесь же мы будем полагать, что вычислительные схемы решения задач, рассматриваемых далее в качестве примеров, уже известны1Несмотря на то, что для многих научно-технических задач на самом деле известны не только последовательные, но и параллельные методы решения, данное предположение является, конечно, очень сильным, поскольку для новых возникающих задач, требующих для своего решения большого объема вычислений, процесс разработки алгоритмов составляет существенную часть всех выполняемых работ.. С учетом высказанных предположений последующие действия для определения эффективных способов организации параллельных вычислений могут состоять в следующем: • выполнить анализ имеющихся вычислительных схем и осуществить их разделение ( декомпозицию ) на части ( подзадачи ), которые могут быть реализованы в значительной степени независимо друг от друга; • выделить для сформированного набора подзадач информационные взаимодействия, которые должны осуществляться в ходе решения исходной поставленной задачи; • определить необходимую (или доступную) для решения задачи вычислительную систему и выполнить распределение имеющего набора подзадач между процессорами системы. Рис. 4.1. Общая схема разработки параллельных алгоритмов При самом общем рассмотрении понятно, что объем вычислений для каждого используемого процессора должен быть примерно одинаков – это позволит обеспечить равномерную вычислительную загрузку ( балансировку ) процессоров. Кроме того, также понятно, что распределение подзадач между процессорами должно быть выполнено таким образом, чтобы количество информационных связей ( коммуникационных взаимодействий ) между подзадачами было минимальным. После выполнения всех перечисленных этапов проектирования можно оценить эффективность разрабатываемых параллельных методов: для этого обычно определяются значения показателей качества порождаемых параллельных вычислений ( ускорение, эффективность, масштабируемость ). По результатам проведенного анализа может оказаться необходимым повторение отдельных (в предельном случае всех) этапов разработки – следует отметить, что возврат к предшествующим шагам разработки может происходить на любой стадии проектирования параллельных вычислительных схем. Поэтому часто выполняемым дополнительным действием в приведенной выше схеме проектирования является корректировка состава сформированного множества задач после определения имеющегося количества процессоров – подзадачи могу быть укрупнены ( агрегированы ) при наличии малого числа процессоров или, наоборот, детализированы в противном случае. В целом, данные действия могут быть определены как масштабирование разрабатываемого алгоритма и выделены в качестве отдельного этапа проектирования параллельных вычислений. Чтобы применить получаемый в конечном итоге параллельный метод, необходимо выполнить разработку программ для решения сформированного набора подзадач и разместить разработанные программы по процессорам в соответствии с выбранной схемой распределения подзадач. Для проведения вычислений программы запускаются на выполнение (программы на стадии выполнения обычно именуются процессами ), для реализации информационных взаимодействий программы должны иметь в своем распоряжении средства обмена данными ( каналы передачи сообщений ). Следует отметить, что каждый процессор обычно выделяется для решения единственной подзадачи, однако при наличии большого количества подзадач или использовании ограниченного числа процессоров это правило может не соблюдаться и, в результате, на процессорах может выполняться одновременно несколько программ ( процессов ). В частности, при разработке и начальной проверке параллельной программы для выполнения всех процессов может использоваться один процессор (при расположении на одном процессоре процессы выполняются в режиме разделения времени). Рассмотрев внимательно разработанную схему проектирования и реализации параллельных вычислений, можно отметить, что данный подход в значительной степени ориентирован на вычислительные системы с распределенной памятью, когда необходимые информационные взаимодействия реализуются при помощи передачи сообщений по каналам связи между процессорами. Тем не менее данная схема может быть применена без потери эффективности параллельных вычислений и для разработки параллельных методов для систем с общей памятью – в этом случае механизмы передачи сообщений для обеспечения информационных взаимодействий должны быть заменены операциями доступа к общим (разделяемым) переменным. 13.1. Моделирование параллельных программ Рассмотренная схема проектирования и реализации параллельных вычислений дает способ понимания параллельных алгоритмов и программ. На стадии проектирования параллельный метод может быть представлен в виде графа "подзадачи – сообщения", который представляет собой не что иное, как укрупненное (агрегированное) представление графа информационных зависимостей (графа "операции – операнды" – см. "Моделирование и анализ параллельных вычислений" ). Аналогично на стадии выполнения для описания параллельной программы может быть использована модель в виде графа "процессы – каналы", в которой вместо подзадач используется понятие процессов, а информационные зависимости заменяются каналами передачи сообщений. Дополнительно на этой модели может быть показано распределение процессов по процессорам вычислительной системы, если количество подзадач превышает число процессоров – см. рис. 4.2. Рис. 4.2. Модель параллельной программы в виде графа "процессы - каналы" Использование двух моделей параллельных вычислений позволяет лучше разделить проблемы, которые проявляются при разработке параллельных методов. Первая модель – граф "подзадачи – сообщения" – позволяет сосредоточиться на вопросах выделения подзадач одинаковой вычислительной сложности, обеспечивая при этом низкий уровень информационной зависимости между подзадачами. Вторая модель – граф "процессы – каналы" – концентрирует внимание на вопросах распределения подзадач по процессорам, обеспечивая еще одну возможность снижения трудоемкости информационных взаимодействий между подзадачами за счет размещения на одних и тех же процессорах интенсивно взаимодействующих процессов. Кроме того, эта модель позволяет лучше анализировать эффективность разработанного параллельного метода и обеспечивает возможность более адекватного описания процесса выполнения параллельных вычислений. Дадим дополнительные пояснения для используемых понятий в модели "процессы – каналы": • под процессом будем понимать выполняемую на процессоре программу, которая использует для своей работы часть локальной памяти процессора и содержит ряд операций приема/передачи данных для организации информационного взаимодействия с другими выполняемыми процессами параллельной программы; • канал передачи данных с логической точки зрения может рассматриваться как очередь сообщений, в которую один или несколько процессов могут отправлять пересылаемые данные и из которой процесс -адресат может извлекать сообщения, отправляемые другими процессами. В общем случае, можно считать, что каналы возникают динамически в момент выполнения первой операции приема/передачи с каналом. По степени общности канал может соответствовать одной или нескольким командам приема данных процесса -получателя; аналогично, при передаче сообщений канал может использоваться одной или несколькими командами передачи данных одного или нескольких процессов. Для снижения сложности моделирования и анализа параллельных методов будем предполагать, что емкость каналов является неограниченной и, как результат, операции передачи данных выполняются практически без задержек простым копированием сообщений в канал. С другой стороны, операции приема сообщений могут приводить к задержкам ( блокировкам ), если запрашиваемые из канала данные еще не были отправлены процессами – источниками сообщений. Следует отметить важное достоинство рассмотренной модели "процессы – каналы" – в ней проводится четкое разделение локальных (выполняемых на отдельном процессоре) вычислений и действий по организации информационного взаимодействия одновременно выполняемых процессов. Такой подход значительно снижает сложность анализа эффективности параллельных методов и существенно упрощает проблемы разработки параллельных программ. 13.2. Этапы разработки параллельных алгоритмов Рассмотрим более подробно изложенную выше методику разработки параллельных алгоритмов. В значительной степени данная методика опирается на подход, впервые разработанный в [ [ 32 ] ], и, как отмечалось ранее, включает этапы выделения подзадач, определения информационных зависимостей, масштабирования и распределения подзадач по процессорам вычислительной системы (см. рис. 4.1). Для демонстрации приводимых рекомендаций далее будет использоваться учебная задача поиска максимального значения среди элементов матрицы A (такая задача возникает, например, при численном решении систем линейных уравнений для определения ведущего элемента метода Гаусса): ( 4.1) Данная задача носит полностью иллюстративный характер, и после рассмотрения этапов разработки в оставшейся части лекции будет приведен более полный пример использования данной методики для разработки параллельных алгоритмов. Кроме того, данная схема разработки будет применена и при изложении всех рассматриваемых далее методов параллельных вычислений. 13.2.1. Разделение вычислений на независимые части Выбор способа разделения вычислений на независимые части основывается на анализе вычислительной схемы решения исходной задачи. Требования, которым должен удовлетворять выбираемый подход, обычно состоят в обеспечении равного объема вычислений в выделяемых подзадачах и минимума информационных зависимостей между этими подзадачами (при прочих равных условиях нужно отдавать предпочтение редким операциям передачи сообщений большего размера по сравнению с частыми пересылками данных небольшого объема). В общем случае, проведение анализа и выделение задач представляет собой достаточно сложную проблему – ситуацию помогает разрешить существование двух часто встречающихся типов вычислительных схем (см. рис. 4.3). Рис. 4.3. Разделение данных матрицы: а) ленточная схема, б) блочная схема Для большого класса задач вычисления сводятся к выполнению однотипной обработки большого набора данных – к такому классу задач относятся, например, матричные вычисления, численные методы решения уравнений в частных производных и др. В этом случае говорят, что существует параллелизм по данным, и выделение подзадач сводится к разделению имеющихся данных. Так, например, для рассматриваемой учебной задачи поиска максимального значения при формировании подзадач исходная матрица может быть разделена на отдельные строки (или последовательные группы строк) – так называемая ленточная схема разделения данных (см. рис. 4.3) – либо на прямоугольные наборы элементов – блочная схема разделения данных. Для большого количества решаемых задач разделение вычислений по данным приводит к порождению одно-, дву- и трехмерных наборов подзадач, в которых информационные связи существуют только между ближайшими соседями (такие схемы обычно именуются сетками или решетками). Рис. 4.4. Регулярные одно-, дву- и трехмерные структуры базовых подзадач после декомпозиции данных Для другой части задач вычисления могут состоять в выполнении разных операций над одним и тем же набором данных – в этом случае говорят о существовании функционального параллелизма (в качестве примеров можно привести задачи обработки последовательности запросов к информационным базам данных, вычисления с одновременным применением разных алгоритмов расчета и т.п.). Очень часто функциональная декомпозиция может быть использована для организации конвейерной обработки данных (так, например, при выполнении каких-либо преобразований данных вычисления могут быть сведены к функциональной последовательности ввода, обработки и сохранения данных). Важный вопрос при выделении подзадач состоит в выборе нужного уровня декомпозиции вычислений. Формирование максимально возможного количества подзадач обеспечивает использование предельно достижимого уровня параллелизма решаемой задачи, однако затрудняет анализ параллельных вычислений. Применение при декомпозиции вычислений только достаточно "крупных" подзадач приводит к ясной схеме параллельных вычислений, однако может затруднить эффективное использование достаточно большого количества процессоров. Возможное разумное сочетание этих двух подходов может состоять в применении в качестве конструктивных элементов декомпозиции только тех подзадач, для которых методы параллельных вычислений являются известными. Так, например, при анализе задачи матричного умножения в качестве подзадач можно использовать методы скалярного произведения векторов или алгоритмы матрично-векторного произведения. Подобный промежуточный способ декомпозиции вычислений позволит обеспечить и простоту представления вычислительных схем, и эффективность параллельных расчетов. Выбираемые подзадачи при таком подходе будем именовать далее базовыми, которые могут быть элементарными (неделимыми), если не допускают дальнейшего разделения, или составными - в противном случае. Для рассматриваемой учебной задачи достаточный уровень декомпозиции может состоять, например, в разделении матрицы на множество отдельных строк и получении на этой основе набора подзадач поиска максимальных значений в отдельных строках; порождаемая при этом структура информационных связей соответствует линейному графу (см. рис. 4.5). Рис. 4.5. Структура информационных связей учебной задачи Для оценки корректности этапа разделения вычислений на независимые части можно воспользоваться контрольным списком вопросов, предложенных в [ [ 32 ] ]: • выполненная декомпозиция не увеличивает объем вычислений и необходимый объем памяти? • возможна ли при выбранном способе декомпозиции равномерная загрузка всех имеющихся процессоров? • достаточно ли выделенных частей процесса вычислений для эффективной загрузки имеющихся процессоров (с учетом возможности увеличения их количества)? 13.2.2. Выделение информационных зависимостей При наличии вычислительной схемы решения задачи после выделения базовых подзадач определение информационных зависимостей между ними обычно не вызывает больших затруднений. При этом, однако, следует отметить, что на самом деле этапы выделения подзадач и информационных зависимостей достаточно сложно поддаются разделению. Выделение подзадач должно происходить с учетом возникающих информационных связей, после анализа объема и частоты необходимых информационных обменов между подзадачами может потребоваться повторение этапа разделения вычислений. При проведении анализа информационных зависимостей между подзадачами следует различать (предпочтительные формы информационного взаимодействия выделены подчеркиванием): • локальные и глобальные схемы передачи данных – для локальных схем передачи данных в каждый момент времени выполняются только между небольшим числом подзадач (располагаемых, как правило, на соседних процессорах), для глобальных операций передачи данных в процессе коммуникации принимают участие все подзадачи; • структурные и произвольные способы взаимодействия – для структурных способов организация взаимодействий приводит к формированию некоторых стандартных схем коммуникации (например, в виде кольца, прямоугольной решетки и т. д.), для произвольных структур взаимодействия схема выполняемых операций передач данных не носит характера однородности; • статические или динамические схемы передачи данных – для статических схем моменты и участники информационного взаимодействия фиксируются на этапах проектирования и разработки параллельных программ, для динамического варианта взаимодействия структура операции передачи данных определяется в ходе выполняемых вычислений; • синхронные и асинхронные способы взаимодействия – для синхронных способов операции передачи данных выполняются только при готовности всех участников взаимодействия и завершаются только после полного окончания всех коммуникационных действий, при асинхронном выполнении операций участники взаимодействия могут не дожидаться полного завершения действий по передаче данных. Для представленных способов взаимодействия достаточно сложно выделить предпочтительные формы организации передачи данных: синхронный вариант, как правило, более прост для применения, в то время как асинхронный способ часто позволяет существенно снизить временные задержки, вызванные операциями информационного взаимодействия. Как уже отмечалось в предыдущем пункте, для учебной задачи поиска максимального значения при использовании в качестве базовых элементов подзадач поиска максимальных значений в отдельных строках исходной матрицы структура информационных связей имеет вид, представленный на рис. 4.5. Для оценки правильности этапа выделения информационных зависимостей можно воспользоваться контрольным списком вопросов, предложенным в [ [ 32 ] ]: • соответствует ли вычислительная сложность подзадач интенсивности их информационных взаимодействий? • является ли одинаковой интенсивность информационных взаимодействий для разных подзадач? • является ли схема информационного взаимодействия локальной? • не препятствует ли выявленная информационная зависимость параллельному решению подзадач? 13.2.3. Масштабирование набора подзадач Масштабирование разработанной вычислительной схемы параллельных вычислений проводится в случае, если количество имеющихся подзадач отличается от числа планируемых к использованию процессоров. Для сокращения количества подзадач необходимо выполнить укрупнение ( агрегацию ) вычислений. Применяемые здесь правила совпадают с рекомендациями начального этапа выделения подзадач: определяемые подзадачи, как и ранее, должны иметь одинаковую вычислительную сложность, а объем и интенсивность информационных взаимодействий между подзадачами должны оставаться на минимально возможном уровне. Как результат, первыми претендентами на объединение являются подзадачи с высокой степенью информационной взаимозависимости. При недостаточном количестве имеющихся подзадач для загрузки всех доступных к использованию процессоров необходимо выполнить детализацию ( декомпозицию ) вычислений. Как правило, проведение подобной декомпозиции не вызывает каких-либо затруднений, если для базовых задач методы параллельных вычислений являются известными. Выполнение этапа масштабирования вычислений должно свестись, в конечном итоге, к разработке правил агрегации и декомпозиции подзадач, которые должны параметрически зависеть от числа процессоров, применяемых для вычислений. Для рассматриваемой учебной задачи поиска максимального значения агрегация вычислений может состоять в объединении отдельных строк в группы (ленточная схема разделения матрицы – см. рис. 4.3а), при декомпозиции подзадач строки исходной матрицы могут разбиваться на несколько частей (блоков). Список контрольных вопросов, предложенный в [ [ 32 ] ] для оценки правильности этапа масштабирования, выглядит следующим образом: • не ухудшится ли локальность вычислений после масштабирования имеющегося набора подзадач? • имеют ли подзадачи после масштабирования одинаковую вычислительную и коммуникационную сложность? • соответствует ли количество задач числу имеющихся процессоров? • зависят ли параметрически правила масштабирования от количества процессоров? 13.2.4. Распределение подзадач между процессорами Распределение подзадач между процессорами является завершающим этапом разработки параллельного метода. Надо отметить, что управление распределением нагрузки для процессоров возможно только для вычислительных систем с распределенной памятью, для мультипроцессоров (систем с общей памятью) распределение нагрузки обычно выполняется операционной системой автоматически. Кроме того, данный этап распределения подзадач между процессорами является избыточным, если количество подзадач совпадает с числом имеющихся процессоров, а топология сети передачи данных вычислительной системы представляет собой полный граф (т. е. все процессоры связаны между собой прямыми линиями связи). Основной показатель успешности выполнения данного этапа – эффективность использования процессоров, определяемая как относительная доля времени, в течение которого процессоры использовались для вычислений, связанных с решением исходной задачи. Пути достижения хороших результатов в этом направлении остаются прежними: как и ранее, необходимо обеспечить равномерное распределение вычислительной нагрузки между процессорами и минимизировать количество сообщений, передаваемых между ними. Точно так же как и на предшествующих этапах проектирования, оптимальное решение проблемы распределения подзадач между процессорами основывается на анализе информационной связности графа "подзадачи – сообщения". Так, в частности, подзадачи, имеющие информационные взаимодействия, целесообразно размещать на процессорах, между которыми существуют прямые линии передачи данных. Следует отметить, что требование минимизации информационных обменов между процессорами может противоречить условию равномерной загрузки. Мы можем разместить все подзадачи на одном процессоре и полностью устранить межпроцессорную передачу сообщений, однако понятно, что загрузка большинства процессоров в этом случае будет минимальной. Для учебной задачи поиска максимального значения распределение подзадач между процессорами не вызывает каких-либо затруднений – достаточно лишь обеспечить размещение подзадач, между которыми имеются информационные связи, на процессорах, для которых существуют прямые каналы передачи данных. Поскольку структура информационной связей учебной задачи имеет вид линейного графа, выполнение данного требования может быть обеспечено практически при любой топологии сети вычислительной системы. Решение вопросов балансировки вычислительной нагрузки значительно усложняется, если схема вычислений может изменяться в ходе решения задачи. Причиной этого могут быть, например, неоднородные сетки при решении уравнений в частных производных, разреженность матриц и т.п. 3Можно отметить, что даже для нашей простой учебной задачи может наблюдаться различная вычислительная сложность сформированных базовых задач. Так, например, количество операций при поиске максимального значания для строки, в которой максимальное значение имеет первый элемент, и строки, в которой значения являются упорядоченными по возрастанию, будет различаться в два раза.. Кроме того, используемые на этапах проектирования оценки вычислительной сложности решения подзадач могут иметь приближенный характер, и, наконец, количество подзадач может изменяться в ходе вычислений. В таких ситуациях может потребоваться перераспределение базовых подзадач между процессорами уже непосредственно в ходе выполнения параллельной программы (или, как обычно говорят, придется выполнить динамическую балансировку вычислительной нагрузки). Данные вопросы являются одними из наиболее сложных (и наиболее интересных) в области параллельных вычислений – к сожалению, их рассмотрение выходит за рамки нашего учебного материала (дополнительная информация может быть получена, например, в [ [ 24 ] , [ 75 ] ]). В качестве примера дадим краткую характеристику широко используемого способа динамического управления распределением вычислительной нагрузки, обычно именуемого схемой "менеджер – исполнитель" ( the manager-worker scheme ). При использовании данного подхода предполагается, что подзадачи могут возникать и завершаться в ходе вычислений, при этом информационные взаимодействия между подзадачами либо полностью отсутствуют, либо минимальны. В соответствии с рассматриваемой схемой для управления распределением нагрузки в системе выделяется отдельный процессор-менеджер, которому доступна информация обо всех имеющихся подзадачах. Остальные процессоры системы являются исполнителями, которые для получения вычислительной нагрузки обращаются к процессору-менеджеру. Порождаемые в ходе вычислений новые подзадачи передаются обратно процессору-менеджеру и могут быть получены для решения при последующих обращениях процессоров- исполнителей. Завершение вычислений происходит в момент, когда процессоры-исполнители завершили решение всех переданных им подзадач, а процессор-менеджер не имеет каких-либо вычислительных работ для выполнения. Предложенный в [ [ 32 ] ] перечень контрольных вопросов для проверки этапа распределения подзадач состоит в следующем: • не приводит ли распределение нескольких задач на один процессор к росту дополнительных вычислительных затрат? • существует ли необходимость динамической балансировки вычислений? • не является ли процессор-менеджер "узким" местом при использовании схемы "менеджер – исполнитель"? 13.3. Параллельное решение гравитационной задачи N тел Многие задачи в области физики сводятся к операциям обработки данных для каждой пары объектов имеющейся физической системы. Такой задачей является, в частности, проблема, широко известная в литературе как гравитационная задача N тел (или просто задача N тел ) – см., например, [ [ 5 ] ]. В самом общем виде задача может быть описана следующим образом. Пусть дано большое количество тел (планет, звезд и т. д.), для каждого из которых известна масса, начальное положение и скорость. Под действием гравитации положение тел меняется, и требуемое решение задачи состоит в моделировании динамики изменения системы N тел на протяжении некоторого задаваемого интервала времени. Для проведения такого моделирования заданный интервал времени обычно разбивается на временные отрезки небольшой длительности и далее на каждом шаге моделирования вычисляются силы, действующие на каждое тело, а затем обновляются скорости и положения тел. Очевидный алгоритм решения задачи N тел состоит в рассмотрении на каждом шаге моделирования всех пар объектов физической системы и выполнении для каждой получаемой пары всех необходимых расчетов. Как результат, при таком подходе время выполнения одной итерации моделирования будет составлять4Следует отметить, что для решения задачи N тел существуют и более эффективные последовательные алгоритмы, однако их изучение может потребовать достаточно больших усилий. С учетом данного обстоятельства для дальнейшего рассмотрения выбирается именно данный "очевидный" (но не самый быстрый) метод. ( 4.2) где есть время перевычисления параметров одной пары тел. Как следует из приведенного описания, вычислительная схема рассмотренного алгоритма является сравнительно простой, что позволяет использовать задачу N тел в качестве еще одной наглядной демонстрации применения методики разработки параллельных алгоритмов. 13.3.1. Разделение вычислений на независимые части Выбор способа разделения вычислений не вызывает каких-либо затруднений – очевидный подход состоит в выборе в качестве базовой подзадачи всего набора вычислений, связанных с обработкой данных какого-либо одного тела физической системы. 13.3.2. Выделение информационных зависимостей Выполнение вычислений, связанных с каждой подзадачей, становится возможным только в случае, когда в подзадачах имеются данные (положение и скорости передвижения) обо всех телах физической системы. Как результат, перед началом каждой итерации моделирования каждая подзадача должна получить все необходимые сведения от всех других подзадач системы. Такая процедура передачи данных, как отмечалось в "Оценка коммуникационной трудоемкости параллельных алгоритмов" , именуется операцией сбора данных ( single-node gather ). В рассматриваемом алгоритме данная операция должна быть выполнена для каждой подзадачи – такой вариант передачи данных обычно именуется операцией обобщенного сбора данных ( multi-node gather или all gather ). Определение требований к необходимым результатам информационного обмена не приводит к однозначному установлению нужного информационного обмена между подзадачами – достижение требуемых результатов может быть обеспечено при помощи разных алгоритмов выполнения операции обобщенного сбора данных. Наиболее простой способ выполнения необходимого информационного обмена состоит в реализации последовательности шагов, на каждом из которых все имеющиеся подзадачи разбиваются попарно и обмен данными осуществляется между подзадачами образовавшихся пар. При надлежащей организации попарного разделения подзадач ( N-1 )-кратное повторение описанных действий приведет к полной реализации требуемой операции сбора данных. Рассмотренный выше метод организации информационного обмена является достаточно трудоемким – для сбора всех необходимых данных требуется провести N-1 итерацию, на каждой из которых выполняется одновременно N/2 операций передачи данных. Для сокращения требуемого количества итераций можно обратить внимание на факт, что после выполнения первого шага операции сбора данных подзадачи будут уже содержать не только свои данные, но и данные подзадач, с которыми они образовывали пары. Как результат, на второй итерации сбора данных можно будет образовывать пары подзадач для обмена данными сразу о двух телах физической системы – тем самым, после завершения второй итерации каждая подзадача будет содержать сведения о четырех телах системы и т. д. Как можно заметить, данный способ реализации обменов позволяет завершить необходимую процедуру за log2N итераций. Следует отметить, что при этом объем пересылаемых данных в каждой операции обмена удваивается от итерации к итерации: на первой итерации между подзадачами пересылаются данные об одном теле системы, на второй – о двух телах и т. д. 13.3.3. Масштабирование и распределение подзадач по процессорам Как правило, число тел физической системы N значительно превышает количество процессоров p. Поэтому рассмотренные ранее подзадачи следует укрупнить, объединив в рамках одной подзадачи вычисления для группы из N/p тел. После проведения подобной агрегации число подзадач и количество процессоров будет совпадать и при распределении подзадач между процессорами останется лишь обеспечить наличие прямых коммуникационных линий между процессорами с подзадачами, у которых имеются информационные обмены при выполнении операции сбора данных. 13.3.4. Анализ эффективности параллельных вычислений Оценим эффективность разработанных способов параллельных вычислений для решения задачи N тел. Поскольку предложенные варианты отличаются только методами выполнения информационных обменов, для сравнения подходов достаточно определить длительность операции обобщенного сбора данных. Используем для оценки времени передачи сообщений модель, предложенную Хокни (см. "Оценка коммуникационной трудоемкости параллельных алгоритмов" ), - тогда длительность выполнения операции сбора данных для первого варианта параллельных вычислений может быть выражена как ( 4.3) где и есть параметры модели Хокни (латентность и пропускная способность сети передачи данных), а m задает объем пересылаемых данных для одного тела физической системы. Для второго способа информационного обмена, как уже отмечалось ранее, объем пересылаемых данных на разных итерациях операции сбора данных различается. На первой итерации объем пересылаемых сообщений составляет Nm/p, на второй этот объем увеличивается вдвое и оказывается равным 2Nm/p и т.д. В общем случае, для итерации с номером i объем сообщений оценивается как 2i-1Nm/p. Как результат, длительность выполнения операции сбора данных в этом случае может быть определена при помощи следующего выражения: ( 4.4) Сравнение полученных выражений показывает, что второй разработанный способ параллельных вычислений имеет существенно более высокую эффективность, требует меньших коммуникационных затрат и допускает лучшую масштабируемость при увеличении количества используемых процессоров. Лекция 14. Параллельное программирование на основе MPI В вычислительных системах с распределенной памятью (см. рис.1.6) процессоры работают независимо друг от друга. Для организации параллельных вычислений в таких условиях необходимо иметь возможность распределять вычислительную нагрузку и организовать информационное взаимодействие ( передачу данных ) между процессорами. Решение всех перечисленных вопросов и обеспечивает интерфейс передачи данных ( message passing interface – MPI ). В общем плане, для распределения вычислений между процессорами необходимо проанализировать алгоритм решения задачи, выделить информационно независимые фрагменты вычислений, провести их программную реализацию и затем разместить полученные части программы на разных процессорах. В рамках MPI принят более простой подход – для решения поставленной задачи разрабатывается одна программа и эта единственная программа запускается одновременно на выполнение на всех имеющихся процессорах. При этом для того чтобы избежать идентичности вычислений на разных процессорах, можно, во-первых, подставлять разные данные для программы на разных процессорах, а во-вторых, использовать имеющиеся в MPI средства для идентификации процессора, на котором выполняется программа (тем самым предоставляется возможность организовать различия в вычислениях в зависимости от используемого программой процессора). Подобный способ организации параллельных вычислений получил наименование модели "одна программа множество процессов" (single program multiple processes or SPMP). Для организации информационного взаимодействия между процессорами в самом минимальном варианте достаточно операций приема и передачи данных (при этом, конечно, должна существовать техническая возможность коммуникации между процессорами – каналы или линии связи ). В MPI существует целое множество операций передачи данных. Они обеспечивают разные способы пересылки данных, реализуют практически все рассмотренные в "Оценка коммуникационной трудоемкости параллельных алгоритмов" коммуникационные операции. Именно данные возможности являются наиболее сильной стороной MPI (об этом, в частности, свидетельствует и само название MPI ). Следует отметить, что попытки создания программных средств передачи данных между процессорами начали предприниматься практически сразу с появлением локальных компьютерных сетей – ряд таких средств, представлен, например, в работах [ [ 2 ] , [ 5 ] , [ 24 ] ] и многих других. Однако подобные средства часто были неполными и, самое главное, являлись несовместимыми. Таким образом, одна из самых серьезных проблем в программировании – переносимость программ при переводе программного обеспечения на другие компьютерные системы – проявлялась при разработке параллельных программ в максимальной степени. Как результат, уже с 90-х годов стали предприниматься усилия по стандартизации средств организации передачи сообщений в многопроцессорных вычислительных системах. Началом работ, непосредственно приведших к появлению MPI, послужило проведение рабочего совещания по стандартам для передачи сообщений в среде распределенной памяти (the Workshop on Standards for Message Passing in a Distributed Memory Environment, Williamsburg, Virginia, USA, April 1992). По итогам совещания была образована рабочая группа, позднее преобразованная в международное сообщество MPI Forum, результатом деятельности которых явилось создание и принятие в 1994 г. стандарта интерфейса передачи сообщений ( message passing interface – MPI ) версии 1.0. В последующие годы стандарт MPI последовательно развивался. В 1997 г. был принят стандарт MPI версии 2.0. Итак, теперь можно пояснить, что означает понятие MPI. Во- первых, MPI – это стандарт, которому должны удовлетворять средства организации передачи сообщений. Во-вторых, MPI – это программные средства, которые обеспечивают возможность передачи сообщений и при этом соответствуют всем требованиям стандарта MPI. Так, по стандарту, эти программные средства должны быть организованы в виде библиотек программных функций ( библиотеки MPI ) и должны быть доступны для наиболее широко используемых алгоритмических языков C и Fortran. Подобную "двойственность" MPI следует учитывать при использовании терминологии. Как правило, аббревиатура MPI применяется при упоминании стандарта, а сочетание "библиотека MPI " указывает на ту или иную программную реализацию стандарта. Однако достаточно часто для краткости обозначение MPI используется и для библиотек MPI, и, тем самым, для правильной интерпретации термина следует учитывать контекст. Вопросы, связанные с разработкой параллельных программ с применением MPI, достаточно широко рассмотрены в литературе – краткий обзор полезных материалов содержится в конце данной лекции. Здесь же, еще не приступая к изучению MPI, приведем ряд его важных положительных моментов: • MPI позволяет в значительной степени снизить остроту проблемы переносимости параллельных программ между разными компьютерными системами – параллельная программа, разработанная на алгоритмическом языке C или Fortran с использованием библиотеки MPI, как правило, будет работать на разных вычислительных платформах; • MPI содействует повышению эффективности параллельных вычислений, поскольку в настоящее время практически для каждого типа вычислительных систем существуют реализации библиотек MPI, в максимальной степени учитывающие возможности компьютерного оборудования; • MPI уменьшает, в определенном плане, сложность разработки параллельных программ, т. к., с одной стороны, большая часть рассмотренных в "Оценка коммуникационной трудоемкости параллельных алгоритмов" основных операций передачи данных предусматривается стандартом MPI, а с другой стороны, уже имеется большое количество библиотек параллельных методов, созданных с использованием MPI. 14.1. MPI: основные понятия и определения Рассмотрим ряд понятий и определений, являющихся основополагающими для стандарта MPI. 14.1.1. Понятие параллельной программы Под параллельной программой в рамках MPI понимается множество одновременно выполняемых процессов. Процессы могут выполняться на разных процессорах, но на одном процессоре могут располагаться и несколько процессов (в этом случае их исполнение осуществляется в режиме разделения времени). В предельном случае для выполнения параллельной программы может использоваться один процессор – как правило, такой способ применяется для начальной проверки правильности параллельной программы. Каждый процесс параллельной программы порождается на основе копии одного и того же программного кода ( модель SPMP ). Данный программный код, представленный в виде исполняемой программы, должен быть доступен в момент запуска параллельной программы на всех используемых процессорах. Исходный программный код для исполняемой программы разрабатывается на алгоритмических языках C или Fortran с применением той или иной реализации библиотеки MPI. Количество процессов и число используемых процессоров определяется в момент запуска параллельной программы средствами среды исполнения MPI -программ и в ходе вычислений не может меняться без применения специальных, но редко задействуемых средств динамического порождения процессов и управления ими, появившихся в стандарте MPI версии 2.0. Все процессы программы последовательно перенумерованы от 0 до p-1, где p есть общее количество процессов. Номер процесса именуется рангом процесса. 14.1.2. Операции передачи данных Основу MPI составляют операции передачи сообщений. Среди предусмотренных в составе MPI функций различаются парные ( point-to-point ) операции между двумя процессами и коллективные ( collective ) коммуникационные действия для одновременного взаимодействия нескольких процессов. Для выполнения парных операций могут использоваться разные режимы передачи, среди которых синхронный, блокирующий и др. – полное рассмотрение возможных режимов передачи будет выполнено в подразделе 5.3. Как уже отмечалось ранее, в стандарт MPI включено большинство основных коллективных операций передачи данных – см. подразделы 5.2 и 5.4. 14.1.3. Понятие коммуникаторов Процессы параллельной программы объединяются в группы. Другим важным понятием MPI, описывающим набор процессов, является понятие коммуникатора. Под коммуникатором в MPI понимается специально создаваемый служебный объект, который объединяет в своем составе группу процессов и ряд дополнительных параметров (контекст), используемых при выполнении операций передачи данных. Парные операции передачи данных выполняются только для процессов, принадлежащих одному и тому же коммуникатору. Коллективные операции применяются одновременно для всех процессов одного коммуникатора. Как результат, указание используемого коммуникатора является обязательным для операций передачи данных в MPI. В ходе вычислений могут создаваться новые и удаляться существующие группы процессов и коммуникаторы. Один и тот же процесс может принадлежать разным группам и коммуникаторам. Все имеющиеся в параллельной программе процессы входят в состав конструируемого по умолчанию коммуникатора с идентификатором MPI_COMM_WORLD. В версии 2.0 стандарта появилась возможность создавать глобальные коммуникаторы ( intercommunicator ), объединяющие в одну структуру пару групп при необходимости выполнения коллективных операций между процессами из разных групп. Подробное рассмотрение возможностей MPI для работы с группами и коммуникаторами будет выполнено в подразделе 5.6. 14.1.4. Типы данных При выполнении операций передачи сообщений для указания передаваемых или получаемых данных в функциях MPI необходимо указывать тип пересылаемых данных. MPI содержит большой набор базовых типов данных, во многом совпадающих с типами данных в алгоритмических языках C и Fortran. Кроме того, в MPI имеются возможности создания новых производных типов данных для более точного и краткого описания содержимого пересылаемых сообщений. Подробное рассмотрение возможностей MPI для работы с производными типами данных будет выполнено в подразделе 5.5. 14.1.5. Виртуальные топологии Как уже отмечалось ранее, парные операции передачи данных могут быть выполнены между любыми процессами одного и того же коммуникатора, а в коллективной операции принимают участие все процессы коммуникатора. Логическая топология линий связи между процессами имеет структуру полного графа (независимо от наличия реальных физических каналов связи между процессорами). Вместе с этим (и это уже отмечалось в "Оценка коммуникационной трудоемкости параллельных алгоритмов" ), для изложения и последующего анализа ряда параллельных алгоритмов целесообразно логическое представление имеющейся коммуникационной сети в виде тех или иных топологий. В MPI имеется возможность представления множества процессов в виде решетки произвольной размерности (см. рис. 1.7). При этом граничные процессы решеток могут быть объявлены соседними, и, тем самым, на основе решеток могут быть определены структуры типа тор. Кроме того, в MPI имеются средства и для формирования логических (виртуальных) топологий любого требуемого типа. Подробное рассмотрение возможностей MPI для работы с топологиями будет выполнено в подразделе 5.7. И, наконец, последний ряд замечаний перед началом рассмотрения MPI: • описание функций и все приводимые примеры программ будут представлены на алгоритмическом языке C; особенности использования MPI для алгоритмического языка Fortran будут даны в п. 5.8.1; • краткая характеристика имеющихся реализаций библиотек MPI и общее описание среды выполнения MPI -программ будут рассмотрены в п. 5.8.2; • основное изложение возможностей MPI будет ориентировано на стандарт версии 1.2 (так называемый MPI -1), нововведения стандарта версии 2.0 будут представлены в п. 5.8.3. Приступая к изучению MPI, можно отметить, что, с одной стороны, MPI достаточно сложен – в стандарте MPI предусматривается наличие более чем 120 функций. С другой стороны, структура MPI является тщательно продуманной – разработка параллельных программ может быть начата уже после рассмотрения всего лишь 6 функций MPI. Все дополнительные возможности MPI могут осваиваться по мере роста сложности разрабатываемых алгоритмов и программ. Именно в таком стиле – от простого к сложному – и будет далее представлен весь учебный материал по MPI. 14.2. Введение в разработку параллельных программ с использованием MPI 14.2.1. Основы MPI Приведем минимально необходимый набор функций MPI, достаточный для разработки сравнительно простых параллельных программ. 14.2.1.1. Инициализация и завершение MPI-программ Первой вызываемой функцией MPI должна быть функция: int MPI_Init(int *argc, char ***argv), где • argc — указатель на количество параметров командной строки, • argv — параметры командной строки, применяемая для инициализации среды выполнения MPI -программы. Параметрами функции являются количество аргументов в командной строке и адрес указателя на массив символов текста самой командной строки. Последней вызываемой функцией MPI обязательно должна являться функция: int MPI_Finalize(void). Как результат, можно отметить, что структура параллельной программы, разработанная с использованием MPI, должна иметь следующий вид: #include "mpi.h" int main(int argc, char *argv[]) { <программный код без использования функций MPI> MPI_Init(&argc, &argv); <программный код с использованием функций MPI> MPI_Finalize(); <программный код без использования функций MPI> return 0; } Следует отметить: • файл mpi.h содержит определения именованных констант, прототипов функций и типов данных библиотеки MPI ; • функции MPI_Init и MPI_Finalize являются обязательными и должны быть выполнены (и только один раз) каждым процессом параллельной программы ; • перед вызовом MPI_Init может быть использована функция MPI_Initialized для определения того, был ли ранее выполнен вызов MPI_Init, а после вызова MPI_Finalize – MPI_Finalized2Эта функция появилась только в стандарте MPI версии 2.0. аналогичного предназначения. Рассмотренные примеры функций дают представление синтаксиса именования функций в MPI. Имени функции предшествует префикс MPI, далее следует одно или несколько слов названия, первое слово в имени функции начинается с заглавного символа, слова разделяются знаком подчеркивания. Названия функций MPI, как правило, поясняют назначение выполняемых функцией действий. 14.2.1.2. Определение количества и ранга процессов Определение количества процессов в выполняемой параллельной программе осуществляется при помощи функции: int MPI_Comm_size(MPI_Comm comm, int *size), где • comm — коммуникатор, размер которого определяется, • size — определяемое количество процессов в коммуникаторе. Для определения ранга процесса используется функция: int MPI_Comm_rank(MPI_Comm comm, int *rank), где • comm — коммуникатор, в котором определяется ранг процесса, • rank — ранг процесса в коммуникаторе. Как правило, вызов функций MPI_Comm_size и MPI_Comm_rank выполняется сразу после MPI_Init для получения общего количества процессов и ранга текущего процесса: #include "mpi.h" int main(int argc, char *argv[]) { int ProcNum, ProcRank; <программный код без использования функций MPI> MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD, &ProcNum); MPI_Comm_rank(MPI_COMM_WORLD, &ProcRank); <программный код с использованием функций MPI> MPI_Finalize(); <программный код без использования функций MPI> return 0; } Следует отметить: • коммуникатор MPI_COMM_WORLD, как отмечалось ранее, создается по умолчанию и представляет все процессы выполняемой параллельной программы ; • ранг, получаемый при помощи функции MPI_Comm_rank, является рангом процесса, выполнившего вызов этой функции, т. е. переменная ProcRank примет различные значения у разных процессов. 14.2.1.3. Передача сообщений Для передачи сообщения процесс - отправитель должен выполнить функцию: int MPI_Send(void *buf, int count, MPI_Datatype type, int dest, int tag, MPI_Comm comm), где • buf — адрес буфера памяти, в котором располагаются данные отправляемого сообщения; • count — количество элементов данных в сообщении; • type — тип элементов данных пересылаемого сообщения; • dest — ранг процесса, которому отправляется сообщение; • tag — значение-тег, используемое для идентификации сообщения; • comm — коммуникатор, в рамках которого выполняется передача данных. Для указания типа пересылаемых данных в MPI имеется ряд базовых типов, полный список которых приведен в табл. 5.1. Таблица 5.1. Базовые (пpедопpеделенные) типы данных MPI для алгоритмического языка C Тип данных MPI Тип данных C MPI_BYTE MPI_CHAR signed char MPI_DOUBLE double MPI_FLOAT float MPI_INT int MPI_LONG long MPI_LONG_DOUBLE long double MPI_PACKED MPI_SHORT short MPI_UNSIGNED_CHAR unsigned char MPI_UNSIGNED unsigned int MPI_UNSIGNED_LONG unsigned long MPI_UNSIGNED_SHORT unsigned short Следует отметить: • отправляемое сообщение определяется через указание блока памяти (буфера), в котором это сообщение располагается. Используемая для указания буфера триада (buf, count, type) входит в состав параметров практически всех функций передачи данных; • процессы, между которыми выполняется передача данных, в обязательном порядке должны принадлежать коммуникатору, указываемому в функции MPI_Send ; • параметр tag используется только при необходимости различения передаваемых сообщений, в противном случае в качестве значения параметра может быть использовано произвольное положительное целое число3Максимально возможное целое число, однако, не может быть больше, чем определяемая реализацией константа MPI_TAG_UB . (см. также описание функции MPI_Recv ). Сразу же после завершения функции MPI_Send процесс -отправитель может начать повторно использовать буфер памяти, в котором располагалось отправляемое сообщение. Также следует понимать, что в момент завершения функции MPI_Send состояние самого пересылаемого сообщения может быть совершенно различным: сообщение может располагаться в процессе -отправителе, может находиться в состоянии передачи, может храниться в процессе -получателе или же может быть принято процессом -получателем при помощи функции MPI_Recv. Тем самым, завершение функции MPI_Send означает лишь, что операция передачи начала выполняться и пересылка сообщения рано или поздно будет выполнена. Пример использования функции будет представлен после описания функции MPI_Recv. 14.2.1.4. Прием сообщений Для приема сообщения процесс -получатель должен выполнить функцию: int MPI_Recv(void *buf, int count, MPI_Datatype type, int source, int tag, MPI_Comm comm, MPI_Status *status), где • buf, count, type — буфер памяти для приема сообщения, назначение каждого отдельного параметра соответствует описанию в MPI_Send ; • source — ранг процесса, от которого должен быть выполнен прием сообщения; • tag — тег сообщения, которое должно быть принято для процесса ; • comm — коммуникатор, в рамках которого выполняется передача данных; • status – указатель на структуру данных с информацией о результате выполнения операции приема данных. Следует отметить: • буфер памяти должен быть достаточным для приема сообщения. При нехватке памяти часть сообщения будет потеряна и в коде завершения функции будет зафиксирована ошибка переполнения; с другой стороны, принимаемое сообщение может быть и короче, чем размер приемного буфера, в таком случае изменятся только участки буфера, затронутые принятым сообщением; • типы элементов передаваемого и принимаемого сообщения должны совпадать; • при необходимости приема сообщения от любого процесса - отправителя для параметра source может быть указано значение MPI_ANY_SOURCE (в отличие от функции передачи MPI_Send, которая отсылает сообщение строго определенному адресату); • при необходимости приема сообщения с любым тегом для параметра tag может быть указано значение MPI_ANY_TAG (опять-таки, при использовании функции MPI_Send должно быть указано конкретное значение тега); • в отличие от параметров " процесс -получатель" и "тег", параметр "коммуникатор" не имеет значения, означающего "любой коммуникатор"; • параметр status позволяет определить ряд характеристик принятого сообщения: • status.MPI_SOURCE — ранг процесса – отправителя принятого сообщения; • status.MPI_TAG — тег принятого сообщения. Приведенные значения MPI_ANY_SOURCE и MPI_ANY_TAG иногда называют джокерами. Значение переменной status позволяет определить количество элементов данных в принятом сообщении при помощи функции: int MPI_Get_count(MPI_Status *status, MPI_Datatype type, int *count), где • status — статус операции MPI_Recv ; • type — тип принятых данных; • count — количество элементов данных в сообщении. Вызов функции MPI_Recv не обязан быть согласованным со временем вызова соответствующей функции передачи сообщения MPI_Send – прием сообщения может быть инициирован до момента, в момент или после момента начала отправки сообщения. По завершении функции MPI_Recv в заданном буфере памяти будет располагаться принятое сообщение. Принципиальный момент здесь состоит в том, что функция MPI_Recv является блокирующей для процесса -получателя, т.е. его выполнение приостанавливается до завершения работы функции. Таким образом, если по каким-то причинам ожидаемое для приема сообщение будет отсутствовать, выполнение параллельной программы будет блокировано. 14.2.1.5. Первая параллельная программа с использованием MPI Рассмотренный набор функций оказывается достаточным для разработки параллельных программ4Как было обещано ранее, количество функций MPI, необходимых для начала разработки параллельных программ, оказалось равным шести.. Приводимая ниже программа является стандартным начальным примером для алгоритмического языка C. Программа 5.1. Первая параллельная программа с использованием MPI #include #include "mpi.h" int main(int argc, char* argv[]){ int ProcNum, ProcRank, RecvRank; MPI_Status Status; MPI_Init(&argc, &argv); MPI_Comm_size(MPI_COMM_WORLD, &ProcNum); MPI_Comm_rank(MPI_COMM_WORLD, &ProcRank); if ( ProcRank == 0 ){ // Действия, выполняемые только процессом с рангом 0 printf("\n Hello from process %3d", ProcRank); for (int i = 1; i < ProcNum; i++ ) { MPI_Recv(&RecvRank, 1, MPI_INT, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &Status); printf("\n Hello from process %3d", RecvRank); } } else // Сообщение, отправляемое всеми процессами, // кроме процесса с рангом 0 MPI_Send(&ProcRank,1,MPI_INT,0,0,MPI_COMM_WORLD); MPI_Finalize(); return 0; } Как следует из текста программы, каждый процесс определяет свой ранг, после чего действия в программе разделяются. Все процессы, кроме процесса с рангом 0, передают значение своего ранга нулевому процессу. Процесс с рангом 0 сначала печатает значение своего ранга, а далее последовательно принимает сообщения с рангами процессов и также печатает их значения. При этом важно отметить, что порядок приема сообщений заранее не определен и зависит от условий выполнения параллельной программы (более того, этот порядок может изменяться от запуска к запуску). Так, возможный вариант результатов печати процесса 0 может состоять в следующем (для параллельной программы из четырех процессов ): Hello from process 0 Hello from process 2 Hello from process 1 Hello from process 3 Такой "плавающий" вид получаемых результатов существенным образом усложняет разработку, тестирование и отладку параллельных программ, т.к. в этом случае исчезает один из основных принципов программирования – повторяемость выполняемых вычислительных экспериментов. Как правило, если это не приводит к потере эффективности, следует обеспечивать однозначность расчетов и при использовании параллельных вычислений. Для рассматриваемого простого примера можно восстановить постоянство получаемых результатов при помощи задания ранга процесса -отправителя в операции приема сообщения: MPI_Recv(&RecvRank, 1, MPI_INT, i, MPI_ANY_TAG, MPI_COMM_WORLD, &Status). Указание ранга процесса -отправителя регламентирует порядок приема сообщений, и, как результат, строки печати будут появляться строго в порядке возрастания рангов процессов (повторим, что такая регламентация в отдельных ситуациях может приводить к замедлению выполняемых параллельных вычислений). Следует отметить еще один важный момент: разрабатываемая с применением MPI программа, как в данном частном варианте, так и в самом общем случае, используется для порождения всех процессов параллельной программы а значит, должна определять вычисления, выполняемые всеми этими процессами. Можно сказать, что MPI - программа является некоторой "макропрограммой", различные части которой используются разными процессами. Так, например, в приведенном примере программы выделенные рамкой участки программного кода не выполняются одновременно ни одним из процессов. Первый выделенный участок с функцией приема MPI_Recv исполняется только процессом с рангом 0, второй участок с функцией передачи MPI_Send задействуется всеми процессами, за исключением нулевого процесса. Для разделения фрагментов кода между процессами обычно используется подход, примененный в только что рассмотренной программе, – при помощи функции MPI_Comm_rank определяется ранг процесса, а затем в соответствии с рангом выделяются необходимые для процесса участки программного кода. Наличие в одной и той же программе фрагментов кода разных процессов также значительно усложняет понимание и, в целом, разработку MPI -программы. Как результат, можно рекомендовать при увеличении объема разрабатываемых программ выносить программный код разных процессов в отдельные программные модули (функции). Общая схема MPI -программы в этом случае будет иметь вид: MPI_Comm_rank(MPI_COMM_WORLD, &ProcRank); if ( ProcRank == 0 ) DoProcess0(); else if ( ProcRank == 1 ) DoProcess1(); else if ( ProcRank == 2 ) DoProcess2(); Во многих случаях, как и в рассмотренном примере, выполняемые действия являются отличающимися только для процесса с рангом 0. В этом случае общая схема MPI -программы принимает более простой вид: MPI_Comm_rank(MPI_COMM_WORLD, &ProcRank); if ( ProcRank == 0 ) DoManagerProcess(); else DoWorkerProcesses(); В завершение обсуждения примера поясним примененный в MPI подход для контроля правильности выполнения функций. Все функции MPI (кроме MPI_Wtime и MPI_Wtick ) возвращают в качестве своего значения код завершения. При успешном выполнении функции возвращаемый код равен MPI_SUCCESS. Другие значения кода завершения свидетельствуют об обнаружении тех или иных ошибочных ситуаций в ходе выполнения функций. Для выяснения типа обнаруженной ошибки используются предопределенные именованные константы, среди которых: • MPI_ERR_BUFFER — неправильный указатель на буфер; • MPI_ERR_TRUNCATE — сообщение превышает размер приемного буфера; • MPI_ERR_COMM — неправильный коммуникатор; • MPI_ERR_RANK — неправильный ранг процесса и др. Полный список констант для проверки кода завершения содержится в файле mpi.h. Однако, по умолчанию, возникновение любой ошибки во время выполнения функции MPI приводит к немедленному завершению параллельной программы. Для того чтобы иметь возможность проанализировать возвращаемый код завершения, необходимо воспользоваться предоставляемыми MPI функциями по созданию обработчиков ошибок и управлению ими, рассмотрение которых не входит в материал данной лекции. 14.2.2. Определение времени выполнение MPI-программы Практически сразу же после разработки первых параллельных программ возникает необходимость определения времени выполнения вычислений для оценки достигаемого ускорения процессов решения задач за счет использования параллелизма. Используемые обычно средства для измерения времени работы программ зависят, как правило, от аппаратной платформы, операционной системы, алгоритмического языка и т.п. Стандарт MPI включает определение специальных функций для измерения времени, применение которых позволяет устранить зависимость от среды выполнения параллельных программ. Получение текущего момента времени обеспечивается при помощи функции: double MPI_Wtime(void), результат ее вызова есть количество секунд, прошедшее от некоторого определенного момента времени в прошлом. Этот момент времени в прошлом, от которого происходит отсчет секунд, может зависеть от среды реализации библиотеки MPI, и, тем самым, для ухода от такой зависимости функцию MPI_Wtime следует использовать только для определения длительности выполнения тех или иных фрагментов кода параллельных программ. Возможная схема применения функции MPI_Wtime может состоять в следующем: double t1, t2, dt; t1 = MPI_Wtime(); ѕ t2 = MPI_Wtime(); dt = t2 – t1; Точность измерения времени также может зависеть от среды выполнения параллельной программы. Для определения текущего значения точности может быть использована функция: double MPI_Wtick(void), позволяющая определить время в секундах между двумя последовательными показателями времени аппаратного таймера примененной компьютерной системы. 14.2.3. Начальное знакомство с коллективными операциями передачи данных Функции MPI_Send и MPI_Recv, рассмотренные в п. 5.2.1, обеспечивают возможность выполнения парных операций передачи данных между двумя процессами параллельной программы. Для выполнения коммуникационных коллективных операций, в которых принимают участие все процессы коммуникатора, в MPI предусмотрен специальный набор функций. В данном подразделе будут рассмотрены три такие функции, широко применяемые даже при разработке сравнительно простых параллельных программ ; полное же представление коллективных операций будет дано в подразделе 5.4. Для демонстрации применения рассматриваемых функций MPI будет использоваться учебная задача суммирования элементов вектора x (см. подраздел 2.5): Разработка параллельного алгоритма для решения данной задачи не вызывает затруднений: необходимо разделить данные на равные блоки, передать эти блоки процессам, выполнить в процессах суммирование полученных данных, собрать значения вычисленных частных сумм на одном из процессов и сложить значения частичных сумм для получения общего результата решаемой задачи. При последующей разработке демонстрационных программ данный рассмотренный алгоритм будет несколько упрощен: процессам программы будет передаваться весь суммируемый вектор, а не отдельные блоки этого вектора. 14.2.3.1. Передача данных от одного процесса всем процессам программы Первая задача при выполнении рассмотренного параллельного алгоритма суммирования состоит в необходимости передачи значений вектора x всем процессам параллельной программы. Конечно, для решения этой задачи можно воспользоваться рассмотренными ранее функциями парных операций передачи данных: MPI_Comm_size(MPI_COMM_WORLD, &ProcNum); for (int i = 1; i < ProcNum; i++) MPI_Send(&x, n, MPI_DOUBLE, i, 0, MPI_COMM_WORLD); Однако такое решение будет крайне неэффективным, поскольку повторение операций передачи приводит к суммированию затрат (латентностей) на подготовку передаваемых сообщений. Кроме того, как показано в "Оценка коммуникационной трудоемкости параллельных алгоритмов" , данная операция может быть выполнена за log2p итераций передачи данных. Достижение эффективного выполнения операции передачи данных от одного процесса всем процессам программы ( широковещательная рассылка данных ) может быть обеспечено при помощи функции MPI: int MPI_Bcast(void *buf, int count, MPI_Datatype type, int root, MPI_Comm comm), где • buf, count, type — буфер памяти с отправляемым сообщением (для процесса с рангом 0 ) и для приема сообщений (для всех остальных процессов ); • root — ранг процесса, выполняющего рассылку данных; • comm — коммуникатор, в рамках которого выполняется передача данных. Функция MPI_Bcast осуществляет рассылку данных из буфера buf, содержащего count элементов типа type, с процесса, имеющего номер root, всем процессам, входящим в коммуникатор comm (см. рис. 5.1). Следует отметить: • функция MPI_Bcast определяет коллективную операцию, и, тем самым, при выполнении необходимых рассылок данных вызов функции MPI_Bcast должен быть осуществлен всеми процессами указываемого коммуникатора (см. далее пример программы); • указываемый в функции MPI_Bcast буфер памяти имеет различное назначение у разных процессов: для процесса с рангом root, которым осуществляется рассылка данных, в этом буфере должно находиться рассылаемое сообщение, а для всех остальных процессов указываемый буфер предназначен для приема передаваемых данных; • все коллективные операции "несовместимы" с парными операциями — так, например, принять широковещательное сообщение, отосланное с помощью MPI_Bcast, функцией MPI_Recv нельзя, для этого можно задействовать только MPI_Bcast. Рис. 5.1. Общая схема операции передачи данных от одного процесса всем процессам Приведем программу для решения учебной задачи суммирования элементов вектора с использованием рассмотренной функции. Программа 5.2. Параллельная программа суммирования числовых значений #include #include #include #include "mpi.h" int main(int argc, char* argv[]){ double x[100], TotalSum, ProcSum = 0.0; int ProcRank, ProcNum, N=100, k, i1, i2; MPI_Status Status; // Инициализация MPI_Init(&argc,&argv); MPI_Comm_size(MPI_COMM_WORLD,&ProcNum); MPI_Comm_rank(MPI_COMM_WORLD,&ProcRank); // Подготовка данных if ( ProcRank == 0 ) DataInitialization(x,N); // Рассылка данных на все процессы MPI_Bcast(x, N, MPI_DOUBLE, 0, MPI_COMM_WORLD); // Вычисление частичной суммы на каждом из процессов // на каждом процессе суммируются элементы вектора x от i1 до i2 k = N / ProcNum; i1 = k * ProcRank; i2 = k * ( ProcRank + 1 ); if ( ProcRank == ProcNum-1 ) i2 = N; for ( int i = i1; i < i2; i++ ) ProcSum = ProcSum + x[i]; // Сборка частичных сумм на процессе с рангом 0 if ( ProcRank == 0 ) { TotalSum = ProcSum; for ( int i=1; i < ProcNum; i++ ) { MPI_Recv(&ProcSum,1,MPI_DOUBLE,MPI_ANY_SOURCE,0, MPI_COMM_WORLD, &Status); TotalSum = TotalSum + ProcSum; } } else // Все процессы отсылают свои частичные суммы MPI_Send(&ProcSum, 1, MPI_DOUBLE, 0, 0, MPI_COMM_WORLD); // Вывод результата if ( ProcRank == 0 ) printf("\nTotal Sum = %10.2f",TotalSum); MPI_Finalize(); return 0; } 5.2. В приведенной программе функция DataInitialization осуществляет подготовку начальных данных. Необходимые данные могут быть введены с клавиатуры, прочитаны из файла или сгенерированы при помощи датчика случайных чисел – подготовка этой функции предоставляется как задание для самостоятельной разработки. 14.2.3.2. Передача данных от всех процессов одному процессу. Операция редукции В рассмотренной программе суммирования числовых значений имеющаяся процедура сбора и последующего суммирования данных является примером часто выполняемой коллективной операции передачи данных от всех процессов одному процессу. В этой операции над собираемыми значениями осуществляется та или иная обработка данных (для подчеркивания последнего момента данная операция еще именуется операцией редукции данных ). Как и ранее, реализация операции редукции при помощи обычных парных операций передачи данных является неэффективной и достаточно трудоемкой. Для наилучшего выполнения действий, связанных с редукцией данных, в MPI предусмотрена функция: int MPI_Reduce(void *sendbuf, void *recvbuf, int count, MPI_Datatype type, MPI_Op op, int root, MPI_Comm comm), где • sendbuf — буфер памяти с отправляемым сообщением; • recvbuf — буфер памяти для результирующего сообщения (только для процесса с рангом root ); • count — количество элементов в сообщениях; • type — тип элементов сообщений; • op — операция, которая должна быть выполнена над данными; • root — ранг процесса, на котором должен быть получен результат; • comm — коммуникатор, в рамках которого выполняется операция. В качестве операций редукции данных могут быть использованы предопределенные в MPI операции – см. табл. 5.2. Помимо данного стандартного набора операций могут быть определены и новые дополнительные операции непосредственно самим пользователем библиотеки MPI – см., например, [ [ 4 ] , [ 40 ] – [ 42 ] , [ 57 ] ]. Общая схема выполнения операции сбора и обработки данных на одном процессе показана на табл. 5.2. Элементы получаемого сообщения на процессе root представляют собой результаты обработки соответствующих элементов передаваемых процессами сообщений, т.е.: где есть операция, задаваемая при вызове функции MPI_Reduce (для пояснения на рис. 5.3 показан пример выполнения функции редукции данных). Следует отметить: • функция MPI_Reduce определяет коллективную операцию, и, тем самым, вызов функции должен быть выполнен всеми процессами указываемого коммуникатора. При этом все вызовы функции должны содержать одинаковые значения параметров count, type, op, root, comm ; • передача сообщений должна быть выполнена всеми процессами, результат операции будет получен только процессом с рангом root ; • выполнение операции редукции осуществляется над отдельными элементами передаваемых сообщений. Так, например, если сообщения содержат по два элемента данных и выполняется операция суммирования MPI_SUM, то результат также будет состоять из двух значений, первое из которых будет содержать сумму первых элементов всех отправленных сообщений, а второе значение будет равно сумме вторых элементов сообщений соответственно; • не все сочетания типа данных type и операции op возможны, разрешенные сочетания перечислены в табл. 5.3. Таблица 5.2. Базовые (предопределенные) типы операций MPI для функций редукции данных Операции Описание MPI_MAX Определение максимального значения MPI_MIN Определение минимального значения MPI_SUM Определение суммы значений MPI_PROD Определение произведения значений MPI_LAND Выполнение логической операции "И" над значениями сообщений MPI_BAND Выполнение битовой операции "И" над значениями сообщений MPI_LOR Выполнение логической операции "ИЛИ" над значениями сообщений MPI_BOR Выполнение битовой операции "ИЛИ" над значениями сообщений MPI_LXOR Выполнение логической операции исключающего "ИЛИ" над значениями сообщений MPI_BXOR Выполнение битовой операции исключающего "ИЛИ" над значениями сообщений MPI_MAXLOC Определение максимальных значений и их индексов MPI_MINLOC Определение минимальных значений и их индексов Рис. 5.2. Общая схема операции сбора и обработки на одном процессе данных от всех процессов Таблица 5.3. Разрешенные сочетания операции типа операнда в операции редукции Операции Допустимый тип операндов для алгоритмического языка C MPI_MAX, MPI_MIN, MPI_SUM, MPI_PROD Целый, вещественный MPI_LAND, MPI_LOR, MPI_LXOR Целый MPI_BAND, MPI_BOR, MPI_BXOR Целый, байтовый MPI_MINLOC, MPI_MAXLOC Целый, вещественный Рис. 5.3. Пример выполнения операции редукции при суммировании пересылаемых данных для трех процессов (в каждом сообщении 4 элемента, сообщения собираются на процессе с рангом 2) Применим полученные знания для переработки ранее рассмотренной программы суммирования: как можно увидеть, весь программный код ("Сборка частичных сумм на процессе с рангом 0"), может быть теперь заменен на вызов одной лишь функции MPI_Reduce: // Сборка частичных сумм на процессе с рангом 0 MPI_Reduce(&ProcSum, &TotalSum, 1, MPI_DOUBLE, MPI_SUM, 0, MPI_COMM_WORLD); 14.2.3.3. Синхронизация вычислений В ряде ситуаций независимо выполняемые в процессах вычисления необходимо синхронизировать. Так, например, для измерения времени начала работы параллельной программы необходимо, чтобы для всех процессов одновременно были завершены все подготовительные действия, перед окончанием работы программы все процессы должны завершить свои вычисления и т.п. Синхронизация процессов, т.е. одновременное достижение процессами тех или иных точек процесса вычислений, обеспечивается при помощи функции MPI: int MPI_Barrier(MPI_Comm comm), где • comm — коммуникатор, в рамках которого выполняется операция. Функция MPI_Barrier определяет коллективную операцию, и, тем самым, при использовании она должна вызываться всеми процессами используемого коммуникатора. При вызове функции MPI_Barrier выполнение процесса блокируется, продолжение вычислений процесса произойдет только после вызова функции MPI_Barrier всеми процессами коммуникатора. 14.2.3.4. Аварийное завершение параллельной программы Для корректного завершения параллельной программы в случае непредвиденных ситуаций необходимо использовать функцию: int MPI_Abort(MPI_Comm comm, int errorcode), где • comm — коммуникатор, процессы которого необходимо аварийно остановить; • errorcode — код возврата из параллельной программы. Эта функция корректно прерывает выполнение параллельной программы, оповещая об этом событии среду MPI, в отличие от функций стандартной библиотеки алгоритмического языка C, таких, как abort или terminate. Обычное ее использование заключается в следующем: MPI_Abort(MPI_COMM_WORLD, MPI_ERR_OTHER); 14.3. Операции передачи данных между двумя процессами Продолжим начатое в п. 5.2.1 изучение функций MPI для выполнения операций передачи данных между процессами параллельной программы. 14.3.1. Режимы передачи данных Рассмотренная ранее функция MPI_Send обеспечивает так называемый стандартный (standard) режим отправки сообщений, при котором (см. также п. 5.2.1.3): • на время выполнения функции процесс – отправитель сообщения блокируется; • после завершения функции буфер может быть использован повторно; • состояние отправленного сообщения может быть различным – сообщение может располагаться на процессе -отправителе, может находиться в состоянии передачи, может храниться на процессе -получателе или же может быть принято процессом -получателем при помощи функции MPI_Recv. Кроме стандартного режима в MPI предусматриваются следующие дополнительные режимы передачи сообщений: • синхронный ( synchronous ) режим состоит в том, что завершение функции отправки сообщения происходит только при получении от процесса - получателя подтверждения о начале приема отправленного сообщения. Отправленное сообщение или полностью принято процессом - получателем, или находится в состоянии приема; • буферизованный ( buffered ) режим предполагает использование дополнительных системных или задаваемых пользователем буферов для копирования в них отправляемых сообщений. Функция отправки сообщения завершается сразу же после копирования сообщения в системный буфер; • режим передачи по готовности ( ready ) может быть использован только, если операция приема сообщения уже инициирована. Буфер сообщения после завершения функции отправки сообщения может быть повторно использован. Для именования функций отправки сообщения для разных режимов выполнения в MPI применяется название функции MPI_Send, к которому как префикс добавляется начальный символ названия соответствующего режима работы, т.е.: • MPI_Ssend – функция отправки сообщения в синхронном режиме; • MPI_Bsend – функция отправки сообщения в буферизованном режиме; • MPI_Rsend – функция отправки сообщения в режиме по готовности. Список параметров всех перечисленных функций совпадает с составом параметров функции MPI_Send. Для применения буферизованного режима передачи может быть создан и передан MPI буфер памяти, используемая для этого функция имеет вид: int MPI_Buffer_attach(void *buf, int size), где • buf — адрес буфера памяти; • size — размер буфера. После завершения работы с буфером он должен быть отключен от MPI при помощи функции: int MPI_Buffer_detach(void *buf, int *size), где • buf — адрес буфера памяти; • size — возвращаемый размер буфера. По практическому использованию режимов можно привести следующие рекомендации: • стандартный режим обычно реализуется как буферизированный или синхронный, в зависимости от размера передаваемого сообщения, и зачастую является наиболее оптимизированным по производительности; • режим передачи по готовности формально является наиболее быстрым, но используется достаточно редко, т. к. обычно сложно гарантировать готовность операции приема; • буферизованный режим также выполняется достаточно быстро, но может приводить к большим расходам ресурсов (памяти), – в целом может быть рекомендован для передачи коротких сообщений; • синхронный режим является наиболее медленным, т.к. требует подтверждения приема, однако не нуждается в дополнительной памяти для хранения сообщения. Этот режим может быть рекомендован для передачи длинных сообщений. В заключение отметим, что для функции приема MPI_Recv не существует различных режимов работы. 14.3.2. Организация неблокирующих обменов данными между процессами Все рассмотренные ранее функции отправки и приема сообщений являются блокирующими, т.е. приостанавливающими выполнение процессов до момента завершения работы вызванных функций. В то же время при выполнении параллельных вычислений часть сообщений может быть отправлена и принята заранее, до момента реальной потребности в пересылаемых данных. В таких ситуациях было бы крайне желательным иметь возможность выполнения функций обмена данными без блокировки процессов для совмещения процессов передачи сообщений и вычислений. Такой неблокирующий способ выполнения обменов является, конечно, более сложным для использования, но при правильном применении может в значительной степени уменьшить потери эффективности параллельных вычислений из-за медленных (по сравнению с быстродействием процессоров) коммуникационных операций. MPI обеспечивает возможность неблокированного выполнения операций передачи данных между двумя процессами. Наименование неблокирующих аналогов образуется из названий соответствующих функций путем добавления префикса I ( Immediate ). Список параметров неблокирующих функций содержит обычный набор параметров исходных функций и один дополнительный параметр request с типом MPI_Request (в функции MPI_Irecv отсутствует также параметр status ): int MPI_Isend(void *buf, int count, MPI_Datatype type, int dest, int tag, MPI_Comm comm, MPI_Request *request), int MPI_Issend(void *buf, int count, MPI_Datatype type, int dest, int tag, MPI_Comm comm, MPI_Request *request), int MPI_Ibsend(void *buf, int count, MPI_Datatype type, int dest, int tag, MPI_Comm comm, MPI_Request *request), int MPI_Irsend(void *buf, int count, MPI_Datatype type, int dest, int tag, MPI_Comm comm, MPI_Request *request), int MPI_Irecv(void *buf, int count, MPI_Datatype type, int source, int tag, MPI_Comm comm, MPI_Request *request). Вызов неблокирующей функции приводит к инициации запрошенной операции передачи, после чего выполнение функции завершается и процесс может продолжить свои действия. Перед своим завершением неблокирующая функция определяет переменную request, которая далее может использоваться для проверки завершения инициированной операции обмена. Проверка состояния выполняемой неблокирующей операции передачи данных производится при помощи функции: int MPI_Test(MPI_Request *request, int *flag, MPI_status *status), где • request — дескриптор операции, определенный при вызове неблокирующей функции; • flag — результат проверки ( true, если операция завершена); • status — результат выполнения операции обмена (только для завершенной операции). Операция проверки является неблокирующей, т.е. процесс может проверить состояние неблокирующей операции обмена и продолжить далее свои вычисления, если по результатам проверки окажется, что операция все еще не завершена. Возможная схема совмещения вычислений и выполнения неблокирующей операции обмена может состоять в следующем: MPI_Isend(buf, count, type, dest, tag, comm, &request); ѕ do { ѕ MPI_Test(&request, &flag, &status); } while (!flag); Если при выполнении неблокирующей операции окажется, что продолжение вычислений невозможно без получения передаваемых данных, то может быть использована блокирующая операция ожидания завершения операции: int MPI_Wait(MPI_Request *request, MPI_status *status), где • request — дескриптор операции, определенный при вызове неблокирующей функции; • status — результат выполнения операции обмена (только для завершенной операции). Кроме рассмотренных, MPI содержит ряд дополнительных функций проверки и ожидания неблокирующих операций обмена: • MPI_Testall — проверка завершения всех перечисленных операций обмена; • MPI_Waitall — ожидание завершения всех операций обмена; • MPI_Testany — проверка завершения хотя бы одной из перечисленных операций обмена; • MPI_Waitany — ожидание завершения любой из перечисленных операций обмена; • MPI_Testsome — проверка завершения каждой из перечисленных операций обмена; • MPI_Waitsome — ожидание завершения хотя бы одной из перечисленных операций обмена и оценка состояния по всем операциям. Приведение простого примера использования неблокирующих функций достаточно затруднительно. Хорошей возможностью для освоения рассмотренных функций могут служить, например, параллельные алгоритмы матричного умножения (см. "Параллельные методы матричного умножения" ). 14.3.3. Одновременное выполнение передачи и приема Одной из часто выполняемых форм информационного взаимодействия в параллельных программах является обмен данными между процессами, когда для продолжения вычислений процессам необходимо отправить данные одним процессам и в то же время получить сообщения от других. Простейший вариант этой ситуации состоит, например, в обмене данными между двумя процессами. Реализация таких обменов при помощи обычных парных операций передачи данных может быть неэффективна, кроме того, такая реализация должна гарантировать отсутствие тупиковых ситуаций, которые могут возникать, например, когда два процесса начинают передавать сообщения друг другу с использованием блокирующих функций передачи данных. Достижение эффективного и гарантированного одновременного выполнения операций передачи и приема данных может быть обеспечено при помощи функции MPI: int MPI_Sendrecv(void *sbuf,int scount,MPI_Datatype stype, int dest, int stag, void *rbuf,int rcount,MPI_Datatype rtype, int source,int rtag, MPI_Comm comm, MPI_Status *status), где • sbuf, scount, stype, dest, stag — параметры передаваемого сообщения; • rbuf, rcount, rtype, source, rtag — параметры принимаемого сообщения; • comm — коммуникатор, в рамках которого выполняется передача данных; • status — структура данных с информацией о результате выполнения операции. Как следует из описания, функция MPI_Sendrecv передает сообщение, описываемое параметрами ( sbuf, scount, stype, dest, stag ), процессу с рангом dest и принимает сообщение в буфер, определяемый параметрами ( rbuf, rcount, rtype, source, rtag ), от процесса с рангом source. В функции MPI_Sendrecv для передачи и приема сообщений применяются разные буферы. В случае же когда отсылаемое сообщение больше не нужно на процессе -отправителе, в MPI имеется возможность использования единого буфера: int MPI_Sendrecv_replace(void *buf, int count, MPI_Datatype type, int dest, int stag, int source, int rtag, MPI_Comm comm, MPI_Status* status), где • buf, count, type — параметры передаваемого сообщения; • dest — ранг процесса, которому отправляется сообщение; • stag — тег для идентификации отправляемого сообщения; • source — ранг процесса, от которого выполняется прием сообщения; • rtag — тег для идентификации принимаемого сообщения; • comm — коммуникатор, в рамках которого выполняется передача данных; • status — структура данных с информацией о результате выполнения операции. Пример использования функций для одновременного выполнения операций передачи и приема приведен в "Параллельные методы матричного умножения" при разработке параллельных программ матричного умножения. 14.4. Коллективные операции передачи данных Как уже отмечалось ранее, под коллективными операциями в MPI понимаются операции над данными, в которых принимают участие все процессы используемого коммуникатора. Выделение основных видов коллективных операций было выполнено в "Оценка коммуникационной трудоемкости параллельных алгоритмов" . Часть коллективных операций уже была рассмотрена в п.5.2.3 – это операции передачи от одного процесса всем процессам коммуникатора ( широковещательная рассылка ) и операции обработки данных, полученных на одном процессе от всех процессов ( редукция данных ). Рассмотрим далее оставшиеся базовые коллективные операции передачи данных. 14.4.1. Обобщенная передача данных от одного процесса всем процессам Обобщенная операция передачи данных от одного процесса всем процессам ( распределение данных ) отличается от широковещательной рассылки тем, что процесс передает процессам различающиеся данные (см. рис. 5.4). Выполнение данной операции может быть обеспечено при помощи функции: int MPI_Scatter(void *sbuf, int scount, MPI_Datatype stype, void *rbuf, int rcount, MPI_Datatype rtype, int root, MPI_Comm comm), где • sbuf, scount, stype — параметры передаваемого сообщения (scount определяет количество элементов, передаваемых на каждый процесс ); • rbuf, rcount, rtype — параметры сообщения, принимаемого в процессах ; • root — ранг процесса, выполняющего рассылку данных; • comm — коммуникатор, в рамках которого выполняется передача данных. Рис. 5.4. Общая схема операции обобщенной передачи данных от одного процесса всем процессам При вызове этой функции процесс с рангом root произведет передачу данных всем другим процессам в коммуникаторе. Каждому процессу будет отправлено scount элементов. Процесс с рангом 0 получит блок данных из sbuf элементов с индексами от 0 до scount-1, процессу с рангом 1 будет отправлен блок из sbuf элементов с индексами от scount до 2*scount-1 и т.д. Тем самым, общий размер отправляемого сообщения должен быть равен scount * p элементов, где p есть количество процессов в коммуникаторе comm. Следует отметить, что поскольку функция MPI_Scatter определяет коллективную операцию, вызов этой функции при выполнении рассылки данных должен быть обеспечен на каждом процессе коммуникатора. Отметим также, что функция MPI_Scatter передает всем процессам сообщения одинакового размера. Выполнение более общего варианта операции распределения данных, когда размеры сообщений для процессов могут быть различны, обеспечивается при помощи функции MPI_Scatterv. Пример использования функции MPI_Scatter рассматривается в "Параллельные методы умножения матрицы на вектор" при разработке параллельных программ умножения матрицы на вектор. 14.4.2. Обобщенная передача данных от всех процессов одному процессу Операция обобщенной передачи данных от всех процессов одному процессу (сбор данных) является двойственной к процедуре распределения данных (см. рис. 5.5). Для выполнения этой операции в MPI предназначена функция: int MPI_Gather(void *sbuf, int scount, MPI_Datatype stype, void *rbuf, int rcount, MPI_Datatype rtype, int root, MPI_Comm comm), где • sbuf, scount, stype — параметры передаваемого сообщения; • rbuf, rcount, rtype — параметры принимаемого сообщения; • root — ранг процесса, выполняющего сбор данных; • comm — коммуникатор, в рамках которого выполняется передача данных. Рис. 5.5. Общая схема операции обобщенной передачи данных от всех процессов одному процессу При выполнении функции MPI_Gather каждый процесс в коммуникаторе передает данные из буфера sbuf на процесс с рангом root. Процесс с рангом root собирает все получаемые данные в буфере rbuf (размещение данных в буфере осуществляется в соответствии с рангами процессов – отправителей сообщений). Для того чтобы разместить все поступающие данные, размер буфера rbuf должен быть равен scount * p элементов, где p есть количество процессов в коммуникаторе comm. Функция MPI_Gather также определяет коллективную операцию, и ее вызов при выполнении сбора данных должен быть обеспечен в каждом процессе коммуникатора. Следует отметить, что при использовании функции MPI_Gather сборка данных осуществляется только на одном процессе. Для получения всех собираемых данных на каждом из процессов коммуникатора необходимо применять функцию сбора и рассылки: int MPI_Allgather(void *sbuf, int scount, MPI_Datatype stype, void *rbuf, int rcount, MPI_Datatype rtype, MPI_Comm comm), где • sbuf, scount, stype — параметры передаваемого сообщения; • rbuf, rcount, rtype — параметры принимаемого сообщения; • comm — коммуникатор, в рамках которого выполняется передача данных. Выполнение общего варианта операции сбора данных, когда размеры передаваемых процессами сообщений могут быть различны, обеспечивается при помощи функций MPI_Gatherv и MPI_Allgatherv. Пример использования функции MPI_Gather рассматривается в "Параллельные методы умножения матрицы на вектор" при разработке параллельных программ умножения матрицы на вектор. 14.4.3. Общая передача данных от всех процессов всем процессам Передача данных от всех процессов всем процессам является наиболее общей операцией передачи данных (см. рис. 5.6). Выполнение данной операции может быть обеспечено при помощи функции: int MPI_Alltoall(void *sbuf,int scount,MPI_Datatype stype, void *rbuf,int rcount,MPI_Datatype rtype,MPI_Comm comm), где • sbuf, scount, stype — параметры передаваемых сообщений; • rbuf, rcount, rtype — параметры принимаемых сообщений; • comm — коммуникатор, в рамках которого выполняется передача данных. Рис. 5.6. Общая схема операции передачи данных от всех процессов всем процессам При выполнении функции MPI_Alltoall каждый процесс в коммуникаторе передает данные из scount элементов каждому процессу (общий размер отправляемых сообщений в процессах должен быть равен scount * p элементов, где p есть количество процессов в коммуникаторе comm ) и принимает сообщения от каждого процесса. Вызов функции MPI_Alltoall при выполнении операции общего обмена данными должен быть выполнен в каждом процессе коммуникатора. Вариант операции общего обмена данных, когда размеры передаваемых процессами сообщений могут быть различны, обеспечивается при помощи функций MPI_Alltoallv. Пример использования функции MPI_Alltoall рассматривается в "Параллельные методы умножения матрицы на вектор" при разработке параллельных программ умножения матрицы на вектор как задание для самостоятельного выполнения. 14.4.4. Дополнительные операции редукции данных Рассмотренная в п. 5.2.3.2 функция MPI_Reduce обеспечивает получение результатов редукции данных только на одном процессе. Для получения результатов редукции данных на каждом из процессов коммуникатора необходимо использовать функцию редукции и рассылки: int MPI_Allreduce(void *sendbuf, void *recvbuf, int count, MPI_Datatype type, MPI_Op op, MPI_Comm comm), где • sendbuf — буфер памяти с отправляемым сообщением; • recvbuf — буфер памяти для результирующего сообщения; • count — количество элементов в сообщениях; • type — тип элементов сообщений; • op — операция, которая должна быть выполнена над данными; • comm — коммуникатор, в рамках которого выполняется операция. Функция MPI_Allreduce выполняет рассылку между процессами всех результатов операции редукции. Возможность управления распределением этих данных между процессами предоставляется функций MPI_Reduce_scatter. И еще один вариант операции сбора и обработки данных, при котором обеспечивается получение всех частичных результатов редуцирования, может быть реализован при помощи функции: int MPI_Scan(void *sendbuf, void *recvbuf, int count, MPI_Datatype type, MPI_Op op, MPI_Comm comm), где • sendbuf — буфер памяти с отправляемым сообщением; • recvbuf — буфер памяти для результирующего сообщения; • count — количество элементов в сообщениях; • type — тип элементов сообщений; • op — операция, которая должна быть выполнена над данными; • comm — коммуникатор, в рамках которого выполняется операция. Общая схема выполнения функции MPI_Scan показана на рис. 5.7. Элементы получаемых сообщений представляют собой результаты обработки соответствующих элементов передаваемых процессами сообщений, при этом для получения результатов на процессе с рангом i, 0<=i
«Высокопроизводительные вычислительные системы» 👇
Готовые курсовые работы и рефераты
Купить от 250 ₽
Решение задач от ИИ за 2 минуты
Решить задачу
Помощь с рефератом от нейросети
Написать ИИ
Получи помощь с рефератом от ИИ-шки
ИИ ответит за 2 минуты

Тебе могут подойти лекции

Смотреть все 493 лекции
Все самое важное и интересное в Telegram

Все сервисы Справочника в твоем телефоне! Просто напиши Боту, что ты ищешь и он быстро найдет нужную статью, лекцию или пособие для тебя!

Перейти в Telegram Bot