Введение

Параллельное программирование – это парадигма, в которой множество процессов выполняются одновременно.

Параллельные вычисления – это тип вычислений, в которых одновременно выполняются многочисленные вычисления или процессы. Большие задачи часто можно разделить на более мелкие, которые затем можно решать параллельно. Существует несколько различных форм параллельных вычислений: параллелизм на уровне битов, инструкций, данных и задач. Параллелизм давно используется в высокопроизводительных вычислениях, но в последнее время стал представлять более широкий интерес из-за физических ограничений, препятствующих увеличению тактовой частоты. Поскольку энергопотребление (и, как следствие, тепловыделение) компьютеров стало проблемой в последние годы, параллельные вычисления стали доминирующей парадигмой в компьютерной архитектуре, главным образом в форме многоядерных процессоров. Параллельные вычисления тесно связаны с конкурентными (одновременными) вычислениями – они часто используются вместе и нередко смешиваются, хотя это разные понятия: возможен параллелизм без конкурентности и конкурентность без параллелизма (например, многозадачность с разделением времени на одноядерном процессоре). В параллельных вычислениях вычислительная задача обычно разбивается на несколько, часто множество, очень похожих подзадач, которые могут обрабатываться независимо, а результаты объединяются после завершения. В отличие от этого, в конкурентных вычислениях различные процессы часто не решают связанные задачи; когда они это делают, как это обычно бывает в распределенных вычислениях, отдельные задачи могут иметь различный характер и часто требуют межпроцессного взаимодействия во время выполнения. Параллельные компьютеры можно примерно классифицировать в зависимости от уровня, на котором аппаратное обеспечение поддерживает параллелизм: многоядерные и многопроцессорные компьютеры имеют несколько вычислительных элементов в одной машине, в то время как кластеры, MPP и вычислительные сети используют несколько компьютеров для работы над одной и той же задачей. Специализированные параллельные компьютерные архитектуры иногда используются вместе с традиционными процессорами для ускорения конкретных задач. В некоторых случаях параллелизм прозрачен для программиста, например, на уровне битов или инструкций, но явно параллельные алгоритмы, особенно те, которые используют конкурентность, сложнее писать, чем последовательные, поскольку конкурентность вводит новые классы потенциальных программных ошибок, наиболее распространенными из которых являются гонки данных. Коммуникация и синхронизация между различными подзадачами обычно являются одними из основных препятствий для достижения оптимальной производительности параллельной программы. Теоретический предел ускорения одной программы в результате параллелизации задается законом Амдала, который утверждает, что он ограничен долей времени, в течение которого может быть использована параллелизация.

Предыстория

Традиционно компьютерное программное обеспечение разрабатывалось для последовательных вычислений. Для решения задачи строится алгоритм и реализуется в виде последовательного потока инструкций. Эти инструкции выполняются на центральном процессоре одного компьютера. Одновременно может выполняться только одна инструкция – после завершения этой инструкции выполняется следующая. Параллельные вычисления, напротив, используют несколько вычислительных элементов одновременно для решения задачи. Это достигается путем разбиения задачи на независимые части, чтобы каждый вычислительный элемент мог выполнять свою часть алгоритма одновременно с другими. Вычислительные элементы могут быть разнообразными и включать в себя такие ресурсы, как один компьютер с несколькими процессорами, несколько объединенных в сеть компьютеров, специализированное оборудование или любую комбинацию вышеперечисленного. Увеличение тактовой частоты было основной причиной повышения производительности компьютеров с середины 1980-х годов до 2004 года. Время выполнения программы равно количеству инструкций, умноженному на среднее время выполнения одной инструкции. При прочих равных условиях увеличение тактовой частоты уменьшает среднее время, необходимое для выполнения инструкции. Таким образом, увеличение частоты сокращает время выполнения для всех вычислительно-интенсивных программ. Однако потребляемая мощность P чипа определяется уравнением P = C × V² × F, где C – емкость, переключаемая за один тактовый цикл (пропорциональна количеству транзисторов, у которых меняется входное состояние), V – напряжение, а F – частота процессора (циклы в секунду). Увеличение частоты приводит к увеличению потребляемой процессором мощности. Рост энергопотребления процессоров в конечном итоге привел к отмене Intel 8 мая 2004 года процессоров Tejas и Jayhawk, что обычно считается концом эры масштабирования частоты как доминирующей парадигмы компьютерной архитектуры. Для решения проблемы энергопотребления и перегрева основные производители центральных процессоров (CPU или процессоров) начали выпускать энергоэффективные процессоры с несколькими ядрами. Ядро – это вычислительная единица процессора, и в многоядерных процессорах каждое ядро является независимым и может одновременно обращаться к одной и той же памяти. Многоядерные процессоры сделали параллельные вычисления доступными для настольных компьютеров. Таким образом, параллелизация последовательных программ стала основной задачей программирования. В 2012 году четырехъядерные процессоры стали стандартом для настольных компьютеров, а серверы оснащаются процессорами с 10 и более ядрами. Согласно закону Мура, можно предсказать, что количество ядер на процессор будет удваиваться каждые 18–24 месяца. Это может означать, что после 2020 года типичный процессор будет иметь десятки или сотни ядер, однако на практике стандарт находится в диапазоне от 4 до 16 ядер, при этом некоторые конструкции сочетают ядра производительности и энергоэффективности (например, дизайн ARM big.LITTLE) из-за тепловых и конструктивных ограничений. Операционная система может обеспечить параллельное выполнение различных задач и пользовательских программ на доступных ядрах. Однако, чтобы последовательная программа в полной мере использовала преимущества многоядерной архитектуры, программисту необходимо реструктурировать и параллелизировать код. Ускорение времени выполнения прикладного программного обеспечения больше не будет достигаться за счет масштабирования частоты, вместо этого программистам необходимо будет параллелизировать свой программный код, чтобы использовать растущую вычислительную мощность многоядерных архитектур.

