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

По умолчанию, вновь созданный процессорный объект размещается на том же самом процессоре, на котором ведется разработка программы. Альтернативное расположение может быть задано новому оператору параметром размещения. В C++ этот параметр применяется для размещения объекта в пространстве памяти. В CC++ он может использоваться также и для размещения процессорного объекта в пространстве процессоров. (Параметр размещения может также использоваться для точного описания места расположения кода процессорного объекта в файловой системе компьютера.).

Местоположение определяется обусловленной зависимостью от класса, названного proc_t. Конструкторы функций proc_t и node_t, определенные в библиотеке CC++, используются для создания структуры размещения с точно установленным именем процессора. Пример их использования показан в следующем кодовом фрагменте. Здесь, в классе MyClass, создается новый процессорный объект location на процессоре с именем mymachine:

MyClass *global G;

proc_t location(node_t("mymachine"));

G = new (location) MyClass;

Оператор new создает новый процессорный объект G со свойствами объекта location класса MyClass. Процессорный объект location ключевыми словами proc_t и node_t привязан к процессору mymachine. Процессорный объектG наследует эту связь. Чтобы объектG расположить на другом процессоре, потребуется только одно изменение на второй строчке кодового фрагмента, например, так, как показано ниже:

proc_t location(node_t("yourmachine"));

В следующем кодовом фрагменте создается 32 процессорных объекта. Каждый из них размещается на различных процессорах (узлах) многопроцессорной вычислительной системы с названиями процессоров соответственно sp#0, sp#1, ..., sp#31. Ключевое слово parfor используется для одновременного создания этих процессорных объектов.

MyClass *global G[32];

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

{

char node_name[256];

sprintf(node_name,"sp#%",i)

proc_t location(node_t(node_name));

G[i] = new (location) MyClass;

}

Приведенный код прост, однако он плох тем, что в выполняемую программу внедряется информация об окружающей вычислительной среде. Лучшим подходом является тот, в котором отображения формируются в отдельном классе, как в классе Mapping, описанном в тексте 3.8. В этом классе формируется две частные переменные P и proc_names, которые представляют необходимую вычислительную среду для выполнения программы: число процессоров и список их имен. Функция-член initmap используется для инициализации этих переменных. Две дополнительные функции-члены, processor и random_p, возвращают proc_t объект, отображающий i-тый процессор и случайно выбранный процессор соответственно. Наконец, две, опущенные для краткости, функции передачи данных с упаковкой и распаковкой взаимно однозначно свяжут список процессоров с отображаемыми объектами. Каждое отображение передается параметром, когда создается новый процессорный объект. Использование класса Mapping проиллюстрировано в следующем примере.

Текст 3.8. – Размещение процессорных объектов по процессорам

classMapping                     // Формирование списка имен процессоров

{

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

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

public:

proc_tprocessor(int);    // Доступ к очередному процессору.

proc_trandom_p();               // Доступ к случайному процессору.

voidinitmap(char **);    // Активация процессорных имен.

private:

intP;                                                  // Число процессоров

char *proc_names[];                 // Список процессорных имен

};

proc_t Mapping::processor(int i)

{                // Определение функции отображения очередного процессора

return(proc_t(node_t(proc_names[i%])));

}

proc_t Mapping::random_p()

{                // Определение функции отображения случайного процессора.

return processor(drand48()*((float) P));

}

void Mapping::initmap(char *plist[])

{                // Определение функции создания списка процессорных имен.