我已经阅读了一些关于C ++ 11中移动语义的描述,我想知道它可以在什么上下文中使用。 目前,许多C ++数学库使用模板元编程来延迟评估。
如果M = A + B + C * D,其中M,A,B,C和D是矩阵,则模板元编程允许避免无用的副本。 移动语义是一种更方便的方式来做这些事情吗?
如果没有,在什么上下文中使用移动语义。 如果是,那么与模板元编程相比,这种用途的区别/限制是什么?
答案 0 :(得分:34)
我认为,对于您所谓的“模板元编程”更精确的术语是expression templates。
如果你的矩阵动态分配他们的数据,移动语义可以帮助将数据从对象传递到对象(包括来自/来自临时对象)在表达式中生成,例如:
M = A + B + C*D
另一方面,表达模板将完全消除临时性。
如果您的矩阵没有动态分配数据(例如,如果它们的大小固定且很小),那么移动语义将无助于您的性能。
将表达式模板应用于矩阵库将获得最高性能。这也是一种非常困难的实现技术。移动语义很多更容易实现,并且除了表达式模板之外还可以完成(如果有可以传输的内存等资源)。
总结:
移动语义不会消除临时性,但会在临时数据库中传输动态分配的内存,而不是重新分配它。
表达式模板消除了临时性。
答案 1 :(得分:13)
我不是这些优化方面的专家,但正如我所理解的那样,您所讨论的延迟评估技术是通过在矩阵类型上定义算术运算符来实现的,例如A+B+C*D
不会返回矩阵,它返回一个可以转换到矩阵的代理对象。当它被分配给M
时会发生这种情况,转换代码将通过库设计者可以提出的最有效的方式计算结果矩阵的每个单元格,从而避免使用临时矩阵对象。
因此,假设程序包含M = A + B + C * D;
如果除了使用operator+
以通常的方式实现operator+=
之外你没有做任何其他聪明的事情,那么一旦正常,你会得到类似的东西,C ++ 03风格的复制省略已经开始:< / p>
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
移动,因此我们最终会得到相应的: / p>
Matrix tmp1 = C;
tmp1 *= D;
Matrix tmp2 = A;
tmp2 += B;
tmp2 += std::move(tmp1);
M = std::move(tmp2);
所以移动语义对最后一点以外的任何东西都没有帮助,其中运算符的rvalue版本可能比常规版本更好。如果你写std::move(A) + std::move(B) + std::move(C) * std::move(D)
,可以使用更多,因为我们不必从C
或A
复制,但我仍然认为结果不如延迟评估好代码。
基本上,移动语义对延迟评估提供的优化的一些重要部分没有帮助:
1)延迟评估,中间结果永远不需要实际存在为完整的矩阵。移动语义不会保存编译器在某些时候在内存中创建完整的矩阵A+B
。
2)延迟评估,我们可以在计算完整个表达式之前开始修改M
。移动语义不能帮助编译器重新排序修改:即使编译器足够聪明以发现潜在的机会,如果存在任何危险,对非临时的的修改必须保持正确的顺序抛出异常,因为如果A + B + C * D
的任何部分抛出,则M
必须在开始时保留。
答案 2 :(得分:2)
他们是两种不同的野兽。移动语义是从一个将要被破坏的值中占用资源。当与大in(需要动态内存分配)的表达模板混合时,人们只需要适当的内存而不是制作即将被销毁的东西的副本。
移动语义对于本质上不是可复制(如fstreams)的对象也很重要,但是使可移动是有意义的。
答案 3 :(得分:0)
移动语义适用于对象内管理的资源,用于避免在创建临时对象时不必要地获取/释放资源(例如,动态分配的内存是资源)。
模板元编程对结构进行操作,这些结构在堆栈上分配(因为它需要对操作数进行编译时评估)。您可以使用它来避免运行时计算可以在编译时计算的操作
答案 4 :(得分:0)
移动语义是动态的,表达式模板不是动态的。你不能表达模板一个分布在几个语句中的表达式,其中一些表达式仅在月亮是蓝色的情况下进行评估,而移动语义则可以。