Плотнозернистые, грубозернистые и неудобные параллели

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

Таксономия Флинна

Майкл Флинн создал одну из первых систем классификации параллельных (и последовательных) компьютеров и программ, ныне известную как таксономия Флинна. Флинн классифицировал программы и компьютеры в зависимости от того, используют они один или несколько наборов инструкций, и один или несколько наборов данных для этих инструкций. Классификация «одна инструкция – один поток данных» (SISD) эквивалентна полностью последовательной программе. Классификация «одна инструкция – несколько потоков данных» (SIMD) аналогична многократному выполнению одной и той же операции над большим набором данных. Это часто применяется в задачах обработки сигналов. Классификация «несколько инструкций – один поток данных» (MISD) используется редко. Хотя компьютерные архитектуры для реализации этой модели и были разработаны (например, систолические массивы), практических приложений, подходящих под этот класс, практически не появилось. Программы «несколько инструкций – несколько потоков данных» (MIMD) – наиболее распространенный тип параллельных программ. По мнению Дэвида Паттерсона и Джона Л. Хеннесси, «некоторые машины, конечно, являются гибридами этих категорий, но эта классическая модель сохранилась благодаря своей простоте, понятности и хорошей начальной аппроксимации. Вероятно, именно благодаря своей понятности она является наиболее широко используемой схемой».

Параллелизм на уровне битов

С появлением технологии производства компьютерных чипов с очень высокой степенью интеграции (VLSI) в 1970-х годах и примерно до 1986 года, повышение производительности компьютерной архитектуры было обусловлено удвоением разрядности слова компьютера – объема информации, которую процессор может обработать за один цикл. Увеличение разрядности слова уменьшает количество инструкций, которые процессор должен выполнить для операции с переменными, размер которых превышает разрядность слова. Например, 8-разрядному процессору для сложения двух 16-разрядных целых чисел необходимо сначала сложить 8 младших разрядов каждого числа, используя стандартную инструкцию сложения, а затем сложить 8 старших разрядов, используя инструкцию сложения с переносом и бит переноса из сложения младших разрядов; таким образом, 8-разрядному процессору требуются две инструкции для выполнения одной операции, в то время как 16-разрядный процессор может выполнить эту операцию одной инструкцией. Исторически, 4-разрядные микропроцессоры были заменены 8-разрядными, затем 16-разрядными, а затем 32-разрядными микропроцессорами. Эта тенденция в основном завершилась с появлением 32-разрядных процессоров, которые на протяжении двух десятилетий являлись стандартом в вычислительной технике общего назначения. Лишь в начале 2000-х годов, с появлением архитектур x86-64, 64-разрядные процессоры стали широко распространены.

Параллелизм на уровне инструкции

