Схема размещения в локальной памяти и блочно-циклическое отображение плотных матриц.

Для равномерной загрузки параллельных процессов и эффективного использования подпрограмм пакета BLAS при выполнении операций над плотными глобальными матрицами были разработаны специальные способы разбиения таких матриц на прямоугольные блоки ( MB * NB ) и распределения этих блоков по двумерной решетке параллельных процессов (о решетках см. раздел документации "Решетки процессов, контекст и подпрограммы пакета BLACS"). Простой пример конкретного разбиения плотной матрицы на блоки и распределения их по двумерной решетке процессов, называемого блочно - циклическим отображением, приводится в разделе документации "Пример блочно - циклического распределения плотной матрицы по решетке процессов".

Здесь же мы рассмотрим, каким образом блоки, отображенные на один и тот же процесс, располагаются и хранятся в локальной иамяти этого процесса. Другими словами, мы выпишем точные формулы, которые связывают элемент глобальной матрицы, определяемый глобальными индексами  I и  J (т.е. A ( I, J )), с координатами процесса, владеющего этим элементом, в решетке процессов ( Pr, Pc) и с расположением этого элемента матрицы в локальной памяти этого процесса.

Рассмотрим сначала, для простоты, эти связи на примере одномерного случая, т.е. размещение одномерного глобального массива длины  N, разбитого на блоки длины NB, по одномерной решетке из  P процессов, перенумерованных, начиная с 0 до ( P - 1). Элементы самого глобального массива нумеруются от 1 до  N. Прежде всего, массив делится на смежные блоки размера NB. Когда  N не делится на NB нацело, последний блок массива элементов будет содержать только  mod ( N, NB ) элементов вместо NB. Блоки, на которые разбивается исходный массив, нумеруются (как и процессы), начиная с 0. Они распределяются по процессам подобно тому, как сдается колода карт участникам игры - по кругу (т.е. циклически).

Другими словами, мы предполагаем, что процесс с номером 0 получает первый блок (с номером 0),  k - ый блок связывается с процессом, имеющим координату (номер)  mod ( k, P ) . Блоки, связанные с одним и тем же процессом, хранятся в памяти рядом (в смежных, прилегающих частях). Отображение элемента массива с глобальным индексом  I определяется следующим соотношением:

     I   =   k * NB + x   =   ( m * P + p ) * NB + x  ,


где   I - глобальный индекс глобального массива,
       m - локальная координата блока, в котором этот элемент
            расположен,
       р - координата процесса, владеющего этим блоком,
       х -  локальная координата элемента внутри того блока,
            где находится элемент глобального массива с индексом  I.

Легко установить соотношения между этими переменными:

     p   =   [ ( I - 1) / NB ] mod P ,
     m   =   [ ( I - 1) / ( P * NB ) ] ,
     x   =   mod ( I - 1, NB ) + 1 .

Эти уравнения позволяют определить локальную информацию (т.е. локальный индекс  m * NB + x), так же как и координату процесса  р, соответствующую глобальному элементу, идентифицируемому его глобальным индексом  I, и наоборот.

В  таблице ниже показано отображение в локальную память при разбиении массива на блоки, когда  P = 2,  N = 16 и  NB = 8.

I 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
p 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1
m 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
x 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8
m*NB + x 1 2 3 4 5 6 7 8 1 2 3 4 5 6 7 8

Вовсе не обязательно всегда делать распределение блоков, начиная с процесса с номером 0. Иногда, бывает полезно начать распределение данных с процесса имеющего отличную от нуля координату  SRC. В этом случае соотношения становятся такими:

     p   =   ( SRC + [ ( I - 1) / NB ] ) mod P ,
     m   =   [ ( I - 1) / ( P * NB ) ] ,
     x   =   mod ( I - 1, NB ) + 1 .

Ниже в таблице показано размещение блоков при  P = 2,  SRC = 1,  NB = 3 и  N = 16.

I 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
p 1 1 1 0 0 0 1 1 1 0 0 0 1 1 1 0
m 0 0 0 0 0 0 1 1 1 1 1 1 2 2 2 2
x 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1
m*NB + x 1 2 3 1 2 3 4 5 6 4 5 6 7 8 9 7

