Мне нужно использовать матричную структуру данных в моей программе, в то время как C++ имеет двумерные массивы, это очень низкий уровень, в то время как некоторые библиотеки, такие как Eigen, обеспечивают более высокий уровень матричной структуры данных. Но, на мой взгляд, независимо от того, насколько хорошо библиотека работает в некоторых высококвалифицированных операциях, таких как svd, высокая скорость основных операций, включая чтение (доступ), запись, суммирование, точка, должна быть предварительным требованием таких библиотек. . Поскольку в реальных приложениях такие базовые операции могут выполняться гораздо чаще, чем высококвалифицированные, то если библиотека на таких операциях тормозит, то это может оказаться бременем или даже узким местом системы.
Итак, я пишу несколько очень простых программ, используя как 2d-массив, так и плотную матрицу Eigen3 (MatrixXd), и сравниваю их производительность на 4 основных операциях. Оказывается, что в большинстве случаев 2d-массив выигрывает у Eigen3, что весьма разочаровывает. Я перечисляю некоторые из результатов моего теста ниже (код находится в конце приложения):
Матрица 10000X10000, команда компиляции: g++ -o test.o test.cpp -O0 -msse2
Эйген:
[!COST] инициализация: 6,8 сек.
[!COST] чтение: 14,85 сек.
[!COST] запись: 23,02 сек.
Сумма [!COST]: 3,28 сек.
[!COST] точка: 3,12 сек.
Цена за конверсию:
[!COST] инициализация: 1,81 сек.
[!COST] чтение: 2,4 сек.
[!COST] запись: 3,4 сек.
Сумма [!COST]: 0,63 сек.
[!COST] точка: 0,52 сек.
Матрица 10000X10000, команда компиляции: g++ -o test.o test.cpp -O3 -msse2
Эйген:
[!COST] инициализация: 2,44 сек.
[!COST] чтение: 2,16 сек.
[!COST] запись: 2,18 сек.
Сумма [!COST]: 0,26 сек.
[!COST] точка: 0,26 сек.
Цена за конверсию:
[!COST] инициализация: 1,71 сек.
[!COST] чтение: 2,06 сек.
[!COST] запись: 2,24 сек.
Сумма [!COST]: 0,15 сек.
[!COST] точка: 0,06 сек.
Тем не менее, у меня все еще есть некоторые сомнения по этому поводу, может быть, я не должен ожидать, что абстракция более высокого уровня матричной структуры будет работать так же быстро, как ее необработанная версия, если да, то чего мне ожидать, используя такую библиотеку, как Eigen? Обратите внимание, что в моей программе есть некоторые высококвалифицированные операции, такие как SVD, а есть более простые операции, такие как доступ к матрице и запись матрицы.
Приложение, test.cpp:
#include <iostream>
#include <Eigen/Dense>
#include <ctime>
using Eigen::MatrixXf;
inline int cpp_testor_read(float **m, const int M, const int N)
{
float randomTmp = 0;
for (int i = 0; i < M; i ++)
for (int j = 0; j < N; j ++)
{
randomTmp += m[i][j];
randomTmp -= m[j][i];
}
return randomTmp;
}
inline int eigen_testor_read(MatrixXf m, const int M, const int N)
{
float randomTmp = 0;
for (int i = 0; i < M; i ++)
for (int j = 0; j < N; j ++)
{
randomTmp += m(i, j);
randomTmp -= m(j, i);
}
return randomTmp;
}
inline int cpp_testor_write(float **m, const int M, const int N)
{
for (int i = 0; i < M; i ++)
for (int j = 0; j < N; j ++)
{
m[i][j] += m[j][i];
m[j][i] -= m[i][j];
}
return m[rand()%10000][rand()%10000];
}
inline int eigen_testor_write(MatrixXf m, const int M, const int N)
{
for (int i = 0; i < M; i ++)
for (int j = 0; j < N; j ++)
{
m(i, j) += m(j, i);
m(j, i) -= m(i, j);
}
return m(rand()%10000, rand()%10000);
}
inline int cpp_testor_sum(float **m, const int M, const int N, float val)
{
for (int i = 0; i < M; i ++)
for (int j = 0; j < N; j ++)
{
m[i][i] += m[i][j];
}
return m[rand()%1000][rand()%1000];
}
inline int eigen_testor_sum(MatrixXf m, const int M, const int N, float val)
{
m += m;
return m(0, 0);
}
inline int cpp_testor_dot(float **m, const int M, const int N, float val)
{
float randomTmp = 0;
for (int i = 0; i < M; i ++)
for (int j = 0; j < N; j ++)
{
m[i][j] *= val;
}
return m[rand()%1000][rand()%1000];
}
inline int eigen_testor_dot(MatrixXf m, const int M, const int N, float val)
{
m *= val;
return m(0, 0);
}
float** cpp_generator_mtarix(const int M, const int N)
{
float **m = new float*[M];
for (int i = 0; i < M; i ++)
m[i] = new float[N];
return m;
}
MatrixXf& eigen_generator_matrix(const int M, const int N)
{
static MatrixXf m(M,N);
return m;
}
int main()
{
const int M = 10000;
const int N = M;
int antiopt = 0;
srand(time(NULL));
float val1 = rand()%10000 + 1;
float val2 = rand()%10000 + 1;
std::cout<< M << " " << N << std::endl;
std::cout<<"Eigen:" << std::endl;
size_t t = clock();
//MatrixXf m = eigen_generator_matrix(M, N);
MatrixXf m(M,N);
for (int i = 0; i < M; i ++)
for (int j = 0; j < N; j ++)
m(i,j) = rand()%1000 + 1;
t = clock() - t;
std::cout<< "[!COST] init: " << t/float(CLOCKS_PER_SEC) << " sec." <<std::endl;
t = clock();
antiopt += eigen_testor_read(m,M,N);
t = clock() - t;
std::cout<< "[!COST] read: " << t/float(CLOCKS_PER_SEC) << " sec." <<std::endl;
t = clock();
antiopt += eigen_testor_write(m,M,N);
t = clock() - t;
std::cout<< "[!COST] write: " << t/float(CLOCKS_PER_SEC) << " sec." <<std::endl;
t = clock();
antiopt += eigen_testor_sum(m,M,N, val1);
t = clock() - t;
std::cout<< "[!COST] sum: " << t/float(CLOCKS_PER_SEC) << " sec." <<std::endl;
t = clock();
antiopt += eigen_testor_dot(m,M,N, val2);
t = clock() - t;
std::cout<< "[!COST] dot: " << t/float(CLOCKS_PER_SEC) << " sec." <<std::endl;
std::cout<<"CPP:" << std::endl;
t = clock();
//float **mm = cpp_generator_mtarix(M, N);
float **mm = new float*[M];
for (int i = 0; i < M; i ++)
mm[i] = new float[N];
for (int i = 0; i < M; i ++)
for (int j = 0; j < N; j ++)
mm[i][j] = rand()%1000 + 1;
t = clock() - t;
std::cout<< "[!COST] init: " << t/float(CLOCKS_PER_SEC) << " sec." <<std::endl;
t = clock();
antiopt += cpp_testor_read(mm,M,N);
t = clock() - t;
std::cout<< "[!COST] read: " << t/float(CLOCKS_PER_SEC) << " sec." <<std::endl;
t = clock();
antiopt += cpp_testor_write(mm,M,N);
t = clock() - t;
std::cout<< "[!COST] write: " << t/float(CLOCKS_PER_SEC) << " sec." <<std::endl;
t = clock();
antiopt += cpp_testor_sum(mm,M,N, val1);
t = clock() - t;
std::cout<< "[!COST] sum: " << t/float(CLOCKS_PER_SEC) << " sec." <<std::endl;
t = clock();
antiopt += cpp_testor_dot(mm,M,N, val2);
t = clock() - t;
std::cout<< "[!COST] dot: " << t/float(CLOCKS_PER_SEC) << " sec." <<std::endl;
std::cout<<antiopt<<std::endl;
}