В тексте 3.13 приведен фрагмент СС++ кода конечно-разностных вычислений в системе SPMD. Инициализация, создание процессорных объектов и глобальных указателей, необходимых для связи, условная циклическая конструкция многократно вызывающая некоторую подпрограмму finite_difference и глобальная переменная global_maximum, участвующая в вычислениях (описана вне фрагмента). Фрагмент реализует параллельную структуру вычислений в соответствии с рисунком 3.8.а
Набросок альтернативного кода СС++, выполняющего в SPMD код конечных разностей, представлен текстом 3.14. В этой версии, каждый процессор выполняет подпрограмму node_execute, осуществляя SPMD вычисления в соответствии с рисунком 3.8.б.
// Каждая задача для вычисления использует один и тот же код
void node_execute()
{
while(!done)
{
finite_difference(localgrid, locamax);
global_maximum(localmax; globmax);
if (globmax < threshold) done = TRUE;
}
}
void main(int argc, char *argv[])
{
intP = atoi(argv[1]);
// Инициализация, создающая массив процессорных объектов, узлов
initialize(P);
// Выполнение вычислений в процессорных объектах
parfor (int i=0; i<P; i++)
nodes[i] ->node_execute();
}
В приведенном ниже тексте 3.15 показан пример выполнения библиотеки канала, объекты которой были использованы ранее в тексте 3.3. Этот пример даст представление о том, как языковые конструкции и прочие парадигмы программирования СС++ можно использовать при разработке библиотечных подпрограмм, например, канальной связи в частности.
classChannel
{
public:
Channel(ChannelUser *global, ChannelUser *global);
InPort *global get_in_port() { return in_port; }
OutPort *global get_out_port() { return out_port; }
private:
InPort *global in_port;
OutPort *global out_port;
};
// Выборочные функции класса Channel
Channel::Channel(ChannelUser *global out_pobj, ChannelUser *global in_pobj)
{
in_port = in_pobj->create_inport();
out_port = out_pobj->create_outport(in_port);
}
global class ChannelUser
{
public:
OutPort *global create_outport(InPort *global ip)
{ return new OutPort(ip); }
InPort *global create_inport()
{ return new InPort; }
};
class InPort: Queue
{
friend class OutPort; //Разрешить OutPort стать в очередь
public:
int receive() { return dequeue(); }
};
class OutPort
{
public:
OutPort(InPort *global iport) { inport = iport; }
void send(int val) { inport->enqueue(val); }
private:
InPort *global inport;
};
Рисунок 3.9. Схема использования функций библиотеки канала.
В приведенном программном фрагменте определяется процессорный объект ChannelUser и два класса каналов InPort и OutPort: канал для передачи и канал для приема. Этот глобального типа процессорный объект поставляет функции create_inport и create_outport, которые создают inport и outport. Любая программа, желающая использовать библиотеку канала, должна включить этот процессорный объект в качестве базового класса. Пример того, как это делается, можно посмотреть в тексте 3.3 при определении процессорного объекта Construction.
Спецификатор класса Channel объявляет три общих функции члена: конструктор канала, предназначенный для создания нового канала, который свяжет два точно установленных процессорных объекта; get_out_port, который возвращает указатель на каналoutport; и get_in_port, который возвращает указатель на канал inport.
Класс InPort получен из класса Queueтекста 3.6. Он добавляется к функциям, уже определенным в том классе. Новая функция просто убирает сообщение из очереди. Класс OutPort формирует глобальный указатель и определяет функцию, которая вызывает операцию постановки сообщения в очередь, упомянутую этим указателем.
1. Какие специальные объекты были определены в языке С++, превратив его в универсальный язык программирования параллельных процессов?
Уважаемый посетитель!
Чтобы распечатать файл, скачайте его (в формате Word).
Ссылка на скачивание - внизу страницы.