我不是这些优化方面的专家,但据我了解,您所说的延迟评估技术通过在矩阵类型上定义算术运算符来工作,例如 A+B+C*D 不返回矩阵,它返回一个可以转换为矩阵的代理对象。当它被分配给M 时会发生这种情况,并且转换代码将通过库设计者可以想出的最有效的方式计算结果矩阵的每个单元格,避免临时矩阵对象。
那么,假设程序包含M = A + B + C * D;
如果您除了使用operator+= 以通常的方式实现operator+ 之外没有做任何聪明的事情,那么您会在正常情况下得到类似的东西,C++03 风格的复制省略已经开始了:
Matrix tmp1 = C;
tmp1 *= D;
Matrix tmp2 = A;
tmp2 += B;
tmp2 += tmp1;
M = tmp2;
使用延迟评估,您可能会得到类似的结果:
for (int i = 0; i < M.rows; ++i) {
for (int j = 0; j < M.cols; ++j) {
/* not necessarily the best matrix multiplication, but serves to illustrate */
c_times_d = 0;
for (int k = 0; k < C.cols; ++k) {
c_times_d += C[i][k] * D[k][j];
}
M[i][j] = A[i][j] + B[i][j] + c_times_d;
}
}
而“没什么聪明”的代码会做几个单独的加法循环和更多的赋值。
据我所知,在这种情况下,移动语义并没有多大帮助。你所写的内容中没有任何内容允许我们从 A、B、C 或 D 转移,所以我们最终会得到相当于:
Matrix tmp1 = C;
tmp1 *= D;
Matrix tmp2 = A;
tmp2 += B;
tmp2 += std::move(tmp1);
M = std::move(tmp2);
所以移动语义除了最后一点之外没有任何帮助,也许右值版本的操作符比普通的更好。如果你写了std::move(A) + std::move(B) + std::move(C) * std::move(D),还有更多可用的,因为我们不必从C 或A 复制,但我仍然认为结果不如延迟评估代码。
基本上,移动语义对延迟评估提供的优化的一些重要部分没有帮助:
1) 通过延迟评估,中间结果永远不需要实际作为完整矩阵存在。移动语义不会使编译器免于在某些时候在内存中创建完整的矩阵A+B。
2) 通过延迟评估,我们可以在计算完整个表达式之前开始修改M。移动语义无助于编译器重新排序修改:即使编译器足够聪明地发现潜在机会,如果存在任何危险,对非临时变量的修改必须保持正确的顺序抛出异常,因为如果 A + B + C * D 的任何部分抛出,那么 M 必须保持原样。