Next:2. ИСПОЛЬЗОВАНИЕ БИБЛИОТЕКИ ПАРАЛЛЕЛЬНЫХ ПОДПРОГРАММ Aztec
Up:1. БИБЛИОТЕКА ПОДПРОГРАММ ScaLAPACK
Prev:1.3. Использование библиотеки ScaLAPACK

1.4. Примеры использования пакета ScaLAPACK

В качестве первого примера использования пакета ScaLAPACK рассмотрим уже знакомую нам задачу перемножения матриц. Это позволит сопоставить получаемые результаты и производительность двух программ, созданных с использованием непосредственно среды параллельного программирования MPI (часть II, раздел 8.2) и с помощью пакета ScaLAPACK.

Пример 1. Перемножение матриц

Используется подпрограмма PDGEMM из PBLAS, которая выполняет матричную операцию C = aA*B + bC, где A, В и С - матрицы, a и b - константы. В нашем случае мы полагаем a = 1, b = 0.

    program abcsl
    include 'mpif.h'

c Параметр nm определяет максимальную размерность блока матрицы
c на одном процессоре, массивы описаны как одномерные

    parameter (nm = 1000, nxn = nm*nm)
    double precision a(nxn), b(nxn), c(nxn), mem(nm)
    double precision time(6), ops, total, t1

c Параметр NOUT - номер выходного устройства (терминал)
    PARAMETER ( NOUT = 6 )
    DOUBLE PRECISION ONE, ZERO
    PARAMETER ( ONE = 1.0D+0, ZERO = 0.0D+0)
    INTEGER DESCA(9), DESCB(9), DESCC(9)

c Инициализация BLACS
    CALL BLACS_PINFO( IAM, NPROCS )
c Вычисление формата сетки процессоров,
c наиболее приближенного к квадратному
    NPROW = INT(SQRT(REAL(NPROCS)))
    NPCOL = NPROCS/NPROW
c Считывание параметров решаемой задачи ( N - размер матриц и
c NB - размер блоков ) 0-м процессором и печать этой информации
    IF( IAM.EQ.0 ) THEN
    WRITE(*,* ) ' Input N and NB: '
    READ( *, * ) N, NB
    WRITE( NOUT, FMT = * )
    WRITE( NOUT, FMT = 9999 )
   $      'The following parameter values will be used:'
    WRITE( NOUT, FMT = 9998 ) 'N ', N
    WRITE( NOUT, FMT = 9998 ) 'NB ', NB
    WRITE( NOUT, FMT = 9998 ) 'P ', NPROW
    WRITE( NOUT, FMT = 9998 ) 'Q ', NPCOL
    WRITE( NOUT, FMT = * )
    END IF

c Рассылка считанной информации всем процессорам
    call MPI_BCAST(N, 1, MPI_INTEGER, 0, MPI_COMM_WORLD, ierr)
    call MPI_BCAST(NB,1, MPI_INTEGER, 0, MPI_COMM_WORLD, ierr)
c Теоретическое количество операций при перемножении
c двух квадратных матриц
    ops = (2.0d0*dfloat(n)-1)*dfloat(n)*dfloat(n)

c Инициализация сетки процессоров
    CALL BLACS_GET( -1, 0, ICTXT )
    CALL BLACS_GRIDINIT( ICTXT, 'Row-major', NPROW, NPCOL )
    CALL BLACS_GRIDINFO( ICTXT, NPROW, NPCOL, MYROW, MYCOL )

c Если процессор не вошел в сетку, то он ничего не делает;
c такое может случиться, если заказано, например, 5 процессоров
    IF( MYROW.GE.NPROW .OR. MYCOL.GE.NPCOL )
   $ GO TO 500

c Вычисление реальных размеров матриц на процессоре
    NP = NUMROC( N, NB, MYROW, 0, NPROW )
    NQ = NUMROC( N, NB, MYCOL, 0, NPCOL )
c Инициализация дескрипторов для 3-х матриц
    CALL DESCINIT( DESCA, N, N, NB, NB, 0, 0, ICTXT, MAX(1,NP ), INFO )
    CALL DESCINIT( DESCB, N, N, NB, NB, 0, 0, ICTXT, MAX(1,NP ), INFO )
    CALL DESCINIT( DESCC, N, N, NB, NB, 0, 0, ICTXT, MAX(1,NP ), INFO )