Компьютерная программа, по сути, представляет собой поток инструкций, выполняемых процессором. Без параллелизма на уровне инструкций процессор может выдавать менее одной инструкции за такт (IPC < 1). Эти процессоры известны как субскалярные. Эти инструкции можно переупорядочить и объединить в группы, которые затем выполняются параллельно, не изменяя результат программы. Это и есть параллелизм на уровне инструкций. Достижения в области параллелизма на уровне инструкций доминировали в компьютерной архитектуре с середины 1980-х до середины 1990-х годов. Все современные процессоры имеют многоступенчатые конвейеры. Каждая ступень конвейера соответствует определенной операции, которую процессор выполняет над данной инструкцией на этом этапе; процессор с N-ступенчатым конвейером может одновременно содержать до N различных инструкций на разных стадиях выполнения и, следовательно, выдавать одну инструкцию за такт (IPC = 1). Эти процессоры известны как скалярные. Классическим примером конвейерного процессора является RISC-процессор с пятью стадиями: выборка инструкции (IF), декодирование инструкции (ID), выполнение (EX), доступ к памяти (MEM) и запись результата в регистр (WB). Процессор Pentium 4 имел 35-ступенчатый конвейер. Большинство современных процессоров также имеют несколько функциональных блоков. Обычно они сочетают эту особенность с конвейером и, таким образом, могут выдавать более одной инструкции за такт (IPC > 1). Эти процессоры известны как суперскалярные. Суперскалярные процессоры отличаются от многоядерных тем, что несколько функциональных блоков не являются полноценными процессорами (т.е. вычислительными ядрами). Инструкции можно группировать только в том случае, если между ними нет зависимостей по данным. Метод таблиц результатов (scoreboarding) и алгоритм Томасуло (который аналогичен методу таблиц результатов, но использует переименование регистров) – два из наиболее распространенных методов реализации внеочередного выполнения и параллелизма на уровне инструкций.

Параллелизм задач

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

Параллелизм на уровне суперслов

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

Память и общение

Основная память в параллельном компьютере является либо общей памятью (общей для всех процессорных элементов в едином адресном пространстве), либо распределенной памятью (в которой каждый процессорный элемент имеет собственное локальное адресное пространство). Распределенная память указывает на логическое распределение памяти, но часто подразумевает и физическое распределение. Распределенная общая память и виртуализация памяти объединяют оба подхода, предоставляя каждому процессорному элементу собственную локальную память и доступ к памяти на нелокальных процессорах. Доступ к локальной памяти обычно быстрее, чем доступ к нелокальной памяти. На суперкомпьютерах распределенное пространство общей памяти может быть реализовано с использованием модели программирования, такой как PGAS. Эта модель позволяет процессам на одном вычислительном узле прозрачно обращаться к удаленной памяти другого вычислительного узла. Все вычислительные узлы также подключены к внешней системе общей памяти через высокоскоростное соединение, например, Infiniband; эта внешняя система общей памяти известна как буфер всплеска и обычно строится из массивов энергонезависимой памяти, физически распределенных по нескольким узлам ввода-вывода. Компьютерные архитектуры, в которых ко всем элементам основной памяти можно получить доступ с одинаковой задержкой и пропускной способностью, называются системами с равномерным доступом к памяти (UMA). Обычно этого можно достичь только с помощью системы общей памяти, в которой память не распределена физически. Система, не обладающая этим свойством, известна как архитектура с неравномерным доступом к памяти (NUMA). Распределенные системы памяти имеют неравномерный доступ к памяти. Компьютерные системы используют кэши — небольшие и быстрые памяти, расположенные вблизи процессора, которые хранят временные копии значений памяти (близкие как в физическом, так и в логическом смысле). Параллельные компьютерные системы сталкиваются с трудностями, связанными с кэшами, которые могут хранить одно и то же значение в нескольких местах, что может привести к неправильному выполнению программы. Эти компьютеры требуют системы когерентности кэша, которая отслеживает кэшированные значения и стратегически сбрасывает их, обеспечивая тем самым правильное выполнение программы. Прослушивание шины — один из наиболее распространенных методов отслеживания того, к каким значениям осуществляется доступ (и, следовательно, их следует сбросить). Разработка больших, высокопроизводительных систем когерентности кэша является сложной задачей в компьютерной архитектуре. В результате, архитектуры компьютеров с общей памятью масштабируются хуже, чем распределенные системы памяти. Конфликты на шине препятствуют масштабированию архитектур шин. В результате, SMP обычно не содержат более 32 процессоров. Благодаря небольшому размеру процессоров и значительному снижению требований к пропускной способности шины, достигаемому за счет больших кэшей, такие симметричные мультипроцессоры чрезвычайно экономичны, при условии достаточной пропускной способности памяти. Одна и та же система может характеризоваться как "параллельная" и "распределенная"; процессоры в типичной распределенной системе работают одновременно параллельно.

Кластерные вычисления

