Чего следует ожидать, когда я использую плотную матрицу данных Eigen?

Мне нужно использовать матричную структуру данных в моей программе, в то время как C ++ имеет 2d-массивы, это очень низкий уровень, в то время как некоторые библиотеки, такие как Eigen, обеспечивают более высокий уровень структуры матричных данных. Но, на мой взгляд, независимо от того, насколько хорошо библиотека работает в некоторых высококвалифицированных операциях, таких как svd, высокая скорость выполнения основных операций, включая чтение (доступ), запись, суммирование, точка, должна быть предварительным требованием для таких библиотек. , Поскольку в реальных приложениях такие базовые операции могут быть гораздо более частыми, чем у высококвалифицированных, если библиотека медленно выполняет такие операции, это может оказаться бременем или даже узким местом системы.

Поэтому я пишу несколько очень простых программ, использующих как 2d-массив, так и плотную матрицу Eigen3 (MatrixXd), и сравниваю их производительность с 4 основными операциями. Оказывается, что в большинстве случаев 2d-массив выигрывает Eigen3, что весьма разочаровывает. Я перечислю некоторые результаты моего теста ниже (код находится в конце приложения):

Матрица 10000X10000, команда компиляции: g ++ -o test.o test.cpp -O0 -msse2

Эйген:

[! COST] init: 6,8 сек.

[! СТОИМОСТЬ] прочитано: 14,85 сек.

[! COST] запись: 23,02 сек.

[! COST] сумма: 3.28 сек.

Точка [! COST]: 3,12 сек.

CPP:

[! COST] init: 1,81 сек.

[! СТОИМОСТЬ] читать: 2,4 сек.

[! COST] запись: 3,4 сек.

[! COST] сумма: 0,63 сек.

Точка [! COST]: 0,52 сек.

Матрица 10000X10000, команда компиляции: g ++ -o test.o test.cpp -O3 -msse2

Эйген:

[! COST] init: 2,44 сек.

[! СТОИМОСТЬ] прочитано: 2.16 сек.

[! COST] запись: 2.18 сек.

[! COST] сумма: 0,26 сек.

[! COST] точка: 0,26 сек.

CPP:

[! COST] init: 1,71 сек.

[! СТОИМОСТЬ] прочитано: 2.06 сек.

[! СТОИМОСТЬ] запись: 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;
}

1

Решение

Для собственных тестовых функций вы передаете матрицу по значению, что означает, что она должна быть скопирована. Время для этих (больших) копий включено в тест.

Вместо этого вы должны передать матрицы по ссылке, чтобы избежать издержек копирования и получить ту же семантику, что и у версии массива. С этим изменением я получаю следующие результаты, которые звучат довольно быстро для меня:

10000 10000
Eigen:
[!COST] init: 3.5 sec.
[!COST] read: 2.98 sec.
[!COST] write: 3.03 sec.
[!COST] sum: 0.06 sec.
[!COST] dot: 0.07 sec.
CPP:
[!COST] init: 1.46 sec.
[!COST] read: 3.41 sec.
[!COST] write: 3.57 sec.
[!COST] sum: 0.14 sec.
[!COST] dot: 0.05 sec.

(Также обратите внимание, что сравнение с -O0 бессмысленно: вы явно указываете компилятору не делать это быстро.)

3

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]