为什么列式乘法比计算Schur补码的整体乘法效率低?

时间:2018-05-23 08:22:02

标签: c++ eigen3

我们试图计算由对角线块U和V以及偏移对角线块W和W'(W的转置)组成的大对称正定矩阵H的Schur补码,并且V是对角正定矩阵。 Schur补充公式是

H <-- U - W*V^(-1)*W'                       (1)

在实施操作时,一种方法是直接使用上述等式。

第二种方式利用了V将全零作为其对角线元素的事实。公式1写出并实现如下

H <-- U - \Sigma W_i * V_{ii}^(-1) * (W_i)'      (2)

其中W_i是W的第i列,V_ {ii}是V的第i个对角线元素,并且\ Sigma对W的每一列求和。显然,(2)预期比(1)更有效。它避免了整个大矩阵的乘法。

这两种方法都是用C ++语言的Eigen库实现的,并在PC和带有intel atom处理器的移动设备上进行测试。

令人惊讶的是,在intel核心i7 4核PC上,(2)的代码片段(用gcc,-O3,发布模式编译)效率低于(1)。 (1)和(2)的中值计算时间分别为0.092 ms,0.212 ms,注意比率为2.30。在intel atom 4核处理器上,观察到类似的(用clang -O3编译的代码片段)。 (1)和(2)的中值计算时间分别为0.575 ms,1.923 ms,注意比率为3.34。

有人可以帮我解释为什么(2)比(1)慢吗?

有关这两种方法的代码段,请参阅以下内容。

        // preconditioner
        Eigen::VectorXd p = (H_.diagonal().array() > 1.0e-9).select(H_.diagonal().cwiseSqrt(),1.0e-3);
        Eigen::VectorXd p_inv = p.cwiseInverse();

        // scale H and b
        H_ = p_inv.asDiagonal() * H_ * p_inv.asDiagonal();
        b0_ = p_inv.asDiagonal() * b0_;

        Eigen::MatrixXd U = H_.topLeftCorner(H_.rows() - marginalizationParametersLandmarks,
                                             H_.rows() - marginalizationParametersLandmarks);
        Eigen::MatrixXd V = H_.bottomRightCorner(marginalizationParametersLandmarks,
                                                 marginalizationParametersLandmarks);
        Eigen::MatrixXd W = H_.topRightCorner(H_.rows() - marginalizationParametersLandmarks,
                                              marginalizationParametersLandmarks);
        Eigen::VectorXd b_a = b0_.head(H_.rows() - marginalizationParametersLandmarks);
        Eigen::VectorXd b_b = b0_.tail(marginalizationParametersLandmarks);

        // split preconditioner
        Eigen::VectorXd p_a = p.head(H_.rows() - marginalizationParametersLandmarks);

        // invert the marginalization block
        static const int sdim = SIZE_PARAMETERIZATION_SIZE_FEATURE;
        b0_.resize(b_a.rows());
        b0_ = b_a;
        H_.resize(U.rows(), U.cols());
        H_ = U;

// surprisingly, the below operation is more efficient than its alternative on both
// an intel atom processor and on an intel core i7 PC
#if defined(ANDROID) || defined(__ANDROID__)
        Eigen::VectorXd V_inv(V.cols());
        for (int jack = 0; jack < V.cols(); ++jack) {
            V_inv[jack] = 1.0/V(jack, jack);
        }
        H_ -= W*V_inv.asDiagonal()*W.transpose();
        b0_ -= W*V_inv.asDiagonal()*b_b;
#else
        // assume V off-diagonal elements are all zeros
        Eigen::MatrixXd delta_H(U.rows(), U.cols());
        Eigen::VectorXd delta_b(b_a.rows());
        delta_H.setZero();
        delta_b.setZero();
        for (int i = 0; i < V.cols(); ++i) {
            double V_inv = 1.0 / V(i, i);
            double V_inv_sqrt = std::sqrt(V_inv);
            Eigen::VectorXd M = W.block(0, i, W.rows(), 1) * V_inv_sqrt;
            Eigen::VectorXd M1 = W.block(0, i, W.rows(), 1) * V_inv;
            delta_H += M * M.transpose();
            delta_b += M1 * b_b[i];
        }
        b0_ -= delta_b;
        H_ -= delta_H;
#endif
        // unscale
        H_ = p_a.asDiagonal() * H_ * p_a.asDiagonal();
        b0_ = p_a.asDiagonal() * b0_;

0 个答案:

没有答案