为什么增强矩阵乘法比我的慢?

时间:2012-06-19 22:50:10

标签: c++ performance boost ublas boost-ublas

我已使用boost::numeric::ublas::matrix实现了一次矩阵乘法(参见my full, working boost code

Result result = read ();

boost::numeric::ublas::matrix<int> C;
C = boost::numeric::ublas::prod(result.A, result.B);

另一个使用标准算法(参见full standard code):

vector< vector<int> > ijkalgorithm(vector< vector<int> > A, 
                                    vector< vector<int> > B) {
    int n = A.size();

    // initialise C with 0s
    vector<int> tmp(n, 0);
    vector< vector<int> > C(n, tmp);

    for (int i = 0; i < n; i++) {
        for (int k = 0; k < n; k++) {
            for (int j = 0; j < n; j++) {
                C[i][j] += A[i][k] * B[k][j];
            }
        }
    }
    return C;
}

这就是我测试速度的方法:

time boostImplementation.out > boostResult.txt
diff boostResult.txt correctResult.txt

time simpleImplementation.out > simpleResult.txt
diff simpleResult.txt correctResult.txt

两个程序都读取一个包含两个2000 x 2000矩阵的硬编码文本文件。 这两个程序都是用这些标志编译的:

g++ -std=c++98 -Wall -O3 -g $(PROBLEM).cpp -o $(PROBLEM).out -pedantic

我的实施时间 15秒,而 4分钟用于提升实施!

编辑:用

编译后
g++ -std=c++98 -Wall -pedantic -O3 -D NDEBUG -DBOOST_UBLAS_NDEBUG library-boost.cpp -o library-boost.out

ikj算法 28.19秒,Boost 60.99秒。所以Boost仍然相当慢。

为什么提升比我的实施慢得多?

3 个答案:

答案 0 :(得分:47)

如TJD所指出的那样,可以通过调试后者的功能来部分解释uBLAS版本的较慢性能。

以下是uBLAS版本调试时间:

real    0m19.966s
user    0m19.809s
sys     0m0.112s

以下是关闭调试的uBLAS版本所花费的时间(添加了-DNDEBUG -DBOOST_UBLAS_NDEBUG编译器标志):

real    0m7.061s
user    0m6.936s
sys     0m0.096s

因此,关闭调试功能后,uBLAS版本的速度提高了近3倍。

可以通过引用uBLAS FAQ的以下部分来解释剩余的性能差异“为什么uBLAS比(atlas-)BLAS慢得多”:

  

ublas的一个重要设计目标是尽可能一般。

这种普遍性几乎总是伴随着成本。特别地,prod函数模板可以处理不同类型的矩阵,例如稀疏矩阵或三角矩阵。幸运的是,uBLAS提供了针对密集矩阵乘法优化的替代方案,特别是axpy_prodblock_prod。以下是比较不同方法的结果:

ijkalgorithm   prod   axpy_prod  block_prod
   1.335       7.061    1.330       1.278

正如您所看到的,axpy_prodblock_prod都比实施速度快一些。仅测量没有I / O的计算时间,删除不必要的复制并仔细选择block_prod(我使用64)的块大小可以使差异更加深远。

另请参阅uBLAS FAQEffective uBlas and general code optimization

答案 1 :(得分:13)

我相信,你的编译器没有足够的优化。 uBLAS代码大量使用模板和模板需要大量使用优化。我在发布模式下通过MS VC 7.1编译器为1000x1000矩阵运行代码,它给了我

对于uBLAS

10.064

7.851 s for vector

差异仍然存在,但绝不是压倒性的。 uBLAS的核心概念是惰性评估,因此prod(A, B)仅在需要时评估结果,例如prod(A, B)(10,100)将立即执行,因为实际上只会计算一个元素。因此,实际上没有用于整个矩阵乘法的专用算法可以被优化(见下文)。但你可以稍微帮助图书馆,宣布

matrix<int, column_major> B;

会减少4.426的运行时间,而单手操作可以击败你的功能。这个声明使得在乘法矩阵时可以更加顺序地访问内存,从而优化缓存的使用。

P.S。读完uBLAS文档到最后;),您应该已经发现实际上有一个专用函数可以立即乘以整个矩阵。 2个功能 - axpy_prodopb_prod。所以

opb_prod(A, B, C, true);

即使是未经优化的row_major B矩阵也会在8.091秒内执行,并且与您的矢量算法相同

P.P.S。还有更多的优化:

C = block_prod<matrix<int>, 1024>(A, B);

4.4中执行,无论B是column_还是row_ major。 考虑描述:“函数block_prod是为大密度矩阵设计的。”为特定任务选择特定工具!

答案 2 :(得分:2)

我创建了一个小网站Matrix-Matrix Product Experiments with uBLAS。它是关于将矩阵矩阵产品的新实现集成到uBLAS中。如果您已经拥有boost库,则它只包含额外的4个文件。所以它几乎是独立的。

如果其他人可以在不同的机器上运行简单的基准测试,我会感兴趣。