当我使用本征密集矩阵数据结构时,我应该期待什么
what should I expect when I use Eigen dense matrix data structure?
我需要在程序中使用矩阵数据结构,而C++有2d数组,这是非常低的级别,而像Eigen这样的一些库提供了更高级别的矩阵数据结构。但在我看来,无论一个库在一些高技能的操作(如svd)中表现得多么好,在读(访问)、写、求和、点等基本操作上的快速都应该是此类库的先决条件。因为在实际应用中,这样的基本操作可能比那些高技能的操作频繁得多,如果库在这样的操作上很慢,它可能会成为系统的负担甚至瓶颈。
因此,我使用2d阵列和Eigen3稠密矩阵(MatrixXd)编写了一些非常简单的程序,并比较了它们在4种基本运算上的性能,结果发现,大多数情况下,2d阵列都赢得了Eigen3,这非常令人失望。我在下面列出了我的一些测试结果(代码在最后的附录中):
10000X10000矩阵,编译命令:g++-o test.otest.cpp-O0-msse2
特征:
[!COST]init:6.8秒
[!成本]读数:14.85秒
[!COST]写入:23.02秒
[!成本]总计:3.28秒
[!COST]dot:3.12秒
CPP:
[!COST]初始化:1.81秒
[!COST]读数:2.4秒
[!COST]写入:3.4秒
[!成本]总和:0.63秒
[!COST]点:0.52秒
10000X10000矩阵,编译命令:g++-o test.otest.cpp-O3-msse2
特征:
[!COST]初始化:2.44秒
[!COST]读数:2.16秒
[!COST]写入:2.18秒
[!COST]总和:0.26秒
[!COST]dot:0.26秒
CPP:
[!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;
}
对于特征测试函数,您通过值传递矩阵,这意味着必须复制它。这些(大)拷贝的时间包含在基准测试中。
相反,您应该通过引用传递矩阵,以避免复制开销,并获得与数组版本相同的语义。有了这个变化,我得到了如下结果,听起来相当快:
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
进行基准测试是毫无意义的:您明确地告诉编译器不要让它变快。)
- 我对数据结构、双向链表有一些问题
- 我应该在C++中将这些结构用作参数化构造函数吗?
- 我可以使用哪种数据结构来处理这种方式
- 我应该为地图使用哪种数据结构?(C++)
- 我应该使用什么样的数据结构来实现UPGMA
- 我应该如何在恒定长度的 STL 样式数据结构中实现max_size?
- Qt:通过深度复制访问列表中的数据结构是否应该比通过指针访问它慢得多
- 我应该使用什么数据结构来对数据库/表进行建模
- 如何维护城市道路数据?(我应该使用什么数据结构)
- 数据结构:我获得了不希望的价值
- 我需要一个像堆栈一样的数据结构,但具有随机访问,但是,我应该实现什么
- 我应该如何共享数据结构
- 我应该使用哪些键值数据结构?按值快速检索和按键快速检索
- 我应该在这个C++项目中使用哪种数据结构
- 读取前检查boost::asio套接字上的数据?或者我应该使用async_read()
- 当我使用本征密集矩阵数据结构时,我应该期待什么
- 我应该使用哪种数据结构来达到我的目的
- C++ - 3-D 数据结构 - 我应该使用指针向量还是向量向量
- 我应该使用什么数据结构来支持插入、删除和随机选择
- 我应该编写自己的数据结构来处理语义解析还是直接在编译器项目中使用STL