Кластер — это группа слабо связанных компьютеров, тесно взаимодействующих друг с другом, так что в некоторых аспектах их можно рассматривать как единый компьютер. Кластеры состоят из нескольких независимых машин, соединенных сетью. Хотя машины в кластере не обязательно должны быть симметричными, балансировка нагрузки затруднена, если они не симметричны. Наиболее распространенным типом кластера является кластер Beowulf, реализованный на нескольких идентичных коммерчески доступных компьютерах, подключенных к локальной сети TCP/IP Ethernet. Технология Beowulf была первоначально разработана Томасом Стерлингом и Дональдом Беккером. 87% всех суперкомпьютеров, входящих в список Top500, являются кластерами. Остальные — это массивно-параллельные процессоры, о которых будет рассказано ниже. Поскольку системы распределенных вычислений (описанные ниже) легко справляются с задачами, допускающими параллельное выполнение без существенных коммуникаций, современные кластеры обычно разрабатываются для решения более сложных задач — задач, требующих от узлов более частого обмена промежуточными результатами. Это требует высокой пропускной способности и, что более важно, сети соединений с низкой задержкой. Многие исторические и современные суперкомпьютеры используют специализированное высокопроизводительное сетевое оборудование, разработанное специально для кластерных вычислений, например, сеть Cray Gemini. По состоянию на 2014 год большинство современных суперкомпьютеров используют стандартное сетевое оборудование, такое как Myrinet, InfiniBand или Gigabit Ethernet.

Массивно параллельные вычисления

Массивно параллельный процессор (MPP) – это единый компьютер, состоящий из множества объединенных в сеть процессоров. MPP обладают многими характеристиками, схожими с кластерами, однако MPP используют специализированные сети межсоединений (в то время как кластеры применяют стандартное оборудование для организации сети). MPP, как правило, крупнее кластеров и обычно содержат значительно более 100 процессоров. В MPP "каждый процессор имеет собственную память и копию операционной системы и приложения. Каждая подсистема обменивается данными с другими через высокоскоростное межсоединение". IBM Blue Gene/L, пятый по скорости суперкомпьютер в мире по данным рейтинга TOP500 на июнь 2009 года, является MPP.

Вычисления сетей

Сетевые вычисления – наиболее распределенная форма параллельных вычислений. Они используют компьютеры, взаимодействующие через Интернет, для решения определенной задачи. Из-за низкой пропускной способности и очень высокой задержки в Интернете, распределенные вычисления обычно применяются только к задачам, легко распараллеливаемым. Большинство приложений для сетевых вычислений используют промежуточное программное обеспечение (программное обеспечение, расположенное между операционной системой и приложением для управления сетевыми ресурсами и стандартизации программного интерфейса). Наиболее распространенным промежуточным программным обеспечением для сетевых вычислений является Berkeley Open Infrastructure for Network Computing (BOINC). Часто программы добровольных вычислений используют "свободные ресурсы процессора", выполняя вычисления в периоды простоя компьютера.

Облачные вычисления

Повсеместное распространение Интернета сделало возможным крупномасштабные облачные вычисления.

Специализированные параллельные компьютеры

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

Реконфигурируемые вычисления с полевыми программируемыми массивами шлюзов

Реконфигурируемые вычисления – это использование полевой программируемой вентильной матрицы (FPGA) в качестве сопроцессора для компьютера общего назначения. FPGA, по сути, представляет собой компьютерный чип, который может переконфигурировать себя для выполнения конкретной задачи. FPGA могут быть запрограммированы с помощью языков описания аппаратуры, таких как VHDL или Verilog. Несколько производителей разработали языки C to HDL, которые стремятся эмулировать синтаксис и семантику языка программирования C, с которым знакомы большинство программистов. Наиболее известные языки C to HDL – Mitrion C, Impulse C и Handel C. Для этих целей также могут использоваться специфические подмножества SystemC, основанные на C++. Решение AMD открыть свою технологию HyperTransport для сторонних производителей стало ключевой технологией для высокопроизводительных реконфигурируемых вычислений. По словам Майкла Р. Д’Амура, главного операционного директора DRC Computer Corporation, «когда мы впервые пришли в AMD, они называли нас «похитителями сокетов». Теперь они называют нас своими партнерами». Обработка компьютерной графики – это область, где преобладают параллельные операции с данными, особенно операции линейной алгебры с матрицами. На заре развития GPGPU-программы использовали стандартные графические API для выполнения программ. Однако было разработано несколько новых языков программирования и платформ для выполнения вычислений общего назначения на графических процессорах, при этом Nvidia и AMD выпустили среды программирования CUDA и Stream SDK соответственно. Другие языки программирования для GPU включают BrookGPU, PeakStream и RapidMind. Nvidia также выпустила специализированные продукты для вычислений в линейке Tesla. Технологический консорциум Khronos Group выпустил спецификацию OpenCL, которая представляет собой фреймворк для написания программ, выполняемых на платформах, состоящих из процессоров и графических процессоров. AMD, Apple, Intel, Nvidia и другие компании поддерживают OpenCL.

