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