【问题标题】:Optimization of repeated identical computations重复相同计算的优化
【发布时间】:2014-07-24 00:13:58
【问题描述】:

考虑以下两个函数:

std::pair<double,Vector> myMatrixOperation1(Matrix const& A, Vector const& V) {
    Vector AV = A*V;
    double norm_A_V = std::sqrt(dot(V,AV));

    return make_pair(norm_A_V,AV);
}

std::pair<double,Vector> myMatrixOperation2(Matrix const& A, Vector const& V) {
    return make_pair( norm(V,A) , A*V );
}
double norm(Vector const& V, Matrix const& innerProductMatrix) {
    double norm_A_V = std::sqrt(dot(V,innerProductMatrix*V));
}

他们显然做同样的事情,除了矩阵向量乘积保证在第一个函数中只计算一次。

但是,第二个函数更具可读性,因为它被重构以便完全分离关注点:向量范数关于任意内积的概念被抽象为一个单独的函数。

现在的问题是,在没有任何优化的情况下,矩阵向量积现在被计算了两次。

我的问题如下:编译器是否足够聪明,只计算一次矩阵向量乘积?如果是,我需要做什么?

我想至少我需要内联 norm() 函数。 此外,关于 operator*(Matrix const& A, Vector const& V),是否有任何帮助的惰性求值? (旁注:我正在使用 Eigen 库)

注意:我知道一个类似的问题:Will the compiler optimize repeated math computations?。但是,请注意我的问题对编译器来说更难,因为 operator*(Matrix const& A, Vector const& V) 不是内置的,因此编译器应该需要一些关于它的保证

编辑:

在进一步思考了来自 Wikipedia (http://en.wikipedia.org/wiki/Optimizing_compiler) 的引用后:

例如,在某些语言中,函数不允许具有 副作用。因此,如果一个程序多次调用同一个 具有相同参数的函数,编译器可以立即推断 函数的结果只需要计算一次。在语言中 在允许函数产生副作用的情况下,另一种策略是 可能的。优化器可以确定哪个函数没有边 效果,并将此类优化限制为无副作用 功能。仅当优化器具有 访问被调用函数。

似乎编译器可以用第一个函数替换第二个函数,前提是 operator+ 是纯的(即:没有副作用)。根据https://stackoverflow.com/a/5464114/1583122,在 C++ 中,可以通过告诉编译器一个函数是 constexpr、只有 constexpr 函数调用和具有 const 参数来保证编译器的纯度。所以我认为,只要满足一些要求,编译器就有可能保证这样的优化。此外,请注意,在 C++14 中,对 constexpr 函数的限制已大大减少

【问题讨论】:

  • 实际上这两种情况都会计算两次乘积而无需进一步优化。 A*V(和dot())都产生一个表达式树对象。在您需要实际值之前,实际工作不会完成 - 转换为 double 以进行 sqrt 调用并转换为 Vector 以构造 std::pair&lt;double,Vector&gt; 返回值。
  • 你是对的。我从 auto 更改为 Vector 以明确要求完成计算
  • 回答这个问题的唯一可靠方法是通过完全优化来衡量这两个函数的性能,但我很怀疑编译器是否能够优化这一点。作为一个纯粹的轶事示例,我最近处理的一些代码在std::vector 上调用std::accumulate,然后再次调用在同一个std::vector 上调用std::accumulate 的函数,我看到当我改为传递accumulate 的结果。
  • 我知道性能测量会告诉编译器实际做了什么,但不知道为什么,也没有任何方式告诉编译器该做什么。我也很怀疑,但关于你的例子,我想知道用 std::valarray 而不是 std::vector 会发生什么

标签: c++ c++11 optimization matrix


【解决方案1】:

如果此代码中的速度优化很重要,我根本不会依赖编译器行为。

即使一个聪明的编译器发现了这个技巧(我在这里怀疑这会涉及到相当多的语义洞察力 - 使函数内联可能会提示编译器),你也不能保证另一个编译器会看到它。甚至是相同的未来版本!

经常出现这种数值算法依赖于中间结果的重用。如果您清楚地评论您保留结果以备后用,我认为没有可读性问题。代码紧凑并不总是意味着代码可读性。

【讨论】:

    猜你喜欢
    • 2013-03-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多