*
    lda = DESCA(9)
c Вызов процедуры генерации матриц А и В
    call pmatgen(a, DESCA, np, nq, b, DESCB, nprow, npcol, myrow, mycol)
    
    t1 = MPI_Wtime()
*
* Вызов процедуры перемножения матриц
    CALL PDGEMM('N','N', N, N, N, ONE, A, 1, 1, DESCA,
   $             B, 1, 1, DESCB, 0. 0, C, 1, 1, DESCC)
*
    time(2) = MPI_Wtime() - t1
c Печать угловых элементов матрицы C
c с помощью служебной подпрограммы
    if (IAM.EQ.0) write(*,*) 'Matrix C...'
    CALL PDLAPRNT( 1, 1, C, 1, 1, DESCC, 0, 0, 'C', 6, MEM )
    CALL PDLAPRNT( 1, 1, C, 1, N, DESCC, 0, 0, 'C', 6, MEM )
    CALL PDLAPRNT( 1, 1, C, N, 1, DESCC, 0, 0, 'C', 6, MEM )
    CALL PDLAPRNT( 1, 1, C, N, N, DESCC, 0, 0, 'C', 6, MEM )
c Вычисление времени, затраченного на перемножение,
c и оценка производительности в Mflops.
    total = time(2)
    time(4) = ops/(1.0d6*total)
    if (IAM.EQ.0) then
    write(6,80) lda
 80 format(' times for array with leading dimension of',i4)
    write(6,110) time(2), time(4)
 110 format(2x,'Time calculation: ',f12.4, ' sec.',
  $        ' Mflops = ',f12.4)
    end if
c Закрытие BLACS-процессов
    CALL BLACS_GRIDEXIT( ICTXT )
    CALL BLACS_EXIT(0)
9998 FORMAT( 2X, A5, '   :   ', I6 )
9999 FORMAT( 2X, 60A )
 500 continue
    stop
    end

    subroutine pmatgen(a, DESCA, np, nq, b, DESCB, nprow, npcol, myrow, mycol)
    integer n, i, j, DESCA(*), DESCB(*), nprow, npcol, myrow, mycol
    double precision a(*), b(*)
c
    nb = DESCA(5)
c Заполнение локальных частей матриц A и B,
c матрица A формируется по алгоритму A(I,J) = I,
c матрица B(I,J) = 1./J
c Здесь имеются в виду глобальные индексы.
    k = 0
    do 250 j = 1,nq
    jc = (j-1)/nb
    jb = mod(j-1,nb)
    jg = mycol*nb + jc*npcol*nb + jb + 1
    do 240 i = 1,np
    ic = (i-1)/nb
    ib = mod(i-1,nb)
    ig = myrow*nb + ic*nprow*nb + ib + 1
    k = k + 1
    a(k) = dfloat(ig)
    b(k) = 1.D+0/dfloat(jg)
 240 continue
 250 continue
    return
    end

Пример 2. Решение системы линейных уравнений с матрицей общего вида

В данном примере решается система линейных уравнений с матрицей общего вида, которая формируется с помощью генератора случайных чисел. Правая часть системы формируется таким образом, чтобы получить единичное решение. Для решения системы используются две вычислительных подпрограммы: PDGETRF (для факторизации матрицы) и PDGETRS (для нахождения решения). Общий шаблон вызовов функций мало отличается от предыдущего примера. Отличие, главным образом, состоит в том, что в этом примере все коммуникационные операции выполняются с помощью подпрограмм из библиотеки BLACS (чисто из иллюстративных соображений), хотя многие операци можно компактнее записать на MPI.

    program pdlusl
    include 'mpif.h'
    parameter (nsize = 3000, nxn = nsize*nsize)
    double precision a(nxn), b(nsize), x(nsize)
    double precision time(6), cray, ops, total, norma, normx, t1
    double precision resid, residn, eps, epslon, rab, RANN
    integer ipvt(nsize), iwork(5), init(4)
    PARAMETER ( NOUT = 6 )
    DOUBLE PRECISION ONE
    PARAMETER ( ONE = 1.0D+0 )
    INTEGER DESCA( 9 ), DESCB( 9 ), DESCX( 9 )
c Параметр NRHS - количество правых частей
    NRHS = 1
    CALL BLACS_PINFO( IAM, NPROCS )
*
    NPROW = INT(SQRT(REAL(NPROCS)))
    NPCOL = NPROCS/NPROW
