![]() |
||||||||||||||||||||||||||||||||||
![]() |
||||||||||||||||||||||||||||||||||
![]() |
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 |