Я надеюсь, что ответ на вопрос в названии состоит в том, что я делаю что-то глупое!
Вот проблема. Я хочу вычислить все собственные значения и собственные векторы вещественной симметричной матрицы. Я реализовал код в MATLAB (фактически, я запускаю его с помощью Octave) и С++, используя Научную библиотеку GNU. Я предоставляю свой полный код ниже для обеих реализаций.
Насколько я понимаю, GSL поставляется с собственной реализацией BLAS API (в дальнейшем я называю это GSLCBLAS) и использовать эту библиотеку, которую я компилирую, используя:
g++ -O3 -lgsl -lgslcblas
GSL предлагает здесь использовать альтернативную библиотеку BLAS, такую как самооптимизирующийся ATLAS, для повышения производительности. Я запускаю Ubuntu 12.04 и установил пакеты ATLAS из репозитория Ubuntu. В этом случае я компилирую, используя:
g++ -O3 -lgsl -lcblas -latlas -lm
Для всех трех случаев я выполнял эксперименты со случайно генерируемыми матрицами размером от 100 до 1000 с шагом 100. Для каждого размера я выполняю 10 собственных наборов с разными матрицами и усредняю время. Результатом является следующее:
Разница в производительности нелепо. Для матрицы размером 1000, Octave выполняет разложение в секунду; GSLCBLAS и ATLAS занимают около 25 секунд.
Я подозреваю, что я могу неправильно использовать библиотеку ATLAS. Любые объяснения приветствуются; спасибо заранее.
Некоторые примечания к коду:
-
В реализации С++ нет необходимости делать матрицу симметрично, потому что функция использует только нижнюю треугольную часть от него.
-
В Octave строка
triu(A) + triu(A, 1)'
обеспечивает симметричную матрицу. -
Если вы хотите скомпилировать код на С++ с вашей собственной машиной Linux, вам также нужно добавить флаг
-lrt
из-за функцииclock_gettime
. -
К сожалению, я не думаю, что
clock_gettime
выходит на другие платформы. Попробуйте изменить его наgettimeofday
.
Октавный код
K = 10;
fileID = fopen('octave_out.txt','w');
for N = 100:100:1000
AverageTime = 0.0;
for k = 1:K
A = randn(N, N);
A = triu(A) + triu(A, 1)';
tic;
eig(A);
AverageTime = AverageTime + toc/K;
end
disp([num2str(N), " ", num2str(AverageTime), "\n"]);
fprintf(fileID, '%d %f\n', N, AverageTime);
end
fclose(fileID);
Код С++
#include <iostream>
#include <fstream>
#include <time.h>
#include <gsl/gsl_rng.h>
#include <gsl/gsl_randist.h>
#include <gsl/gsl_eigen.h>
#include <gsl/gsl_vector.h>
#include <gsl/gsl_matrix.h>
int main()
{
const int K = 10;
gsl_rng * RandomNumberGenerator = gsl_rng_alloc(gsl_rng_default);
gsl_rng_set(RandomNumberGenerator, 0);
std::ofstream OutputFile("atlas.txt", std::ios::trunc);
for (int N = 100; N <= 1000; N += 100)
{
gsl_matrix* A = gsl_matrix_alloc(N, N);
gsl_eigen_symmv_workspace* EigendecompositionWorkspace = gsl_eigen_symmv_alloc(N);
gsl_vector* Eigenvalues = gsl_vector_alloc(N);
gsl_matrix* Eigenvectors = gsl_matrix_alloc(N, N);
double AverageTime = 0.0;
for (int k = 0; k < K; k++)
{
for (int i = 0; i < N; i++)
{
for (int j = 0; j < N; j++)
{
gsl_matrix_set(A, i, j, gsl_ran_gaussian(RandomNumberGenerator, 1.0));
}
}
timespec start, end;
clock_gettime(CLOCK_MONOTONIC_RAW, &start);
gsl_eigen_symmv(A, Eigenvalues, Eigenvectors, EigendecompositionWorkspace);
clock_gettime(CLOCK_MONOTONIC_RAW, &end);
double TimeElapsed = (double) ((1e9*end.tv_sec + end.tv_nsec) - (1e9*start.tv_sec + start.tv_nsec))/1.0e9;
AverageTime += TimeElapsed/K;
std::cout << "N = " << N << ", k = " << k << ", Time = " << TimeElapsed << std::endl;
}
OutputFile << N << " " << AverageTime << std::endl;
gsl_matrix_free(A);
gsl_eigen_symmv_free(EigendecompositionWorkspace);
gsl_vector_free(Eigenvalues);
gsl_matrix_free(Eigenvectors);
}
return 0;
}