1

我需要在我的程序中使用矩阵数据结构,而 C++ 有 2d 数组,它的级别非常低,而像 Eigen 这样的一些库提供了更高级别的矩阵数据结构。但在我看来,无论一个库在 svd 等一些高技能的操作中表现如何,在包括 read(access)、write、sum、dot 在内的基本操作上的快速速度应该是此类库的先决条件. 因为在实际应用中,这样的基本操作可能比那些高技能的操作频繁得多,如果库在这些操作上的速度很慢,可能会成为系统的负担甚至瓶颈。

所以我用二维数组和 Eigen3 密集矩阵 (MatrixXd) 编写了一些非常简单的程序,并比较了它们在 4 种基本操作上的表现,结果发现大多数时候,二维数组胜过 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;
}
4

1 回答 1

3

对于特征测试函数,您按值传递矩阵,这意味着它必须被复制。这些(大)副本的时间包含在基准中。

您应该改为通过引用传递矩阵以避免复制开销并获得与数组版本相同的语义。通过此更改,我得到如下结果,这对我来说听起来相当快:

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毫无意义:您明确告诉编译器不要让它变快。)

于 2013-07-03T03:12:04.097 回答