Параллельное программирование: Учебное пособие, страница 68

Текст 3.10. – Отображение процессов на процессорные объекты

class POArray

{

friend CCVoid& operator<<(CCVoid&, const POArray&);

friend CCVoid& operator>>(CCVoid&, POArray&);

public:

void init(int, char * []);

virtual POArrayNode *global create_pobj(proc_t)=0;

POArrayNode *global pobjs[];

int posize;

};

global class POArrayNode

{

friend class POArray;

public:

virtual void init_pobj(POArray&)=0;

POArray *p_array;

};

void POArray::init(int sz, char *nodelist[])

{

posize = sz;

// Создаются новые процессорные объекты

pobjs = new (POArrayNode *global)[posize];

parfor (int i=0; i<posize; i++)

{

proc_t loc(node_t(nodelist[i] ));

pobjs[i] = create_pobj(loc);

}

// Копирование указателей процессорных объектов

parfor (intj=0; j<posize; j++) // в новые процессорные

pobjs[j]->init_pobj(*this);  //объекты

}

Можно более искусно использовать класс POArray для создания процессорных объектов произвольного типа. Нужно сделать функции create_pobj и init_pobj виртуальными, для чего они должны быть объявлены с ключевым словом virtual и дополнены знаком присвоения нуля (=0). Благодаря этому, эти функции могут быть определены в классах, полученных соответственно из классов POArrary и POArrayNode.

Для создания массива виртуальных функций, например, типа T, необходимо просто вывести новые классы из классов POArrary и POArrayNode и в этих классах определить функции create_pobj и init_pobj. Теперь эти производные (дочерние) функции будут создавать и инициализировать новые процессорные объекты типа T.

Текст 3.11 является фрагментом программы, в котором описана параллельная структура двух взаимодействующих вычислительных моделей, представленных классами POArray иAtmOcn. Каждая из моделей использует половину (P/2) процессоров.

В этом тексте из класса POArray получается классAtmOcn, который дополняется определением виртуальной функции create_pobj, использованной в POArray для создания процессорного объекта, и определениями вычислительных функций atmosphere и ocean, представляющих собственно некие программные модели

Аналогично, класс процессорного объекта AtmOcnNode, выведенный из POArrayNode, определяет виртуальную функцию init_pobj, которая инициализирует процессорный объект объединенной модели, и определяет функции atm_proc и ocn_proc, которые будут выполняться в каждом процессорном объекте.

Функция init_pobj создает локальный образец объекта AtmOcn, переданного как параметр, обеспечивая тем самым каждый процессорный объект доступом к другим процессорным объектам.

Текст 3.11. – Отображение взаимодействующих моделей

globalclassAtmOcnNode: publicPOArrayNode

{

public:

voidatm_proc(int);                            // Процедура вычислений

void ocn_proc(int);                            // Процедура вычислений

void init_pobj(POArray& ar)        // Операция инициализации

{

p_array = new AtmOcn(ar);

}

};

class AtmOcn: public POArray

{

public:

void atmosphere();

void ocean();

AtmOcn(const POArray& ar): POArray(ar) { . . . };

POArrayNode *global create_pobj(proc_t locn)

{

return new (locn) AtmOcnNode;

}

};

void AtmOcn::atmosphere()

{

parfor (int j=0; j<posize; j++)

((AtmOcnNode *global) pobjs[j])->atm_proc(j);

}

void atm_proc(int id)

{

. . .  // Ядро вычислительного кода модели

}

void main(int argc, char *argv[])

{

int P = atoi(argv[l]);

char *nodes[] = read_nodes();

AtmOcn atm, ocn;

atm.init(P/2, nodes);           // P принято четным

ocn.init(P/2, &nodes[P/2]);

par

{

atm.atmosphere();

ocn.ocean();

}

}

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

Текст 3.12. – Взаимодействующие компоненты на тех же процессорах

void main(int argc, char *argv[])

{

int P = atoi(argv[1]);