Параллельное и распределенное программирование на С++
Шрифт:
Как и в дру г их вариантах, все PVM-задачи, порожден н ые в варианте 4, выполняют одинаковый набор инструкций, но с различными данны м и. Этот SPMD – м етод (Single Program, Multiple-Data — одна програ м ма, множество потоков данных) часто используется для реализации параллельного решения проблем некоторого типа. И то, что язык С++ обладает по д держкой объектов и средств обоб щ енного программирования на основе шаблонов, делает его основным инстру м енто м при решении подобных задач. Объекты и шаблоны позволяют С++-программисту представлять обоб щ енные игибкие решения для различных проблем с помо щ ью одной-единственной программной
// Листинг б.З. Создание сочетаний из заданных множеств
int main(int argc,char *argv[]) {
int RetCode,TaskId[4];
RetCode = pvm_spawn («pvm_generic_combination11, NULL, 0, "", 4,TaskId);
if(RetCode == 4) {
colorCombinations (TaskId[0] , 9) ; colorCombinations(TaskId[l] ,12) ; numericCombinations(TaskId[2],4); numericCombinations(TaskId[3],3); saveResult(TaskId[0]); saveResult(TaskId[l]); saveResult(TaskId[2]); saveResult(TaskId[3]); pvm_exit ;
}
else{
cerr « «Ошибка при порождении сыновнего процесса.»
« endl; pvm_exit ;
}
return(0);
}
В листинге 6.3 обратите внимание на порождение четырех PVM-задач: pvm_spawn(«pvm_generic_combination» ,NULL, 0, н » ,4,TaskId) ;
Каждая порожденнал задача должна выполнять програ м му с именем pvm _generic_combination. Аргу м ент NULL в вызове функции pvm_spawn означает, что через параметр argv[] не передаются никакие опции. Значение 0 в вызове функции pvm_spawn свидетельствует, что нас не беспокоит, на каком ко м пьютере будет выполняться наша задача. Аргу м ент TaskId представляет м ассив, предназначенный для хранения четырех целочисленных значений, который при условии успешного выполнения функции pvm_spawn будет содержать идентификаторы каждой порожденной PVM-задачи. В листингеб.З обратите также вни м ание на вызов функций colorCombinations и numericCombinations . Они «дают работу» PVM-задачам. Определение функции colorCombinations представлено в листинге 6.4.
// Листинг 6.4. Определение функции colorCombinations
void colorCombinations(int TaskId,int Choices) {
int MessageId =1; char *Buffer; int Size; int N;
string Source(«blue purple green red yellow orange
silver gray "); Source.append(«pink black white brown light_green
aqua beige cyan "); Source.append(«olive azure magenta plum orchid violet
maroon lavender»); Source. append (" \n**) ;
Buffer = new char[(Source.size + 100)]; strcpy(Buffer,Source.c_str); N = pvm_initsend(PvmDataDefault); pvm_pkint(&Choices,1,1); pvm_send(TaskId,MessageId); N = pvm_initsend(PvmDataDefault); pvm_pkbyte(Buffer,strlen(Buffer),1); pvm_send(TaskId,MessageId); delete Buffer;
}
В листингеб.З от м етьте два обращения к функции colorCombinations. Каждое из них велит PVM-задаче перечислить различное количество сочетаний цветов: C(24,9) и C(24,12). Первал PVM-задача д олжна сгенерировать 1 307 504 цветовых сочетаний, а вторал — 2 704 156. Эту работу выполняет програм м а, заданнал в вызове функции pvm_spawn . Каждый цвет представляется строкой. Следовательно, програ мм а pvm_generic_combination (с
// Листинг 6.5. Использование PVM-задач для генерирования // сочетаний чисел
void numericCombinations(int TaskId,int Choices) {
int MessageId = 2; int N;
double ImportantNumbers[7] =
{3.00e+8,6.67e-ll,1.99e+30,
6.2. Библио т ека PVM для языка С++ 229
1.67e-27,6.023e+23,6.63e-34,
3.14159265359}; N = pvm_initsend(PvmDataDefault); pvm_pkint(&Choices,1,1) ; pvm_send(TaskId,MessageId) ; N = pvm_initsend(PvmDataDefault); pvm_pkdouble (ImportantNumbers, 5,1) ; pvm_send(TaskId,MessageId) ;
}
В функции numericCombinations из листинга 6.4 PVM-задача использует м ассив чисел с плаваю щ ей точкой, а не м ассив байтов, представл я ю щ их строки. Поэто м у функция colorCombinations отправл я ет свои данные PVM-задача м с по м о щ ью вызовов таких функций:
pvrt_pkbyte(Buffer,strlen(Buffer) ,1) ; pvm_send(TaskId,MessageId) ;
А функция numericCombination( ) отправляет свои данные PVM-задача м таки м образом:
pvm_pkdouble (ImportantNumbers, 5,1) ; pvn_send(TaskId,MessageId) ;
Функция colorCombinations в листинге6.4 создает строку названий цветов, азатем копирует ее в м ассив Buffer типа char. Этот м ассив затем упаковывается и отправляется PVM-задаче с помо щ ью функций pvm_pkbyte и pvm_send . Функция numericCombinations в листинге6.5 создает массив типа double и отсылает его PVM-задаче с помо щ ью функций pvm_pkdouble и pvm_send( ). Одна функция отправляет символьный массив, а другая — массив типа double. В обоих случалх PVM-задачи выполняют одну и туже программу pvm_generic_combination. Именно здесь нас выручает преиму щ ество использования С++-шаблонов. Одинаковые задачи благодаря этому могут работать не только с различными данными, но и с различными типами данных без изменения самого кода. Использование шаблонов в С++ позволяет сделать модель SPMD более гибкой и эффективной. Программе pvm_generic_combination практически безразлично, с какими типами данных ей придется работать. Использование контейнерных С++-классов позволяет генерировать любые комбинации векторов (vector<T>) объектов. Программа pvm_generic_combination «не знает», что она будет работать с двумя типами данных. В листин г е 6.6 представлен раздел кода из программы pvm_generic_combination.
// Листинг 6.6. Использование тега MessageId для // распознания типов данных
pvm_bufinfo (N, &NumBytes, &MessageId, &Ptid) ; if(MessageId == 1){
vector<string> Source;
Buf = new char[NumBytes];
pvm_upkbyte(Buf, NumBytes,1);
strstream Buffer;
Buffer « Buf « ends,-
while(Buffer.good)
{
Buffer » Color;
if(!Buffer.eof){
Source.push_back(Color);
}
}
generateCombinations<string>(Source, Ptid,Value); delete Buf;
}
if(MessageId == 2){
vector<double> Source; double *ImportantNumber; NumBytes = NumBytes / sizeof(double); ImportantNumber = new double[NumBytes]; pvm_upkdouble(ImportantNumber, NumBytes,1); copy(ImportantNumber,ImportantNumber +(NumBytes + 1), inserter(Source, Source.begin)); generateCombinations<double>(Source, Ptid,Value); delete ImportantNumber;