Сокращение OpenMP с матрицами GMP / ARB - PullRequest
0 голосов
/ 29 мая 2018

Я хочу распараллелить написанную мною программу, которая вычисляет ряд, включающий матричные и векторные произведения, с результатом, являющимся вектором.Поскольку аргументы становятся очень маленькими и большими, я использую ARB (на основе GMP, MPFR и flint), чтобы предотвратить потерю значимости.Кроме того, поскольку элементы серии являются независимыми, размеры матрицы невелики, но ряд должен оцениваться до 50 тыс. Элементов, имеет смысл иметь несколько потоков, каждый из которых вычисляет несколько элементов серии, то есть 5 потоков могут вычислять 10 тыс. Каждый.элементы параллельно и затем суммируют результирующие векторы.

Теперь проблема в том, что функция ARB для сложения векторов и матриц не является стандартной операцией, которую можно легко использовать с сокращением openmp.Когда я наивно пытаюсь написать собственное сокращение, g ++ жалуется на тип void, поскольку операции в ARB не имеют возвращаемого значения:

void arb_add(arb_t z, const arb_t x, const arb_t y, slong prec)¶

вычислит и установит z в z = x + y сточность предварительных битов, но сам arb_add является функцией void.

В качестве примера: случайный цикл for для аналогичной проблемы выглядит следующим образом (моя настоящая программа, конечно, отличается)

[...]

arb_mat_t RMa,RMb,RMI,RMP,RMV,RRes;

arb_mat_init(RMa,Nmax,Nmax); //3 matrices
arb_mat_init(RMb,Nmax,Nmax);
arb_mat_init(RMI,Nmax,Nmax);
arb_mat_init(RMV,Nmax,1);  // 3 vectors
arb_mat_init(RMP,Nmax,1);
arb_mat_init(RRes,Nmax,1);

[...]

//Result= V + ABV +(AB)^2 V + (AB)^3 V + ...
//in my actual program A and B would be j- and k-dependent and would
//include more matrices and vectors

#pragma omp parallel for collapse(1) private(j)
for(j=0; j<jmax; j++){
        arb_mat_one(RMI); //sets the matrix RMI to 1
        for(k=0; k<j; k++){ 
            Qmmd(RMI,RMI,RMa,Nmax,prec); //RMI=RMI*RMa
            Qmmd(RMI,RMI,RMb,Nmax,prec); //RMI=RMI*RMb
            cout << "j=" << j << ",   k=" << k << "\r" << flush;
        }

        Qmvd(RMP,RMI,RMV,Nmax,prec);     //RMP=RMI*RMV
        arb_mat_add(RRes,RRes,RMP,prec); //Result=Result + RMP        
}

[...]

, что, конечно, не работает при использовании более 1 потока, потому что я не указал сокращение RRes.Здесь Qmmd () и Qmvd () являются самописными матрично-матричными и матрично-векторными функциями произведений, а RMa, RMb и RMV являются случайными матрицами и векторами, соответственно

Идея состоит в том, чтобы уменьшить RResтак, что каждый поток может вычислить частную версию RRes, включая часть конечного результата, прежде чем добавлять их все, используя arb_mat_add.Я мог бы написать функцию matrixadd (A, B) для вычисления A = A + B

void matrixadd(arb_mat_t A, arb_mat_t B) {
    arb_mat_add(A,A,B,2000); 
//A=A+B, the last value is the precision in bits used for that operation
}

, а затем в конечном итоге

#pragma omp declare reduction \
(myadd : void : matrixadd(omp_out, omp_in))
#pragma omp parallel for private(j) reduction(myadd:RRes) 
for(j=0; j<jmax; j++){
        arb_mat_one(RMI);
        for(k=0; k<j; k++){
            Qmmd(RMI,RMI,RMa,Nmax,prec);
            Qmmd(RMI,RMI,RMb,Nmax,prec);
            cout << "j=" << j << ",   k=" << k << "\r" << flush;
        }

        Qmvd(RMP,RMI,RMV,Nmax,prec); 
        matrixadd(RRes,RMP);        
}

Gcc недоволен этим:

main.cpp: In function ‘int main()’:
main.cpp:503:46: error: invalid use of void expression
     (myadd : void : matrixadd(omp_out, omp_in))
                                          ^
main.cpp:504:114: error: ‘RRes’ has invalid type for ‘reduction’

Может ли Openmp понять мое сокращение пустот и может ли он работать с ARB и GMP?Если так, то как?Спасибо!

(Кроме того, моя программа в настоящее время включает проверку сходимости с условием разрыва в цикле j. Если вы также знаете, как легко реализовать такую ​​вещь, я был бы очень признателен, потому чтодля моих текущих тестов openmp я просто удалил разрыв и установил постоянную jmax.)

Мой вопрос очень похож на этот .

Редактировать: Извините, вотмоя попытка минимального, полного и проверяемого примера.Дополнительные необходимые пакеты: arb , flint , gmp , mpfr (доступно через менеджеры пакетов) и gmpfrxx .

#include <iostream>
#include <omp.h>
#include <cmath>
#include <ctime>

#include <cmath>
#include <gmp.h>
#include "gmpfrxx/gmpfrxx.h"

#include "arb.h"
#include "acb.h"
#include "arb_mat.h"

using namespace std;

void generate_matrixARBdeterministic(arb_mat_t Mat, int N, double w2) //generates some matrix
{
    int i,j;
    double what;
    for(i=0;i<N;i++)
    {
        for(j=0;j<N;j++)
        {
            what=(i*j+30/w2)/((1+0.1*w2)*j+20-w2);
            arb_set_d(arb_mat_entry(Mat,i,j),what);
        }
    }
}

void generate_vecARBdeterministic(arb_mat_t Mat, int N) //generates some vector
{
    int i;
    double what;
    for(i=0;i<N;i++)
    {
        what=(4*i*i+40)/200;
        arb_set_d(arb_mat_entry(Mat,i,0),what);
    }
}

void Qmmd(arb_mat_t res, arb_mat_t MA, arb_mat_t MB, int NM, slong prec)
{   ///res=M*M=Matrix * Matrix

    arb_t Qh1;
    arb_mat_t QMh;

    arb_init(Qh1);

    arb_mat_init(QMh,NM,NM);

    for (int i=0; i<NM; i++){
            for(int j=0; j<NM; j++){
                     for (int k=0; k<NM; k++ ) {
                        arb_mul(Qh1,arb_mat_entry(MA, i, k),arb_mat_entry(MB, k, j),prec);
                        arb_add(arb_mat_entry(QMh, i, j),arb_mat_entry(QMh, i, j),Qh1,prec);
                    }
             }
    }

    arb_mat_set(res,QMh);

    arb_mat_clear(QMh);
    arb_clear(Qh1);
  }

void Qmvd(arb_mat_t res, arb_mat_t M, arb_mat_t V, int NM, slong prec)  //res=M*V=Matrix * Vector
{ ///res=M*V
    arb_t Qh,Qs;
    arb_mat_t QMh;

    arb_init(Qh);
    arb_init(Qs);
    arb_mat_init(QMh,NM,1);

    arb_set_ui(Qh,0.0);
    arb_set_ui(Qs,0.0);
    arb_mat_zero(QMh);

    for (int i=0; i<NM; i++){
            arb_set_ui(Qs,0.0);
            for(int j=0; j<NM; j++){
                    arb_mul(Qh,arb_mat_entry(M, i, j),arb_mat_entry(V, j, 0),prec);
                    arb_add(Qs,Qs,Qh,prec);
             }
            arb_set(arb_mat_entry(QMh, i, 0),Qs);
    }
    arb_mat_set(res,QMh);

    arb_mat_clear(QMh);
    arb_clear(Qh);
    arb_clear(Qs);
}

void QPrintV(arb_mat_t A, int N){ //Prints Vector
    for(int i=0;i<N;i++){
                cout <<  arb_get_str(arb_mat_entry(A, i, 0),5,0) << endl; //ARB_STR_NO_RADIUS
    }
}

void matrixadd(arb_mat_t A, arb_mat_t B) {
    arb_mat_add(A,A,B,2000);
}