c Теоретическое число операций, которое необходимо выполнить для
c решения системы
    ops = (2.0d0*dfloat(n)**3)/3.0d0 + 2.0d0*dfloat(n)**2

c Формирование одномерной сетки процессоров
c (только для рассылки параметров)
    CALL BLACS_GET( -1, 0, ICTXT )
    CALL BLACS_GRIDINIT( ICTXT, 'Row-major', 1, NPROCS )
c На 0-м процессоре считываем параметры, упаковываем в массив
c и рассылаем всем остальным с помощью процедуры IGEBS2D.
    IF( IAM.EQ.0 ) THEN
    WRITE( *,* ) ' Input N and NB: '
    READ ( *,* ) N, NB
    IWORK( 1 ) = N
    IWORK( 2 ) = NB
    CALL IGEBS2D( ICTXT, 'All', ' ', 2, 1, IWORK, 2 )

    WRITE( NOUT, FMT = 9999 )
  $         'The following parameter values will be used:'
    WRITE( NOUT, FMT = 9998 ) 'N ', N
    WRITE( NOUT, FMT = 9998 ) 'NB ', NB
    WRITE( NOUT, FMT = 9998 ) 'P ', NPROW
    WRITE( NOUT, FMT = 9998 ) 'Q ', NPCOL
    WRITE( NOUT, FMT = * )
*
    ELSE
c На не 0-м процессоре получаем массив с процессора (0,0) и
c распаковываем его.
      CALL IGEBR2D( ICTXT, 'All', ' ', 2, 1, IWORK, 2, 0, 0 )
      N  = IWORK( 1 )
      NB = IWORK( 2 )
    END IF

c Уничтожаем временную сетку процессоров
    CALL BLACS_GRIDEXIT( ICTXT )
c Формируем рабочую сетку процессоров
    CALL BLACS_GET( -1, 0, ICTXT )
    CALL BLACS_GRIDINIT( ICTXT, 'Row-major', NPROW, NPCOL )
    CALL BLACS_GRIDINFO( ICTXT, NPROW, NPCOL, MYROW, MYCOL )
c
c Проверка процессоров, не вошедших в сетку
    IF( MYROW.GE.NPROW .OR. MYCOL.GE.NPCOL )
  $    GO TO 500
c Определяем точное число строк и столбцов распределенной матрицы
c в процессоре
    NP = NUMROC( N, NB, MYROW, 0, NPROW )
    NQ = NUMROC( N, NB, MYCOL, 0, NPCOL )
c
c Формируем дескрипторы
*
    CALL DESCINIT( DESCA, N, N, NB, NB, 0, 0, ICTXT, MAX(1,NP ), INFO )
    CALL DESCINIT( DESCB, N, NRHS, NB, NB, 0, 0, ICTXT, MAX(1,NP), INFO )
    CALL DESCINIT( DESCX, N, NRHS, NB, NB, 0, 0, ICTXT, MAX(1,NP), INFO )
*
    lda = DESCA(9)
c Вызов процедуры генерации матрицы A и вектора B
    call pmatgenl(a, DESCA, NP, NQ, b, DESCB, nprow, npcol, myrow, mycol)
    t1 = MPI_Wtime()
*
* Обращение к процедере факторизации матрицы A
    CALL PDGETRF(N, N, A, 1, 1, DESCA, ipvt, INFO )
*
    time(1) = MPI_Wtime() - t1
    t1 = MPI_Wtime()
c
c Обращение к процедуре решения системы уравнений с факторизованной
c матрицей
    CALL PDGETRS('No', N, NRHS, A, 1, 1, DESCA, ipvt, B, 1, 1, DESCB, INFO)
*
    time(2) = MPI_Wtime() - t1
    total = time(1) + time(2)
c
c На этом собственно решение задачи заканчивается, далее идет
c подготовка печати и сама печать
    if (iam.eq.0) then
    write(6,40)
 40 format( '      x(1)         x(nр)')
    write(6,50) x(1),x(np)
 50 format(1p5e16.8)