В двумерном случае предположим, что матрица разделена на  MB * NB блоки и что первый блок передан процессу с координатами ( RSRC, CSRC ). Формула, приведенная выше для одномерного случая, должна использоваться повторно независимо для каждого измерения решетки процессов  Pr * Pc. Например, элемент матрицы ( I, J ) находится в процессе с координатами ( Pr, Pc) внутри локального блока ( m, n ) в позиции ( x, y ), задаваемой формулами

  ( m, n )   =   ( [ ( I - 1) / ( Pr * MB ) ], [ ( J - 1) / ( Pc * NB ) ] ) ,

  ( Pr, Pc)   =   ( ( RSRC + [ ( I - 1) / MB ] ) mod Pr, ( CSRC + [ ( J - 1) / NB ] ) mod Pc )

  ( x, y )   =   ( mod ( I - 1, MB ) + 1, mod ( J - 1, NB ) + 1 )

Эти формулы показывают, как матрица  А размера  M_A * N_A отображается и хранится на решетке процессов. Она сначала разбивается на  MB_A * NB_A блоки, начиная с ее верхнего левого угла. Эти блоки затем равномерно распределяются по решетке процессов циклическим образом.

Каждый процесс владеет набором блоков, которые хранятся рядом (смежно) по столбцам в двумерном массиве, расположенном в памяти "по столбцам" ("column major").

Это соглашение о локальном размещении позволяет эффективно использовать иерархию локальной памяти посредством вызова пакета BLAS для подмассивов, которые могут быть больше, чем один блок размера  MB_A * NB_A.

На рисунке ниже представлено отображение матрицы  5 * 5, разделенной на блоки размером  2 * 2, на решетку процессов размером  2 * 2 (т.е.  M_A = N_A = 5,  Pr = Pc = 2 и  MB_A = NB_A = 2). Локальные элементы каждого столбца матрицы хранятся рядом в памяти каждого процесса.

a11  a12
a21  a22
a13  a14
a23  a24
a15
a25
a31  a32
a41  a42
a33  a34
a43  a44
a35
a45
a51  a52 a53  a54 a55



0 1
0 a11  a12
a21  a22
a15
a25
a13  a14
a23  a24
a51  a52 a55 a53  a54
1 a31  a32
a41  a42
a35
a45
a33  a34
a43  a44

Цифры 0 и 1 в левой и верхней части последней таблицы означают номера строк и столбцов решетки процессов соответственно.

Определение числа строк или столбцов плотной глобальной матрицы, которое получает конкретный процесс, является важной задачей для пользователя. ScaLAPACK предоставляет служебную подпрограмму ( tool routine ) NUMROC для выполнения этой функции. Обозначения  LOCr ( ) и  LOCc ( ) используются для обозначения этих локальных величин в головных комментариях текстов подпрограмм и упоминаются в разделах документации "Дескрипторы глобальных массивов" и "Пример блочно - циклического распределения плотной матрицы по решетке процессов". Значения  LOCr ( ) и  LOCc ( ), получаемые подпрограммой NUMROC, являются результатом точных вычислений.

Однако если требуется понять общую идею вычисления размера локального массива, то можно проделать следующее грубое вычисление верхней границы этой величины:

а) верхняя граница для  LOCr ( ) оценивается по формуле

                             M_A + MB_A - 1
                             ----------------------    +   Pr  -  1
                                  MB_A
    LOCr( )   =   -------------------------------------------    *   MB_A
                                              Pr

или 

    LOCr( )   =   [ [ M_A / MB_A] / Pr ] * MB_A



б) величина LOCc( ) оценивается по формуле

                              N_A + NB_A - 1
                              ----------------------    +   Pc  -  1
                                 NB_A
    LOCc( )   =   --------------------------------------------    *   NB_A
                                                 Pc
 
или

    LOCc( )   =   [ [ N_A / NB_A ] / Pc ] * NB_A

Заметим, что эти вычисления могут привести к очень большой переоценке величины действительно требующегося пространства.