![]() |
RefNUMA – библиотека для организации виртуальной общей памяти в программах, использующих MPI.А. О. Лацис
Пример 5. Векторизованный разделяемый массив.Программа из предыдущего примера смотрелась бы гораздо естественнее, если бы массив общей памяти был сплошным, а не разделенным на секции. В этом случае мы говорили бы о нем как о блочно распределенном разделяемом массиве, в котором первые LSECT элементов, с 0-го по (LSECT-1)-й, принадлежат нулевому процессу, следующие LSECT элементов – первому процессу, и так далее. В RefNUMA предусмотрены два способа сделать так, чтобы программист мог представить себе секционированный массив сплошным. В этом примере мы рассмотрим самый простой и эффективный по быстродействию способ – создание векторизованного разделяемого массива. К сожалению, векторизованный массив не бывает одномерным, его размерность должна быть равна, как минимум, двум. Массив общей памяти в предыдущем примере является, по смыслу программы, одномерным. Чтобы не прибегать к искусственным приемам вроде приписывания к элементу массива «лишнего» индекса, всегда равного нулю, несколько модифицируем наш пример. Пусть каждая секция массива общей памяти состоит не из LSECT элементов типа double, а из LSECT строк, по LWIDTH элементов типа double в каждой. Поставим теперь задачу сделать так, чтобы программист имел дело со сплошным массивом строк, распределенных по процессам блоками по LSECT строк. Идея «склеивания» секций секционированного массива в сплошной разделяемый массив очень проста. Необходимо сразу после создания секционированного массива построить в каждом процессе еще один массив указателей, подобный массиву указателей на секции (слева на Рис. 1). Только это должен быть массив указателей уже не на секции, а на отдельные строки внутри секций. Использование этого второго массива указателей для доступа к содержимому массива общей памяти, действительно, позволяет забыть о секциях, и рассматривать массив общей памяти как единый массив строк, имеющих сплошную нумерацию. Правда, совсем забыть об исходном секционированном массиве все-таки не получится – он может потребоваться для обращений к таким функциям, как coarray_report() и/или coarray_get_name(). Естественно, построение массива указателей на строки программист должен выполнять не вручную, а путем обращения к библиотечной функции coarray_vectorize(). Вполне очевидно, что построение массива указателей на строки в каждом процессе – это дополнительный расход памяти, который может стать недопустимо большим, если строки короткие. В пределе, когда строки имеют длину 1, то есть массив является одномерным, такой способ «сшивания» секций точно не годится. Тогда следует пользоваться другим способом, описываемым в одном из последующих примеров. Этот способ, как мы позже увидим, более сложен и менее эффективен по быстродействию, но не связан с дополнительными затратами памяти, и годится для массивов любой размерности. Далее приводится текст программы, похожей на программу из Примера 4, но переписанной с учетом всего сказанного выше. Функция coarray_vectorize(), используемая для построения массива указателей на строки, имеет три аргумента: указатель на исходный секционированный массив, число строк в секции и размер строки в байтах. Предполагается, что указанное число строк указанного размера в секцию помещаются. Границы индексов при обходе массива изменены таким образом, чтобы массив общей памяти рассматривался как сплошной массив из LSECT*n_nodes строк, по LWIDTH элементов в строке. Массив распределен блоками строк: каждые последовательные LSECT строк принадлежат соответствующему процессу. Для доступа к элементам массива используется вектор строк vcoarray, для извлечения информации о расходе системной памяти – исходный массив указателей на секции coarray. #include <stdio.h>
#include <stdlib.h>
#include <mpi.h>
#include <coarray.h>
#include <shared.h>
#define LSECT 10
#define LWIDTH 5
COARRAY_MEM_ALLOC( 2000000000l );
int main( int argc, char *argv[] )
{
int i, j, k, my_node, n_nodes;
double **coarray, **vcoarray;
long requested, used;
FILE *fp;
/***/
COARRAY_Init( &argc, &argv );
my_node = coarray_my_node();
n_nodes = coarray_n_nodes();
coarray = (double**)coarray_create( LSECT*LWIDTH*sizeof(**coarray),
LSECT*LWIDTH*1000*sizeof(**coarray) );
if ( !coarray )
{
fprintf( stderr, "No memory\n" );
exit( -1 );
}
coarray_set_name( (void**)coarray, "This is my coarray" );
vcoarray = (double**)coarray_vectorize( (void**)coarray,
LSECT, LWIDTH*sizeof(**coarray) );
if ( !vcoarray )
{
fprintf( stderr, "No memory\n" );
exit( -1 );
}
if ( my_node == 0 )
{
fp = fopen( "output.dat", "w" );
fclose( fp );
}
NODE_BY_NODE_BEGIN( i, 0, n_nodes )
fp = fopen( "output.dat", "a" );
if ( i == 0 ) fprintf( fp, "BEFORE ASSIGNMENT\n" );
fprintf( fp, "Hello, I am %d of %d\n", my_node, n_nodes );
fprintf( fp, "My view of the coarray is:\n" );
for ( j = 0; j < LSECT*n_nodes; j++ )
{
for ( k = 0; k < LWIDTH; k++ )
{
fprintf( fp, " %f ", vcoarray[j][k] );
}
fprintf( fp, "\n" );
}
fclose( fp );
NODE_BY_NODE_END
coarray_barrier();
for ( i = my_node*LSECT; i < (my_node+1)*LSECT; i++ )
{
for ( j = 0; j < LWIDTH; j++ ) vcoarray[i][j] = i+10*j;
}
coarray_barrier();
NODE_BY_NODE_BEGIN( i, 0, n_nodes )
fp = fopen( "output.dat", "a" );
if ( i == 0 ) fprintf( fp, "AFTER ASSIGNMENT\n" );
fprintf( fp, "Hello, I am %d of %d\n", my_node, n_nodes );
fprintf( fp, "My view of the coarray is:\n" );
for ( j = 0; j < LSECT*n_nodes; j++ )
{
for ( k = 0; k < LWIDTH; k++ )
{
fprintf( fp, " %f ", vcoarray[j][k] );
}
fprintf( fp, "\n" );
}
coarray_report( (void**)coarray, &requested, &used );
fprintf( fp, "Coarray %s requested %ld bytes, actually used %ld bytes\n",
coarray_get_name( (void**)coarray ), requested, used );
fclose( fp );
NODE_BY_NODE_END
COARRAY_Finalize();
return 0;
}
◄ Пример 4
Пример 6 ►
|
|
||||||||||||||||||||||||||||||||
| Тел. +7(499)220-79-72; E-mail: inform@kiam.ru | ||||||||||||||||||||||||||||||||||