Intel MKL LAPACKE_dsyevr на больших матрицах NxN с N~8*10^4
Я пытаюсь диагонализировать (все собственные значения и собственные векторы) матрицы NxN с размерностью N = 80000. Я использую intel MKL 2018 на машине с ~3 ТБ ОЗУ и использую подпрограмму intel LAPACKE_dsyevr. Я прилагаю код ниже, который создает матрицу размера NxN, а затем передает ее в процедуру dsyevr, чтобы найти собственные значения. Хотя код работает для N до ~ 50000, это не работает в случае N ~ 80000, и возникает исключение памяти. Это не может быть связано с памятью моей машины, так как код долго остается в подпрограмме dsyevr до сбоя с ошибкой сегментации.
Обратите внимание, что мне нужно объявить матрицу как std::vector из-за моей основной структуры кода. Поскольку подпрограмма MKL C нуждается в матрице в качестве указателя, я указываю указатель на первый элемент вектора.
Код здесь
// this uses relatively robust representations
#include<iostream>
#include<fstream>
#include<stdlib.h>
#include<stdio.h>
#include<vector>
#include<iterator>
#include<math.h>
#include "mkl_lapacke.h"
#include "mkl.h"
/* Auxiliary routines prototypes */
extern void print_matrix( char* desc, MKL_INT m, MKL_INT n, double* a, MKL_INT lda );
extern void fileprint_matrix( char* desc, MKL_INT m, MKL_INT n, double* a, MKL_INT lda );
/* Main program */
int main() {
unsigned int k;
unsigned int i,j;
MKL_INT N, NSQ, LDA, LDZ, NSELECT, info;
MKL_INT il, iu, m;
double abstol, vl, vu;
N=80000;
NSQ=N*N;
LDA=N;
LDZ=N;
/* allocate space for the arrays */
std::vector<MKL_INT> isuppz(2*N,0);
std::vector<double> w(N,0.0);
std::vector<double> z(NSQ,0.0);
std::vector<double> a(NSQ,0.0);
std::cout<<"size of vector "<<a.size()<<std::endl;
std::cout<<"maximum capacity of vector "<<a.max_size()<<std::endl;
// make the matrix
k=0;
for(size_t pos=0; pos < a.size(); pos++){
i=k/N; j=k%N;
a.at(pos) = 1.0/(i+j+1) + (i+j)*exp(-0.344*(i+j));
k++;
}
double* A = &a[0];
/* print full matrix */
//print_matrix("Full matrix", N, N, A, LDA);
/* Executable statements */
printf( "LAPACKE_dsyevr (column-major, high-level) RRR Example Program Results\n" );
/* Solve eigenproblem */
abstol=-1;
MKL_INT* ISUPPZ = &isuppz[0];
double* W = &w[0];
double* Z = &z[0];
info = LAPACKE_dsyevr( LAPACK_COL_MAJOR, 'V', 'A', 'U', N, A, LDA,
vl, vu, il, iu, abstol, &m, W, Z, LDZ, ISUPPZ );
printf("Back from the routine and working properly. Info = %d. Eval[0]=%e\n",info,W[0]);
//printf("No of eigenvalues = %d\n",m);
/* Check for convergence */
if( info > 0 ) {
printf( "The algorithm failed to compute eigenvalues.\n" );
exit( 1 );
}
isuppz.clear();
w.clear();
z.clear();
a.clear();
exit( 0 );
}
1 ответ
Хорошо, мой друг. Что вы ожидаете? Позвольте мне начать с поздравления с интересной проблемой. Я хотел бы решить это сам. Было сказано, что:
a
является 80000*80000*8 = 47.7GB
, Это только для хранения вашей матрицы. Но это не все. У вас также есть z
и некоторые смехотворно маленькие векторы 80000 двойников. Итак, вы начинаете с выделения 96 ГБ. Сколько оперативной памяти у вас есть для вашего процесса? Но это не твоя проблема, кажется. Ваша проблема LWORK
и с этим W. Документация просит LWORK
нужно хотя бы 1.26*N
:
LWORK is INTEGER
The dimension of the array WORK. LWORK >= max(1,26*N).
For optimal efficiency, LWORK >= (NB+6)*N,
where NB is the max of the blocksize for DSYTRD and DORMTR
returned by ILAENV.
If LWORK = -1, then a workspace query is assumed; the routine
only calculates the optimal size of the WORK array, returns
this value as the first entry of the WORK array, and no error
message related to LWORK is issued by XERBLA.
Но на самом деле, что вы должны сделать, это получить расчет для LWORK
из алгоритмов, установив его один раз -1
как предложено выше. Это даст вам правильный номер для LWORK
в первой клетке WORK
:
Что-то вроде этого:
LWORK=-1;
std::vector<double> w(1);
// Workspace estimation
info = LAPACKE_dsyevr( LAPACK_COL_MAJOR, 'V', 'A', 'U', N, A, LDA,
vl, vu, il, iu, abstol, &m, W, Z, LDZ, ISUPPZ );
LWORK = std::static_cast<int>(work.front());
try {
work.resize(LWORK);
} catch (const std::exception& e) {
std::cerr << "Failed to allocate work space." << std::endl;
exit 1;
}
// Actual calculation
info = LAPACKE_dsyevr( LAPACK_COL_MAJOR, 'V', 'A', 'U', N, A, LDA,
vl, vu, il, iu, abstol, &m, W, Z, LDZ, ISUPPZ );
Полностью забыл об одной из самых важных вещей, которые я хотел упомянуть: убедитесь, что вам действительно нужна двойная точность. LAPACKE_fsyevr
должен иметь оперативную память и быть примерно в два раза быстрее.