RefNUMA – библиотека для организации виртуальной общей памяти в программах, использующих MPI.

А. О. Лацис

Пример 4. Контроль расхода физической памяти и диагностика ее исчерпания.

Разбирая предыдущий пример, мы отмечали, что в качестве второго аргумента coarray_create() указывается объем заранее резервируемой в каждом процессе системной памяти, используемой по мере необходимости библиотекой RefNUMA для реализации данного массива общей памяти. Необходимость оценивать вручную и явно задавать это значение – пожалуй, главное неудобство RefNUMA. Тем более важно не просто понимать, на что и когда эта память расходуется, но и уметь, как минимум, две вещи:

  • узнавать, сколько реально системной памяти потребовалось программе для реализации того или иного массива,
  • понимать, какому именно массиву не хватило отведенной памяти, если программа завершилась аварийно по причине нехватки системной памяти.

Системная память расходуется исключительно для реализации доступа процессов к чужим секциям. Если такой доступ имел место, то системная память расходуется и в процессе, который осуществлял доступ, и в процессе, для которого эта секция – своя. Если ни один процесс не осуществлял доступа к чужим секциям, системная память не расходуется вообще. Расход системной памяти можно грубо оценить как удвоенный объем страниц чужой памяти, к которым осуществлялся доступ из одного процесса. При этом важно принимать во внимание «краевые эффекты»: поскольку системная память выделяется страницами, для «игрушечных», отладочных вариантов вновь создаваемых программ ее требуемый размер может многократно превышать размер секции, поскольку сама секция многократно меньше страницы. Размер страницы – 512 элементов типа double.

Более или менее точная оценка необходимого объема системной памяти, таким образом, сложна и вряд ли целесообразна на практике. Важнее уметь спрашивать у системы, сколько ее реально потребовалось. Это делается обращением к функции coarray_report(), первым (входным) аргументом которой является указатель на секционированный массив, а вторым и третьим (выходными) – адреса переменных, которым присваиваются, соответственно, объем запрошенной и израсходованной при реализации данного массива системной памяти. Оба возвращаемых аргумента имеют тип long, а не int!!!

Однако, для успешной отладки этого мало. Опыт показывает, что довольно часто в еще не отлаженной программе присутствуют массовые ошибочные обращения к чужим секциям некоторого массива общей памяти. При этом объем системной памяти, необходимый для формально успешного завершения программы, оказывается очень большим, и значительно превышает оценки, сделанные программистом. Программа из-за этого постоянно завершается аварийно. В этом случае очень важно бывает быстро и надежно понять, с каким именно массивом общей памяти связана проблема.

Чтобы сделать диагностические сообщения об исчерпании системной памяти более информативными, можно сразу после создания секционированного массива присвоить ему текстовое имя, которое будет фигурировать в этих диагностических сообщениях. Это делается обращением к функции coarray_set_name(). По умолчанию все секционированные массивы имеют текстовое имя из одного пробела, и по тексту диагностического сообщения невозможно понять, в каком именно секционированном массиве исчерпалась системная память.

Присвоенное секционированному массиву имя можно опросить обращением к функции coarray_get_name().

Ниже представлен исходный текст программы предыдущего примера, в которую добавлены описанные только что средства дополнительной диагностики.

#include <stdio.h>
#include <stdlib.h>
#include <mpi.h>
#include <coarray.h>
#include <shared.h>
#define LSECT 10
    COARRAY_MEM_ALLOC( 2000000000l );
    int main( int argc, char *argv[] )
{
    int i, j, k, my_node, n_nodes;
    double **coarray;
    long requested, used;
    FILE *fp;
/***/
    COARRAY_Init( &argc, &argv );
    my_node = coarray_my_node();
    n_nodes = coarray_n_nodes();
    coarray = (double**)coarray_create( LSECT*sizeof(**coarray),
                                        LSECT*1000*sizeof(**coarray) );
    if ( !coarray )
     {
      fprintf( stderr, "No memory\n" );
      exit( -1 );
     }
    coarray_set_name( (void**)coarray, "This is my coarray" );
    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 < n_nodes; j++ )
      {
       for ( k = 0; k < LSECT; k++ )
        {
         fprintf( fp, " %f\n", coarray[j][k] );
        }
      }
     fclose( fp );
    NODE_BY_NODE_END
    coarray_barrier();
    for ( i = 0; i < LSECT; i++ ) coarray[my_node][i] = LSECT*my_node+i;
    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 < n_nodes; j++ )
      {
       for ( k = 0; k < LSECT; k++ )
        {
         fprintf( fp, " %f\n", coarray[j][k] );
        }
      }
     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;
}
◄ Пример 3 Пример 5 ►
 
 
 
 
 
 
 
 
  Тел. +7(499)220-79-72; E-mail: inform@kiam.ru