int main() {
    int Nmax=10,jmax=300; //matrix dimension and max of j-loop
    ulong prec=2000; //precision for arb

    //initializations

    arb_mat_t RMa,RMb,RMI,RMP,RMV,RRes;

    arb_mat_init(RMa,Nmax,Nmax);
    arb_mat_init(RMb,Nmax,Nmax);
    arb_mat_init(RMI,Nmax,Nmax);
    arb_mat_init(RMV,Nmax,1);
    arb_mat_init(RMP,Nmax,1);
    arb_mat_init(RRes,Nmax,1);

    omp_set_num_threads(1);
    cout << "Maximal threads is " << omp_get_max_threads() << endl;

    generate_matrixARBdeterministic(RMa,Nmax,1.0); //generates some Matrix for RMa
    arb_mat_set(RMb,RMa); // sets RMb=RMa

    generate_vecARBdeterministic(RMV,Nmax); //generates some vector

    double st=omp_get_wtime();

    Qmmd(RMI,RMa,RMb,Nmax,prec);
    int j,k=0;

    #pragma omp declare reduction \
    (myadd : void : matrixadd(omp_out, omp_in))
    #pragma omp parallel for private(j) reduction(myadd:RRes)
    for(j=0; j<jmax; j++){
            arb_mat_one(RMI);
            for(k=0; k<j; k++){
                Qmmd(RMI,RMI,RMa,Nmax,prec);
                Qmmd(RMI,RMI,RMb,Nmax,prec);
                cout << "j=" << j << ",   k=" << k << "\r" << flush;
            }

            Qmvd(RMP,RMI,RMV,Nmax,prec);  
            matrixadd(RRes,RMP);      
    }

    QPrintV(RRes,Nmax);

    double en=omp_get_wtime();
    printf("\n Time it took was %lfs\n",en-st);


    arb_mat_clear(RMa);
    arb_mat_clear(RMb);
    arb_mat_clear(RMV);
    arb_mat_clear(RMP);
    arb_mat_clear(RMI);
    arb_mat_clear(RRes);

    return 0;
}

и

g++ test.cpp -g -fexceptions -O3 -ltbb -fopenmp -lmpfr -lflint -lgmp -lgmpxx -larb -I../../PersonalLib -std=c++14 -lm -o b.out

Ответы [ 2 ]

0 голосов
/ 30 мая 2018

Вы можете сделать сокращение вручную следующим образом:

#pragma omp parallel
{
    arb_mat_t RMI, RMP;
    arb_mat_init(RMI,Nmax,Nmax);  //allocate memory
    arb_mat_init(RMP,Nmax,1);     //allocate memory
    #pragma omp for
    for(int j=0; j<jmax; j++){
        arb_mat_one(RMI);
        for(int k=0; k<j; k++){
            Qmmd(RMI,RMI,RMa,Nmax,prec);
            Qmmd(RMI,RMI,RMb,Nmax,prec);
        }
    }
    Qmvd(RMP,RMI,RMV,Nmax,prec);
    #pragma omp critical
    arb_mat_add(RRes,RRes,RMP,prec);
    arb_mat_clear(RMI);  //deallocate memory
    arb_mat_clear(RMP);  //deallocate memory
}

Если вы хотите использовать declare reduction, вам нужно создать оболочку C ++ для arb_mat_t.Использование declare reduction позволяет OpenMP решить, как выполнить сокращение.Но я очень сомневаюсь, что вы найдете случай, когда это дает лучшую производительность, чем случай с ручным управлением.

0 голосов
/ 29 мая 2018

Вы можете создать массив матриц для каждого потока для суммирования.Вы просто заменяете matrixadd(RRes, RMP) на matrixadd(RRes[get_omp_thread_num()], RMP) и затем суммируете все RRes в конце, где RRes теперь будет std::vector<arb_mat_t>.

Или вы можете попытаться определить оператор сложения для класса-оболочки, конечно, вы должны быть осторожны, чтобы не копировать всю матрицу.Это вызывает больше хлопот, так как вы должны быть немного осторожнее с управлением памятью (поскольку вы используете библиотеку - вы не знаете точно, что она делает, если вы не потратите время на ее прохождение).

...