Специфические для конкретного применения интегральные схемы

Для решения параллельных задач разработано несколько подходов к использованию специализированных интегральных схем (ASIC). Поскольку ASIC (по определению) предназначен для конкретного приложения, он может быть полностью оптимизирован под это приложение. В результате, для заданного приложения ASIC, как правило, демонстрирует более высокую производительность, чем компьютер общего назначения. Однако ASIC создаются с помощью ультрафиолетовой фотолитографии, для которой требуется набор масок, стоимость которых может быть чрезвычайно высокой. Набор масок может стоить более миллиона долларов США. (Чем меньше размеры транзисторов, необходимых для чипа, тем дороже будет стоить набор масок.) В то же время, повышение производительности компьютеров общего назначения со временем (в соответствии с законом Мура) обычно нивелирует эти преимущества всего за одно-два поколения чипов. Они тесно связаны с классификацией SIMD Флинна. Одной из концепций, используемых при программировании параллельных программ, является концепция "обещания" (future), когда одна часть программы гарантирует предоставление необходимых данных другой части программы в будущем. К усилиям по стандартизации параллельного программирования относится открытый стандарт OpenHMPP для гибридного многоядерного параллельного программирования. Модель программирования OpenHMPP, основанная на директивах, предоставляет синтаксис для эффективной передачи вычислений на аппаратные ускорители и оптимизации перемещения данных в/из аппаратной памяти с использованием удалённых вызовов процедур. Распространение потребительских графических процессоров (GPU) привело к поддержке вычислительных ядер, либо в графических API (именуемых вычислительными шейдерами), в специализированных API (таких как OpenCL), либо в других расширениях языка программирования.

Автоматическая параллелизация

Автоматическая параллелизация последовательной программы компилятором считается "святым граалем" параллельных вычислений, особенно в свете упомянутого ограничения частоты процессоров. Несмотря на десятилетия исследований в области компиляторов, автоматическая параллелизация добилась лишь ограниченного успеха. Основные языки параллельного программирования остаются либо явно параллельными, либо (в лучшем случае) частично неявными, где программист предоставляет компилятору указания по параллелизации. Существует небольшое количество полностью неявных языков параллельного программирования: SISAL, Parallel Haskell, SequenceL, System C (для ПЛИС), Mitrion C, VHDL и Verilog.

Контрольные точки приложения

По мере роста сложности компьютерной системы среднее время между сбоями обычно сокращается. Контрольная точка приложения – это техника, при которой компьютерная система делает «снимок» состояния приложения, то есть сохраняет информацию обо всех текущих распределениях ресурсов и значениях переменных, подобно дампу памяти; эта информация может быть использована для восстановления программы в случае сбоя. Благодаря контрольным точкам приложение может быть перезапущено с последней сохраненной точки, а не с самого начала. Хотя контрольные точки полезны в различных ситуациях, они особенно эффективны в высокопараллельных системах с большим количеством процессоров, применяемых в высокопроизводительных вычислениях.

Допустимость неисправности

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

История

[[Файл:ILLIAC 4 параллельный компьютер. jpg|right|thumbnail|ILLIAC IV, "самый печально известный из суперкомпьютеров"

В 1957 году компания Compagnie des Machines Bull представила первую компьютерную архитектуру, специально разработанную для параллелизма, – Gamma 60. Она использовала модель "fork join" и "распределитель программ" для отправки и получения данных из независимых процессорных блоков, подключенных к центральной памяти. В апреле 1958 года Стэнли Гилл (Ferranti) обсудил параллельное программирование и необходимость ветвления и ожидания. Также в 1958 году исследователи IBM Джон Кокк и Дэниел Слотник впервые обсудили применение параллелизма в численных расчетах. В 1962 году корпорация Burroughs представила D825 – четырехпроцессорный компьютер, который мог обращаться до 16 модулей памяти через матричный коммутатор. В 1967 году Амдал и Слотник опубликовали дискуссию о целесообразности параллельной обработки на конференции Американской федерации обществ по обработке информации. В 1964 году Слотник предложил создать массивно-параллельный компьютер для Лоуренс-Ливерморской национальной лаборатории. Когда он был наконец готов к запуску первого реального приложения в 1976 году, его производительность оказалась ниже, чем у существующих коммерческих суперкомпьютеров, таких как Cray 1.