В вычислительной технике алгоритмические скелеты , или шаблоны параллелизма , представляют собой высокоуровневую модель параллельного программирования для параллельных и распределенных вычислений.
Алгоритмические скелеты используют преимущества общих шаблонов программирования, чтобы скрыть сложность параллельных и распределенных приложений. Начиная с базового набора шаблонов (скелетов), можно построить более сложные шаблоны, комбинируя базовые.
Наиболее выдающейся особенностью алгоритмических скелетов, которая отличает их от других высокоуровневых моделей параллельного программирования, является то, что оркестровка и синхронизация параллельных действий неявно определяются шаблонами скелета. Программистам не нужно указывать синхронизации между последовательными частями приложения. Это приводит к двум последствиям. Во-первых, поскольку шаблоны связи/доступа к данным известны заранее, модели затрат могут применяться для планирования программ скелетов. [1] Во-вторых, то, что алгоритмическое программирование скелета уменьшает количество ошибок по сравнению с традиционными низкоуровневыми моделями параллельного программирования (потоки, MPI).
Следующий пример основан на библиотеке Java Skandium для параллельного программирования.
Цель состоит в том, чтобы реализовать параллельную версию алгоритма QuickSort на основе Algorithmic Skeleton с использованием шаблона Divide and Conquer. Обратите внимание, что высокоуровневый подход скрывает управление потоками от программиста.
// 1. Определим скелетную программу Skeleton < Range , Range > sort = new DaC < Range , Range > ( new ShouldSplit ( threshold , maxTimes ), new SplitList (), new Sort (), new MergeList ()); // 2. Входные параметры Future < Range > future = sort . input ( new Range ( generate (...))); // 3. Сделайте здесь что-нибудь еще. // ... // 4. Блок для результатов Range result = future . get ();
Функциональные коды в этом примере соответствуют четырем типам: «Условие», «Разделение», «Выполнение» и «Объединение».
открытый класс ShouldSplit реализует Condition < Range > { int threshold , maxTimes , times ; public ShouldSplit ( int threshold , int maxTimes ){ this . threshold = threshold ; this . maxTimes = maxTimes ; this . times = 0 ; } @Override public synchronized boolean condition ( Range r ){ return r . right - r . left > threshold && times ++ < this . maxTimes ; } }
Класс ShouldSplit реализует интерфейс Condition. Функция получает входные данные, в данном случае Range r, и возвращает true или false. В контексте Divide and Conquer, где будет использоваться эта функция, это определит, следует ли снова разделить подмассив или нет.
Класс SplitList реализует интерфейс split, который в этом случае делит (под-)массив на меньшие подмассивы. Класс использует вспомогательную функцию partition(...)
, которая реализует известную схему QuickSort pivot and swap.
открытый класс SplitList реализует Split < Range , Range > { @Override public Range [] split ( Range r ) { int i = partition ( r . array , r . left , r . right ); Range [] intervals = { new Range ( r . array , r . left , i - 1 ), new Range ( r . array , i + 1 , r . right )}; return intervals ; } }
Класс Sort реализует интерфейс Execute и отвечает за сортировку подмассива, указанного с помощью Range r
. В этом случае мы просто вызываем метод Java по умолчанию (Arrays.sort) для данного подмассива.
открытый класс Sort реализует Execute < Range , Range > { @Override public Range execute ( Range r ) { if ( r.right < = r.left ) return r ; Arrays.sort ( r.array , r.left , r.right + 1 ) ; return r ; } }
Наконец, после сортировки набора подмассивов мы объединяем части подмассива в больший массив с помощью класса MergeList, который реализует интерфейс Merge.
открытый класс MergeList реализует Merge < Range , Range > { @Override public Range merge ( Range [] r ){ Range result = new Range ( r [ 0 ] . array , r [ 0 ] . left , r [ 1 ] . right ); return result ; } }
ASSIST [2] [3] — это среда программирования, которая предоставляет программистам структурированный язык координации. Язык координации может выражать параллельные программы как произвольный граф программных модулей. Граф модулей описывает, как набор модулей взаимодействует друг с другом с использованием набора типизированных потоков данных. Модули могут быть последовательными или параллельными. Последовательные модули могут быть написаны на C, C++ или Fortran; а параллельные модули программируются с помощью специального параллельного модуля ASSIST ( parmod ).
AdHoc, [4] [5] иерархическая и отказоустойчивая система распределенной общей памяти (DSM) используется для соединения потоков данных между элементами обработки, предоставляя репозиторий с операциями: get/put/remove/execute. Исследования вокруг AdHoc были сосредоточены на прозрачности, масштабируемости и отказоустойчивости репозитория данных.
Хотя это и не классический каркас скелета, в том смысле, что скелеты не предусмотрены, универсальный parmod ASSIST может быть специализирован в классических скелетах, таких как: farm , map и т. д. ASSIST также поддерживает автономное управление parmods и может подчиняться контракту производительности путем динамической адаптации количества используемых ресурсов.
CO2P3S (правильная объектно-ориентированная система параллельного программирования на основе шаблонов) — это среда разработки, ориентированная на шаблоны, [6] которая обеспечивает параллелизм с помощью потоков в Java.
CO2P3S занимается полным процессом разработки параллельного приложения. Программисты взаимодействуют через программный графический интерфейс, чтобы выбрать шаблон и его параметры конфигурации. Затем программисты заполняют хуки, необходимые для шаблона, и новый код генерируется как фреймворк на Java для параллельного выполнения приложения. Сгенерированный фреймворк использует три уровня в порядке убывания абстракции: слой шаблонов, слой промежуточного кода и слой собственного кода. Таким образом, продвинутые программисты могут вмешиваться в сгенерированный код на нескольких уровнях, чтобы настроить производительность своих приложений. Сгенерированный код в основном типобезопасен , используя типы, предоставленные программистом, которые не требуют расширения суперкласса, но не является полностью типобезопасным, например, в методе reduce(..., Object reducer) в шаблоне сетки.
Набор шаблонов, поддерживаемых в CO2P3S, соответствует последовательности методов, дистрибьютору, сетке и волновому фронту. Сложные приложения могут быть построены путем составления фреймворков с их объектными ссылками. Тем не менее, если ни один шаблон не подходит, графический инструмент MetaCO2P3S решает проблему расширяемости, позволяя программистам изменять дизайн шаблонов и вводить новые шаблоны в CO2P3S.
Поддержка распределенной архитектуры памяти в CO2P3S была введена позже. [7] Чтобы использовать шаблон распределенной памяти, программисты должны изменить параметр памяти шаблона с общего на распределенный и сгенерировать новый код. С точки зрения использования, версия кода с распределенной памятью требует управления удаленными исключениями.
Calcium во многом вдохновлен Lithium и Muskel. Таким образом, он предоставляет алгоритмическое программирование скелета в виде библиотеки Java. Как задачи, так и параллельные скелеты данных полностью вложены; и инстанцируются через параметрические скелетные объекты, а не наследование.
Calcium поддерживает выполнение скелетных приложений поверх среды ProActive для распределенной кластерной инфраструктуры. Кроме того, Calcium имеет три отличительные особенности для алгоритмического скелетного программирования. Во-первых, модель настройки производительности, которая помогает программистам идентифицировать код, ответственный за ошибки производительности. [8] Во-вторых, система типов для вложенных скелетов, которая, как доказано, гарантирует свойства редукции субъекта и реализована с использованием Java Generics. [9] В-третьих, прозрачная модель доступа к файлам алгоритмического скелета, которая позволяет использовать скелеты для приложений с интенсивным использованием данных. [10]
Skandium — это полная переработка Calcium для многоядерных вычислений. Программы, написанные на Skandium, могут использовать преимущества общей памяти для упрощения параллельного программирования. [11]
Eden [12] — параллельный язык программирования для сред с распределенной памятью, расширяющий Haskell. Процессы определяются явно для достижения параллельного программирования, в то время как их коммуникации остаются неявными. Процессы взаимодействуют через однонаправленные каналы, которые соединяют одного писателя с ровно одним читателем. Программистам нужно только указать, от каких данных зависит процесс. Модель процессов Eden обеспечивает прямой контроль над гранулярностью процесса, распределением данных и топологией коммуникаций.
Eden не является скелетным языком в том смысле, что скелеты не предоставляются как языковые конструкции. Вместо этого скелеты определяются поверх абстракции низкоуровневого процесса Eden, поддерживая как параллелизм задач, так и параллелизм данных . Таким образом, в отличие от большинства других подходов, Eden позволяет определять скелеты на том же языке и на том же уровне, экземпляр скелета записывается: сам Eden. Поскольку Eden является расширением функционального языка, скелеты Eden являются функциями более высокого порядка . Eden вводит концепцию скелета реализации, которая является архитектурно-независимой схемой, описывающей параллельную реализацию алгоритмического скелета.
Библиотека Edinburgh Skeleton Library ( eSkel ) написана на языке C и работает поверх MPI. Первая версия eSkel была описана в [13], а более поздняя версия представлена в [14] .
В [15] определены режим вложенности и режим взаимодействия для скелетов. Режим вложенности может быть как временным, так и постоянным, в то время как режим взаимодействия может быть как неявным, так и явным. Временная вложенность означает, что вложенный скелет создается для каждого вызова и впоследствии уничтожается, в то время как постоянная означает, что скелет создается один раз, и один и тот же экземпляр скелета будет вызываться во всем приложении. Неявное взаимодействие означает, что поток данных между скелетами полностью определяется композицией скелета, в то время как явное означает, что данные могут быть созданы или удалены из потока способом, не указанным композицией скелета. Например, скелет, который производит вывод, никогда не получая ввод, имеет явное взаимодействие.
Прогнозирование производительности для планирования и сопоставления ресурсов, в основном для конвейеров, было исследовано Бенуа и др. [16] [17] [18] [19]. Они предоставили модель производительности для каждого сопоставления, основанную на алгебре процессов, и определили наилучшую стратегию планирования на основе результатов модели.
Более поздние работы были посвящены проблеме адаптации структурированного параллельного программирования, [20] в частности, для скелета трубы. [21] [22]
FastFlow — это каркасный параллельный программный фреймворк, специально предназначенный для разработки потоковых и параллельных по данным приложений. Первоначально разработанный для многоядерных платформ, он был успешно расширен для гетерогенных платформ, состоящих из кластеров платформ с общей памятью, [23] [24] возможно, оснащенных вычислительными ускорителями, такими как NVidia GPGPU, Xeon Phi, Tilera TILE64. Основная философия проектирования FastFlow заключается в предоставлении разработчикам приложений ключевых функций для параллельного программирования (например, время выхода на рынок, переносимость, эффективность и переносимость производительности) с помощью подходящих абстракций параллельного программирования и тщательно разработанной поддержки времени выполнения. [25] FastFlow — это универсальный программный фреймворк C++ для гетерогенных параллельных платформ. Как и другие высокоуровневые фреймворки программирования, такие как Intel TBB и OpenMP, он упрощает проектирование и разработку переносимых параллельных приложений. Однако он имеет явное преимущество в плане выразительности и производительности по сравнению с другими фреймворками параллельного программирования в конкретных сценариях применения, включая, среди прочего: мелкозернистый параллелизм на платформах с общей памятью, когерентной по кэшу; потоковые приложения; сопряженное использование многоядерности и ускорителей. В других случаях FastFlow обычно сопоставим (а в некоторых случаях немного быстрее) с современными фреймворками параллельного программирования, такими как Intel TBB, OpenMP, Cilk и т. д. [26]
Higher-order Divide and Conquer ( HDC ) [27] является подмножеством функционального языка Haskell . Функциональные программы представлены как полиморфные функции высшего порядка, которые могут быть скомпилированы в C/MPI и связаны с реализациями скелета. Язык фокусируется на парадигме разделяй и властвуй, и, начиная с общего вида скелета разделяй и властвуй, выводятся более конкретные случаи с эффективными реализациями. Конкретные случаи соответствуют: фиксированной глубине рекурсии, постоянной степени рекурсии, множественной блочной рекурсии, поэлементным операциям и соответствующим коммуникациям [28]
HDC уделяет особое внимание гранулярности подзадачи и ее связи с числом доступных процессоров. Общее число процессоров является ключевым параметром для производительности скелетной программы, поскольку HDC стремится оценить адекватное назначение процессоров для каждой части программы. Таким образом, производительность приложения тесно связана с предполагаемым числом процессоров, что приводит либо к превышению числа подзадач, либо к недостаточному параллелизму для использования доступных процессоров.
HOC-SA — проект инкубатора Globus.
HOC-SA означает Higher-Order Components-Service Architecture (Архитектура компонентов-сервисов высшего порядка). Целью компонентов высшего порядка (HOC) является упрощение разработки приложений Grid.
Целью HOC-SA является предоставление пользователям Globus, которые не хотят знать все подробности промежуточного программного обеспечения Globus (документы GRAM RSL, веб-сервисы и конфигурация ресурсов и т. д.), HOC, которые предоставляют интерфейс более высокого уровня для Grid, чем основной Globus Toolkit.
HOC — это каркасы с поддержкой Grid, реализованные как компоненты поверх Globus Toolkit, с удаленным доступом через веб-сервисы. [29]
JaSkel [30] — это каркас скелета на основе Java, предоставляющий скелеты, такие как ферма, конвейер и heartbeat. Скелеты специализированы с использованием наследования. Программисты реализуют абстрактные методы для каждого скелета, чтобы предоставить свой специфичный для приложения код. Скелеты в JaSkel предоставляются как в последовательной, так и в параллельной и динамической версиях. Например, параллельная ферма может использоваться в средах с общей памятью (потоках), но не в распределенных средах (кластерах), где должна использоваться распределенная ферма. Чтобы перейти с одной версии на другую, программисты должны изменить сигнатуру своих классов, чтобы наследовать от другого скелета. Вложенность скелетов использует базовый класс Java Object, и поэтому никакая система типов не применяется во время композиции скелета.
Распределительные аспекты вычислений обрабатываются в JaSkel с использованием AOP, а точнее реализации AspectJ. Таким образом, JaSkel может быть развернут как в кластерных, так и в Grid-подобных инфраструктурах. [31] Тем не менее, недостатком подхода JaSkel является то, что вложенность скелета строго связана с инфраструктурой развертывания. Таким образом, двойная вложенность фермы обеспечивает лучшую производительность, чем одна ферма в иерархических инфраструктурах. Это противоречит цели использования AOP для разделения распределения и функциональных задач скелетной программы.
Lithium [32] [33] [34] и его преемник Muskel являются каркасными фреймворками, разработанными в Университете Пизы, Италия. Оба они предоставляют программисту вложенные каркасы в виде библиотек Java. Оценка каркасного приложения следует формальному определению операционной семантики, введенному Олдинуччи и Данелутто, [35] [36] , которое может обрабатывать как параллелизм задач, так и параллелизм данных. Семантика описывает как функциональное, так и параллельное поведение скелетного языка с использованием маркированной системы переходов. Кроме того, применяется несколько оптимизаций производительности, таких как: методы переписывания скелета [18, 10], опережающий просмотр задач и ленивое связывание сервера с сервером. [37]
На уровне реализации Lithium использует поток макроданных [38] [39] для достижения параллелизма. Когда входной поток получает новый параметр, скелетная программа обрабатывается для получения графа потока макроданных. Узлы графа — это инструкции потока макроданных (MDFi), которые представляют собой последовательные фрагменты кода, предоставленные программистом. Задачи используются для группировки нескольких MDFi и потребляются простаивающими элементами обработки из пула задач. Когда вычисление графа завершено, результат помещается в выходной поток и, таким образом, доставляется обратно пользователю.
Muskel также предоставляет нефункциональные возможности, такие как качество обслуживания (QoS); [40] безопасность между пулом задач и интерпретаторами; [41] [42] и обнаружение ресурсов, балансировка нагрузки и отказоустойчивость при взаимодействии с Java / Jini Parallel Framework (JJPF), [43] фреймворком распределенного выполнения. Muskel также предоставляет поддержку для объединения структурированного и неструктурированного программирования [44] , а недавние исследования были посвящены расширяемости. [45]
Mallba [46] — это библиотека для комбинаторной оптимизации, поддерживающая точные, эвристические и гибридные стратегии поиска. [47] Каждая стратегия реализована в Mallba как общий скелет, который может использоваться путем предоставления необходимого кода. Для точных алгоритмов поиска Mallba предоставляет скелеты ветвей и границ и динамической оптимизации. Для эвристик локального поиска Mallba поддерживает: восхождение на холм , метрополию, имитацию отжига и поиск с запретами ; а также эвристики на основе популяции, полученные из эволюционных алгоритмов, таких как генетические алгоритмы , стратегия эволюции и другие (CHC). Гибридные скелеты объединяют стратегии, такие как: GASA, смесь генетического алгоритма и имитации отжига, и CHCCES, который объединяет CHC и ES.
Скелеты предоставляются как библиотека C++ и не являются вложенными, но безопасными по типу. Используется пользовательский уровень абстракции MPI, NetStream, который заботится о маршаллинге примитивных типов данных, синхронизации и т. д. Скелет может иметь несколько параллельных реализаций нижнего уровня в зависимости от целевых архитектур: последовательных, LAN и WAN. Например: централизованный master-slave, распределенный master-slave и т. д.
Mallba также предоставляет переменные состояния, которые содержат состояние скелета поиска. Состояние связывает поиск с окружающей средой и может быть доступно для проверки эволюции поиска и принятия решения о будущих действиях. Например, состояние может использоваться для хранения наилучшего решения, найденного до сих пор, или значений α, β для обрезки ветвей и границ. [48]
По сравнению с другими фреймворками, использование концепций скелетов в Mallba уникально. Скелеты предоставляются как параметрические стратегии поиска, а не параметрические шаблоны распараллеливания.
Marrow [49] [50] — это алгоритмический каркас C++ для оркестровки вычислений OpenCL в, возможно, гетерогенных, многопроцессорных средах . Он предоставляет набор скелетов как задач, так и параллельных данных, которые могут быть составлены посредством вложенности для построения составных вычислений. Листовые узлы результирующих деревьев композиции представляют вычислительные ядра GPU, в то время как оставшиеся узлы обозначают скелет, применяемый к вложенному поддереву. Каркас берет на себя всю оркестровку на стороне хоста, необходимую для правильного выполнения этих деревьев в гетерогенных многопроцессорных средах, включая надлежащий порядок передачи данных и запросов на выполнение, а также требуемую связь между узлами дерева.
Среди наиболее отличительных особенностей Marrow — набор скелетов, ранее недоступных в контексте GPU, таких как Pipeline и Loop, а также возможность вложения скелетов — функция, также новая в этом контексте. Более того, фреймворк вводит оптимизации, которые перекрывают связь и вычисления, тем самым маскируя задержку, налагаемую шиной PCIe .
Параллельное выполнение дерева композиции Marrow несколькими GPU следует стратегии параллельной декомпозиции данных, которая одновременно применяет все вычислительное дерево к различным разделам входного набора данных. За исключением выражения того, какие параметры ядра могут быть декомпозированы, и, при необходимости, определения того, как должны быть объединены частичные результаты, программист полностью абстрагируется от базовой архитектуры с несколькими GPU.
Более подробную информацию, а также исходный код можно найти на сайте Marrow.
Muenster Skeleton Library Muesli [51] [52] — это библиотека шаблонов C++, которая повторно реализует многие идеи и концепции, представленные в Skil, например, функции высшего порядка, каррирование и полиморфные типы [1]. Она построена на основе MPI 1.2 и OpenMP 2.5 и поддерживает, в отличие от многих других библиотек скелетов, как скелеты параллельных задач, так и данных. Вложенность скелетов (композиция) похожа на двухуровневый подход P3L, т. е. скелеты параллельных задач могут быть вложены произвольно, в то время как скелеты параллельных данных не могут, но могут использоваться на листьях дерева вложенности параллельных задач. [53] Шаблоны C++ используются для рендеринга скелетов полиморфными, но никакая система типов не применяется. Однако библиотека реализует механизм автоматизированной сериализации, вдохновленный [54] , так что в дополнение к стандартным типам данных MPI в скелетах могут использоваться произвольные пользовательские типы данных. Поддерживаемые скелеты параллельных задач [55] — Branch & Bound, [56] Divide & Conquer, [57] [58] Farm, [59] [60] и Pipe, вспомогательные скелеты — Filter, Final и Initial. Скелеты параллельных данных, такие как fold (reduce), map, permute, zip и их варианты, реализованы как функции-члены более высокого порядка распределенной структуры данных. В настоящее время Muesli поддерживает распределенные структуры данных для массивов, матриц и разреженных матриц. [61]
Как уникальная функция, параллельные скелеты данных Muesli автоматически масштабируются как на одноядерных, так и на многоузловых кластерных архитектурах. [62] [63] Здесь масштабируемость по узлам и ядрам обеспечивается одновременным использованием MPI и OpenMP соответственно. Однако эта функция является необязательной в том смысле, что программа, написанная с помощью Muesli, по-прежнему компилируется и запускается на одноядерном многоузловом кластерном компьютере без внесения изменений в исходный код, т. е. гарантируется обратная совместимость. Это обеспечивается предоставлением очень тонкого уровня абстракции OpenMP, так что поддержка многоядерных архитектур может быть включена/выключена простым предоставлением/пропуском флага компилятора OpenMP при компиляции программы. Благодаря этому фактически не возникает никаких накладных расходов во время выполнения.
P3L [64] (Pisa Parallel Programming Language) — это язык координации на основе скелета. P3L предоставляет скелетные конструкции, которые используются для координации параллельного или последовательного выполнения кода C. Для языка предоставляется компилятор Anacleto [65] . Anacleto использует шаблоны реализации для компиляции кода P3 L в целевую архитектуру. Таким образом, скелет может иметь несколько шаблонов, каждый из которых оптимизирован для другой архитектуры. Шаблон реализует скелет на определенной архитектуре и предоставляет параметрический граф процесса с моделью производительности. Затем модель производительности может использоваться для принятия решения о преобразованиях программы, которые могут привести к оптимизации производительности. [66]
Модуль P3L соответствует правильно определенной конструкции скелета с входными и выходными потоками и другими подмодулями или последовательным кодом C. Модули могут быть вложены с использованием двухуровневой модели, где внешний уровень состоит из параллельных скелетов задач, в то время как параллельные скелеты данных могут использоваться на внутреннем уровне [64]. Проверка типа выполняется на уровне потока данных, когда программист явно указывает тип входных и выходных потоков, а также путем указания потока данных между подмодулями.
SkIE [67] (Skeleton-based Integrated Environment) очень похож на P3L , так как он также основан на языке координации, но предоставляет расширенные функции, такие как инструменты отладки, анализ производительности, визуализация и графический пользовательский интерфейс. Вместо прямого использования языка координации программисты взаимодействуют с графическим инструментом, где можно составлять параллельные модули, основанные на скелетах.
SKELib [68] основывается на вкладах P3L и SkIE , наследуя, среди прочего, систему шаблонов. Он отличается от них тем, что больше не используется язык координации, а вместо этого скелеты предоставляются как библиотека на языке C, с производительностью, аналогичной той, которая достигнута в P3L . В отличие от Skil , другого каркаса скелетов на языке C, безопасность типов не рассматривается в SKELib .
PAS (Parallel Architectural Skeletons) — это фреймворк для программирования скелетов, разработанный на C++ и MPI. [69] [70] Программисты используют расширение C++ для написания своих скелетных приложений1. Затем код передается через скрипт Perl, который расширяет код до чистого C++, где скелеты специализированы посредством наследования.
В PAS каждый скелет имеет объект-представитель (Rep), который должен быть предоставлен программистом и отвечает за координацию выполнения скелета. Скелеты могут быть вложены в иерархическом порядке с помощью объектов Rep. Помимо выполнения скелета, Rep также явно управляет приемом данных от скелета более высокого уровня и отправкой данных в подскелеты. Параметризованный протокол связи/синхронизации используется для отправки и получения данных между родительским и подскелетами.
Расширение PAS, обозначенное как SuperPas [71] и позднее как EPAS [72], решает проблемы расширяемости скелета. С помощью инструмента EPAS в PAS можно добавлять новые скелеты . Язык описания скелета (SDL) используется для описания шаблона скелета путем указания топологии относительно сетки виртуальных процессоров. Затем SDL можно скомпилировать в собственный код C++, который можно использовать как любой другой скелет.
SBASCO ( Skeleton-BAsed Scientific COmponents ) — это среда программирования, ориентированная на эффективную разработку параллельных и распределенных численных приложений. [73] SBASCO нацелена на интеграцию двух моделей программирования: скелетов и компонентов с пользовательским языком композиции. Представление приложения компонента предоставляет описание его интерфейсов (тип ввода и вывода); в то время как представление конфигурации предоставляет, кроме того, описание внутренней структуры компонента и компоновки процессора. Внутренняя структура компонента может быть определена с использованием трех скелетов: фермы, трубы и многоблочного.
SBASCO адресует доменные разложимые приложения через свой многоблочный скелет. Домены указываются через массивы (в основном двумерные), которые разлагаются на подмассивы с возможными перекрывающимися границами. Затем вычисление происходит итеративным способом, подобным BSP. Первый этап состоит из локальных вычислений, в то время как второй этап выполняет обмен границами. Пример использования представлен для задачи реакции-диффузии в. [74]
Два типа компонентов представлены в [75] Scientific Components (SC), которые предоставляют функциональный код; и Communication Aspect Components (CAC), которые инкапсулируют нефункциональное поведение, такое как коммуникация, распределение процессора макета и репликация. Например, компоненты SC подключены к компоненту CAC, который может действовать как менеджер во время выполнения, динамически переназначая процессоры, назначенные SC. Пример использования, показывающий улучшенную производительность при использовании компонентов CAC, показан в [76] .
Язык структурированной координации ( SCL ) [77] был одним из самых ранних языков программирования скелетов. Он обеспечивает подход языка координации для программирования скелетов над компонентами программного обеспечения. SCL считается базовым языком и был разработан для интеграции с хост-языком, например, Fortran или C, используемым для разработки последовательных компонентов программного обеспечения. В SCL скелеты подразделяются на три типа: конфигурация , элементарные и вычисления . Скелеты конфигурации абстрактные шаблоны для часто используемых структур данных, таких как распределенные массивы (ParArray). Элементарные скелеты соответствуют параллельным скелетам данных, таким как map, scan и fold. Скелеты вычислений, которые абстрагируют поток управления и соответствуют в основном параллельным скелетам задач, таким как farm, SPMD и iterateUntil. Подход языка координации использовался в сочетании с моделями производительности для программирования традиционных параллельных машин, а также параллельных гетерогенных машин, которые имеют различные множественные ядра на каждом узле обработки. [78]
SkePU [79] SkePU — это каркас программирования скелетов для многоядерных ЦП и многопроцессорных систем. Это библиотека шаблонов C++ с шестью скелетами параллельных данных и одним скелетом параллельных задач, двумя типами контейнеров и поддержкой выполнения на многопроцессорных системах как с CUDA, так и с OpenCL. Недавно в SkePU была разработана поддержка гибридного выполнения, динамического планирования с учетом производительности и балансировки нагрузки путем внедрения бэкэнда для системы выполнения StarPU. SkePU расширяется для кластеров GPU.
SKiPPER — это библиотека скелетов, специфичная для домена, для приложений машинного зрения [80] , которая предоставляет скелеты в CAML и, таким образом, полагается на CAML для обеспечения безопасности типов. Скелеты представлены двумя способами: декларативным и операционным. Декларативные скелеты напрямую используются программистами, в то время как их операционные версии предоставляют целевую реализацию, специфичную для архитектуры. Из среды выполнения, спецификаций скелета CAML и функций, специфичных для приложения (предоставленных программистом на языке C), генерируется и компилируется новый код C для запуска приложения на целевой архитектуре. Одна из интересных особенностей SKiPPER заключается в том, что программа-скелет может выполняться последовательно для отладки.
В SKiPPER были исследованы различные подходы к написанию операционных скелетов: статические графы потоков данных, параметрические сети процессов, иерархические графы задач и графы потоков данных с тегами-токенами. [81]
QUAFF [82] — более поздняя библиотека скелетов, написанная на C++ и MPI. QUAFF опирается на методы метапрограммирования на основе шаблонов для сокращения накладных расходов во время выполнения и выполнения расширений скелетов и оптимизаций во время компиляции. Скелеты могут быть вложенными, а последовательные функции сохраняют состояние. Помимо проверки типов, QUAFF использует преимущества шаблонов C++ для генерации во время компиляции нового кода C/MPI. QUAFF основан на модели CSP, где программа скелета описывается как сеть процессов и правила производства (одиночные, последовательные, парные, объединенные). [83]
Проект SkeTo [84] — это библиотека C++, которая достигает параллелизации с помощью MPI. SkeTo отличается от других библиотек скелетов, поскольку вместо предоставления вложенных шаблонов параллелизма SkeTo предоставляет параллельные скелеты для параллельных структур данных, таких как: списки, деревья [ 85] [86] и матрицы. [87] Структуры данных типизируются с помощью шаблонов, и на них можно вызывать несколько параллельных операций. Например, структура списка предоставляет параллельные операции, такие как: map, reduce, scan, zip, shift и т. д.
Дополнительные исследования вокруг SkeTo также были сосредоточены на стратегиях оптимизации путем преобразования и, в последнее время, на оптимизации, специфичной для домена. [88] Например, SkeTo обеспечивает преобразование слияния [89] , которое объединяет два последовательных вызова функции в один, тем самым уменьшая накладные расходы на вызов функции и избегая создания промежуточных структур данных, передаваемых между функциями.
Skil [90] — императивный язык для программирования скелетов. Скелеты не являются непосредственной частью языка, но реализуются с его помощью. Skil использует подмножество языка C, которое предоставляет функциональные языковые возможности, такие как функции высшего порядка, curring и полиморфные типы. Когда Skil компилируется, такие возможности устраняются и создается обычный код C. Таким образом, Skil преобразует полиморфные функции высшего порядка в мономорфные функции C первого порядка. Skil не поддерживает вложенную композицию скелетов. Параллелизм данных достигается с помощью определенных параллельных структур данных, например, для распределения массивов между доступными процессорами. Можно использовать скелеты фильтров.
В STAPL Skeleton Framework [91] [92] скелеты определяются как параметрические графы потоков данных, что позволяет им масштабироваться за пределы 100 000 ядер. Кроме того, этот фреймворк рассматривает композицию скелетов как композицию «точка-точка» соответствующих им графов потоков данных через понятие портов, что позволяет легко добавлять новые скелеты в фреймворк. В результате этот фреймворк устраняет необходимость в повторной реализации и глобальных синхронизациях в составных скелетах. STAPL Skeleton Framework поддерживает вложенную композицию и может переключаться между параллельным и последовательным выполнением на каждом уровне вложенности. Этот фреймворк выигрывает от масштабируемой реализации параллельных контейнеров STAPL [93] и может запускать скелеты на различных контейнерах, включая векторы, многомерные массивы и списки.
T4P была одной из первых систем, представленных для скелетного программирования. [94] Система в значительной степени опиралась на свойства функционального программирования, и пять скелетов были определены как функции высшего порядка: Разделяй и властвуй, Ферма, Карта, Труба и RaMP. Программа могла иметь более одной реализации, каждая из которых использовала комбинацию различных скелетов. Кроме того, каждый скелет мог иметь различные параллельные реализации. Методология, основанная на функциональных преобразованиях программ, направляемых моделями производительности скелетов, использовалась для выбора наиболее подходящего скелета для использования в программе, а также наиболее подходящей реализации скелета. [95]