c
    write(6,60) n
 60 format(//'   times are reported for matrices of order ',i5)
    write(6,70)
 70 format(6x,'factor',5x,'solve',6x,'total',5x,'mflops',7x,'unit',
 $         6x,'ratio')
c
    time(3) = total
    time(4) = ops/(1.0d6*total)
    time(5) = 2.0d0/time(4)
    time(6) = total/cray
    write(6,80) lda
 80 format(' times for array with leading dimension of',i4)
    write(6,110) (time(i),i=1,6)
 110 format(6(1pe11.3))
    write(6,*)' end of test'
    end if
c
    CALL BLACS_GRIDEXIT( ICTXT )
    CALL BLACS_EXIT(0)
 9998 FORMAT( 2X, A5, 'аа :аа', I6 )
 9999 FORMAT(2X, 60A )
 500  continue
    stop
    end

c Процедура генерации матрицы A с помощью генератора случайных
c чисел RANN.
c Последовательности случайных чисел на процессорах должны быть
c независимые, чтобы матрица не оказалась вырожденной.
    subroutine pmatgenl(a, DESCA, NP, NQ, b, DESCB, nprow, npcol, myrow, mycol)
    integer n, init(4), i, j, DESCA(*), DESCB(*), nprow, npcol, myrow, mycol
    double precision a(*),b(*),rann
c
    nb = DESCA(5)
    ICTXT = DESCA(2)
c Инициализация генератора случайных чисел
    init(1) = 1
    init(2) = myrow + 2
    init(3) = mycol + 3
    init(4) = 1325
c Заполнение матрицы A
    k = 0
    do 250 j = 1,nq
    do 240 i = 1,np
    k = k + 1
    a(k) = rann(init) - 0.5
 240 continue
 250 continue
    na = k
c Вычисление вектора B такого, чтобы получить единичное решение,
c сначала вычисляются локальные суммы по строке на каждом процессоре,
c а затем выполняется суммирование по всем процессорам.
    do 350 i = 1,np
    k = i
    b(i) = 0
    do 351 j = 1,nq
    b(i) = b(i) + a(k)а
    k = k + np
 351 continue
    CALL BLACS_BARRIER( ICTXT, 'All' )аа
    CALL DGSUM2D( ICTXT, 'Row', ' ', 1, 1, b(i), 1, -1, 0)
 350 continue
    return
    end
Пример 3. Решение системы линейных алгебраических уравнений с ленточной матрицей

В данном примере решается система линейных алгебраических уравнений с симметричной положительно определенной ленточной матрицей. Используются подпрограммы PDPBTRF и PDPBTRS библиотеки ScaLAPACK. Матрица A формируется следующим образом:

6 -4 1 0 0 0 . . .
-4 6 -4 1 0 0 . . .
1 -4 6 -4 1 0 . . .
0 1 -4 6 -4 1 . . .
0 0 1 -4 6 -4 . . .
0 0 0 1 -4 6 . . .

с хранением верхнего треугольника (параметр UPLO ='U'), где звездочкой помечены неиспользуемые позиции:

* * a13 a24 a35 a46 . . . an-2,n
* a12 a23 a34 a45 a56 . . . an-1,n
a11 a22 a33 a44 a55 a66 . . . an,n
    program bandu
c nsize - максимальное число столбцов матрицы в одном процессоре
    parameter (nsize = 30000)
    double precision a(3*nsize), b(nsize), x(nsize), bg(nsize)
    double precision AF(3*nsize), WORK(10)
    integer ipvt(nsize), BW
    PARAMETER ( NOUT=6 )
    INTEGER DESCA(7), DESCB(7), DESCX(7)
    CALL BLACS_PINFO( IAM, NPROCS )

c Задаем размеры матрицы и сетки процессоров
    N = 9000
    NRHS = 1
    NPROW = 1
    NPCOL = 4
c BW - ширина ленты над диагональю
    BW=2
c Вычисляем NB - длину блока матрицы A
    NDD = mod(N,NPCOL)
    IF(NDD.EQ.0) THEN
    NB = N/NPCOL
    ELSE
    NB = N/NPCOL + 1
    END IF
    NB = MAX(NB,2*BW)
    NB = MIN(N,NB)
    IF(IAM.EQ.0) THEN
    WRITE( 6, FMT = 9998 ) 'N ', N
    WRITE( 6, FMT = 9998 ) 'NRHS ', NRHS
    WRITE( 6, FMT = 9998 ) 'BW   ', BW
    WRITE( 6, FMT = 9998 ) 'P ', NPROW
    WRITE( 6, FMT = 9998 ) 'Q ', NPCOL
    WRITE( 6, FMT = 9998 ) 'NB ', NB
    END IF
c Вычисляем размеры рабочих массивов
    LWORK = 2*BW*BW
    LAF = (NB+2*BW)*BW
c
c Инициализируем сетку процессоров
    CALL BLACS_GET( -1, 0, ICTXT )
    CALL BLACS_GRIDINIT( ICTXT, 'Row-major', NPROW, NPCOL )
    CALL BLACS_GRIDINFO( ICTXT, NPROW, NPCOL, MYROW, MYCOL )
    IF( MYROW.GE.NPROW .OR. MYCOL.GE.NPCOL )
 $  GO TO 500
c
    NP = NUMROC( (BW+1), (BW+1), MYROW, 0, NPROW )
    NQ = NUMROC( N, NB, MYCOL, 0, NPCOL )
c Формируем дескрипторы для левых и правых частей уравнения
    DESCA(1) = 501
    DESCA(2) = ICTXT
    DESCA(3) = N
    DESCA(4) = NB
    DESCA(5) = 0
    DESCA(6) = BW+1
    DESCA(7) = 0
c
    DESCB(1) = 502
    DESCB(2) = ICTXT
    DESCB(3) = N
    DESCB(4) = NB
    DESCB(5) = 0
    DESCB(6) = NB
    DESCB(7) = 0
c
    lda = NB
c Вызов подпрограммы генерации ленточной матрицы и правой части
    call pmatgenb(a, DESCA, bw, b, DESCB, nprow, npcol,
 $                myrow, mycol, n, bg)
c Факторизация матрицы
    CALL PDPBTRF('U', N, BW, A, 1, DESCA, AF, LAF, WORK, LWORK, INFO3 )
c Решение системы
    CALL PDPBTRS('U', N, BW, NRHS, A, 1, DESCA, B, 1, DESCB,
 $                AF, LAF, WORK, LWORK, INFO)
    if (iam.eq.0) then
    write(6,40)
 40 format('x(1),...,x(4)')
    write(6,50) (b(i),i=1,4)
 50 format(4d16.8)
    end if
    CALL BLACS_GRIDEXIT( ICTXT )
    CALL BLACS_GRIDEXIT( ICTXTB
    CALL BLACS_EXIT (0)
500 continue
    stop
    end


c Подпрограмма-функция генерации (I,J)-го матричного элемента
    double precision function matij(i,j)
    double precision rab
    rab = 0.0d0
    if(i.eq.j)               rab =  6.0d0
    if(i.eq.j+1.or.j.eq.i+1) rab = -4.0d0
    if(i.eq.j+2.or.j.eq.i+2) rab =  1.0d0
    matij = rab
    return
    end

c Подпрограмма генерации матрицы A и вектора В
    subroutine pmatgenb(a, DESCA, bw, b, DESCB, nprow,npcol,
 $                      myrow, mycol, n, bg)
    integer i, j, DESCA(*), DESCB(*), nprow, npcol, bw, bw1, myrow, mycol
    double precision a(bw+1,*), b(*), bg(*), matij

    nb = DESCA(4)
    ICTXT = DESCA(2)
    n = DESCA(3)
    BW1 = BW + 1
c Генерация всех компонент вектора B таким образом,
c чтобы решение X(I) = I
    do 231 i = 1,n
    bg(i) = 0.0
    n1 = max(1,i-bw)
    n2 = min(n,i+bw)
    do 231 j = n1,n2
    bg(i) = bg(i) + matij(i,j)*j
 231 continue
c
c Вычисление локальной части матрицы A
    jcs = MYCOL*NB
    NC  = MIN(NB,N-jcs)
    do 250 j = 1,NC
    jc = jcs + j
    do 240 i = 1,BW1
    ic = jc - BW1 + i
    if (ic.ge.1 )   a(i,j) = matij(ic,jc)
 240 continue
 250 continue
c Заполнение локальной части вектора B
    do 350 i = 1,NC
    b(i) = bg(jcs+i)
 351 continue
 350 continue
    return
    end


Next:2. ИСПОЛЬЗОВАНИЕ БИБЛИОТЕКИ ПАРАЛЛЕЛЬНЫХ ПОДПРОГРАММ Aztec
Up:1. БИБЛИОТЕКА ПОДПРОГРАММ ScaLAPACK
Prev:1.3. Использование библиотеки ScaLAPACK