| OpenTS |
|
|
Содержание Язык T++ является входным языком Т-системы — системы параллельного программирования c открытой архитектурой, поддерживающей автоматическое динамическое распараллеливание программ.Синтаксически язык T++ максимально приближен к языку C++. Явные параллельные конструкции, понимаемые в привычном смысле, в T++ отсутствуют (т.е. программист не указывает, какие части функций следует выполнять параллельно), и реально счетные гранулы выделяются динамически во время работы Т-программы. Указаниями для такого выделения являются расширения T++ синтаксиса и семантики языка C++. Эти расширения представляют собой несколько ключевых слов и выглядят достаточно прозрачными для синтаксиса и семантики языка C++: программу на языке T++ можно разрабатывать и отлаживать в однопроцессорном режиме, без использования Т-системы. Для этого достаточно переопределить нужным образом с помощью макроопределений эти ключевые слова. Данное свойство упрощает первый этап цикла разработки программ, позволяя отлаживать их в наиболее удобной, привычной для программиста последовательной среде. В языке T++ поддерживается функциональный подход к написанию программ: все данные Т-функция («чистая» функция в языке Т++) может получать только через входные аргументы, а результаты своей работы возвращать только через выходные аргументы. Т-функция не должна иметь побочных эффектов. Таким образом, Т-функция естественным образом служит гранулой параллелизма: если она вызывается, она через свои аргументы получает всю информацию, необходимую ей для работы, и поэтому ее можно "отдать на выполнение" другому процессору. Набор ключевых слов языка T++ невелик, и освоить его не составит труда для любого программиста, привыкшего к написанию программ на языке C. Основные ключевые слова языка T++:
Вспомогательные макроопределения также можно отнести к расширениям, введенным в язык C++ для работы в мультипроцессорной системе с распределенной памятью
Т-переменные содержат неготовые величины (T-величины). Неготовые величины производятся с помощью вызова Т-функций и в каждый момент времени либо неготовы (при этом их значение не определено, а попытка обращения влечет за собой приостановку обращающейся функции), либо готовы (то есть, уже посчитаны), при этом их значение фиксируется и в дальнейшем не меняется. Нельзя изменить собственно неготовую величину, но можно присвоить Т-переменной другую неготовую величину. Т-переменные совместимы по большинству операций с обычными переменными; их можно присваивать таким же образом, обращаться за их значением, брать ссылку с помощью оператора & (указатель на Т-переменную является Т-указателем). Связь с неготовой величиной разрывается в момент уничтожения Т-переменной (например, при выходе из блока, в котором Т-переменная была объявлена), а также при вызове функции tdrop. При этом Т-переменная связывается с новой, только что созданной неготовой величиной. Для неготовых величин, которые не связаны более ни с одной Т-сущностью (Т-переменной или Т-указателем) должен работать распределенный алгоритм сборки мусора. Т-функции являются функциями, которые выполняются каждая в своем потоке управления (thread). При этом они могут одновременно выполняться на разных процессорах в многопроцессорной системе. Т-функции взаимодействуют между собой при помощи Т-переменных, которые содержат Т-величины. Семантика Т-переменных обеспечивает необходимую синхронизацию и семантическую совместимость, которую может ожидать программист от аналогичной C++-программы. Поддержка семантики Т-функций и Т-переменных обеспечивается соответствующей средой исполнения T++-программ. При вызове Т-функции ей в качестве входных аргументов передаются те данные, которые содержались в точке вызова; дальнейшие изменения Т-переменных вызывающей Т-функцией допускаются произвольное число раз и не приводят к видимому эффекту для вызванной Т-функции. В качестве выходных аргументов (описываемых при помощи атрибута tout) указываются собственные Т-величины, при этом они становятся неготовыми, а их поставщиком является вызванная Т-функция. Значением Т-функций также является неготовая величина, которая может быть присвоена (в точке вызова) любой собственной Т-величине. Т-функции, не производящие более Т-значений, на которые имелись бы ссылки у потребителей их значений, должны эффективно завершаться (должно обеспечиваться средой исполнения). Т-указатели являются аналогами C-указателей для неготовых величин. Т-указатели совместимы по большинству операций с обычными указателями; их можно присваивать таким же образом, обращаться за их значением, обращаться к значению с помощью операторов "->" и "*" (результатом применения оператора "*" является неготовая величина). Дополнительные функции tdrop и twait позволяют совершать специфические по отношению к языку C++ операции: разрыв связи с неготовой величиной и ожидание определенных событий (как правило, ожидание готовности одной или нескольких Т-величин). При вызове функции tdrop Т-функцией-поставщиком Т-значения неготовая величина становится готовой и обретает то значение, которое было последним ей присвоено. При этом возникает соответствующее событие, которое влечет за собой продолжение исполнения всех приостановленных по причине ожидания потоков. При вызове функции tdrop Т-функцией-потребителем Т-значения ссылка на неготовую величину теряется, и счетчик ссылок на неготовой величине уменьшается. Обнуление счетчика ссылок на неготовой величине влечет за собой инициацию процесса остановки Т-функции-поставщика Т-значения, если только поставщик Т-функции не производит других значений для кем-либо ожидаемых неготовых величин. При вызове функции twait указывается Т-сущность (обычно Т-величина или массив Т-величин) и образец для ожидаемых событий. Возвращается статус событий. Конкретные образцы могут определяться реализацией языка T++; от реализации требуется поддержка возможности определить статусы готовности одной неготовой величины, а также возможность для получения индексов Т-величин в массиве неготовых величин в порядке наступления их готовности (для реализации недетерминированной альтернативы, что бывает полезно при реализации некоторых переборных алгоритмов). Как уже было сказано выше, в языке Т++ отсутствуют явные распараллеливающие конструкции, так как функцию автоматического динамического распараллеливания берет на себя Т-система. Но поскольку гранулой параллелизма является Т-функция, программист может некоторым образом повлиять на процесс распараллеливания, изменяя размер Т-функции, вернее, объем действий, которые она должна совершить во время исполнения. Таким образом, под размером гранулы параллелизма понимается не количество строк исходного кода, которое содержит тело Т-функции, а объем действий, которые она должна совершить во время исполнения. Например, Т-функция может состоять из одного единственного вызова какой-либо С-функции, и тело ее может состоять из одной-двух строк. Но если при этом вызываемая С-функция выполняет большую работу, то размер гранулы параллелизма будет достаточно большим. От выбора гранулы параллелизма зависит эффективность процесса распараллеливания. Очевидно, что при слишком малом размере гранулы могут возникнуть большие накладные расходы на передачу Т-функций другим вычислительным узлам, при этом полезная работа, выполняемая самой функцией на удаленном процессоре, может быть совсем незначительной. С другой стороны, слишком большой размер гранулы может привести к неравномерной загрузке мультипроцессора, когда количество готовых к выполнению Т-процессов оказывается меньше, чем количество процессоров, и часть процессоров простаивает. Таким образом, если при выполнении программы на мультипроцессоре не возникает ожидаемого ускорения, следует вернуться к исходному коду программы, проанализировать его с учетом всего сказанного, и при необходимости изменить структуру программы. Использование Т-функций в качестве гранул параллелизма определяет необходимость функционального подхода при их написании. То есть, все данные Т-функция должна получать через свои аргументы и передавать через результаты. Это должна быть "чистая функция" без побочных эффектов. Таким образом, использование глобальных переменных для передачи данных между различными Т-функциями запрещается. Исключение составляют проинициализированные глобальные константы. Переменная, которая может содержать неготовое значение объявляется в языке T++ как tval <type> var; где tval - ключевое слово языка T++ — признак того, что переменная var может содержать неготовое значение, <type> — C-тип этой переменной. Тип может быть как простым, так и агрегатным (например, структурой). При использовании в качестве типа С-указателя (или, в случае агрегатного типа, при использовании С-указателя в качестве типа одного из полей структуры) надо иметь в виду, что применение С-указателей в Т-переменных ограничено из-за того, что Т-переменная может быть использована не на том процессоре, на котором создавалась, и на этом другом процессоре С-указатель может не иметь смысла. Поэтому, например, декларации вида tval char * var; или
struct example{
int *fun();
double num;
};
tval struct example var1;можно использовать только в том случае, если С-указатель является указателем на функцию или на константный статический объект (такие объекты имеют одинаковые адреса и содержание на всех процессорах). Использовать С-указатель на динамический или автоматический объект в Т-переменной нельзя. Эффективное распараллеливание в Т-системе кроме всего прочего обусловлено возможностью выполнения некоторых операций с неготовыми значениями без ожидания их готовности. Так, присваивание неготового значения другой Т-переменной выполняется немедленно, без ожидания готовности. Однако попытка использовать значение переменной в операциях, отличных от присваивания Т-переменным, всегда приводит к ожиданию готовности значения. Для эффективного распараллеливания следует избегать преждевременного ожидания готовности переменных. Чем позднее произойдет засыпание Т-процесса по ожиданию готовности переменной, тем лучше. Сначала идут включения заголовочных файлов. Затем, как и в обычной программе на C, должны идти определения типов данных, которые используются в программе. После этого могут идти определения тел обычных функций и Т-функций (т.е. фрагментов кода, который может выполняться на различных узлах и в различных контекстах). Входная функция программы должна иметь имя main, и имеет почти такой же прототип, как и в программах на языке C, только с добавлением ключевого слова tfun. Программу на языке T++ можно разрабатывать и отлаживать без использования Т-системы. Для этого достаточно переопределить с помощью макроопределений ключевые слова, добавленные в язык C. Таким образом, для компиляции программы на языке T++ в последовательном режиме, достаточно обрабатывать программу компилятором t++ с опцией -not и программа преобразуется в программу на языке C на этапе препроцессирования. После того, как программа отлажена в последовательном варианте, можно переходить к ее отладке в параллельном варианте. Для этого используется компилятор t++. При компиляции компилятором t++ без опции -not переопределения ключевых слов не происходит, и программа компилируется для исполнения в параллельном режиме. Для наиболее эффективного процесса разработки программ для исполнения под Т-системой следует придерживаться следующей последовательности действий:
|