在进行添加时如何通过使用 c++ 模板来避免临时对象? [关闭]
Posted
技术标签:
【中文标题】在进行添加时如何通过使用 c++ 模板来避免临时对象? [关闭]【英文标题】:How temporary objects get avoided by using c++ templates when doing additions? [closed] 【发布时间】:2014-12-09 23:32:28 【问题描述】:我正在阅读 Tomas Arce 的 article。 这篇文章讨论了通过使用模板来提高向量加法性能。但有些部分我无法理解。
作者说下面的代码可以避免求值时产生临时对象
vA = vB + vC + vD
,但是怎么做?我不明白如何避免临时对象。是否有人可以解释使用模板时如何避免临时对象的内部机制。
template< class ta_a >
class vecarg
const ta_a& Argv;
public:
inline vecarg( const ta_a& A ) : Argv( A )
inline const float Evaluate( const int i ) const
return Argv.Evaluate( i );
;
template<>
class vecarg< const float >
const ta_a& Argv;
public:
inline vecarg( const ta_a& A ) : Argv( A )
inline const float Evaluate( const int i ) const return Argv;
;
template<>
class vecarg< const int >
const ta_a& Argv;
public:
inline vecarg( const ta_a& A ) : Argv( A )
inline const float Evaluate( const int i ) const return (float)Argv;
;
template< class ta_a, class ta_b, class ta_eval >
class vecexp_2
const vecarg<ta_a> Arg1;
const vecarg<ta_b> Arg2;
public:
inline vecexp_2( const ta_a& A1, const ta_b& A2 )
: Arg1( A1 ), Arg2( A2 )
inline const float Evaluate ( const int I ) const
return ta_eval::Evaluate( i, Arg1, Arg2 );
;
// Listing 5
附:在@Severin Pappadeux 提供的第二个链接中,解释很容易理解。基本上,所有表达式模板的作用是:通过重载运算符+
,它甚至不执行加法,而是创建一个轻量级对象,其主要工作是保存对运算符+
两侧的两个操作数的两个引用,并且在评估运算符 =
时完成添加。通过第二次重载运算符+
(因为左侧是轻量级对象,右侧是vD
),对轻量级对象的引用(其中包含对vB
和vC
的两个先前引用)并且可以创建vD
,并且在评估运算符=
时再次完成添加。
【问题讨论】:
代码vA = vB + vC + vD
在哪里?
您能否将您的代码示例设为MCVE。实际上,您的code sample 不太合适。
这只是一篇大文章的一小部分(OP链接),没有MCVE
没有代码vA = vB + vC + vD
,作者只是解释。但在那篇文章的最后,有代码可以下载。
“没有 MCVE” 对于 Stack Overflow 问题,可以而且应该也必须构建。当那篇文章消失时,这个问题就变得毫无用处了。
【参考方案1】:
拿那篇文章和底特律一样大。它已经 14 岁了,从那时起优化器有了很大的改进。简而言之,这家伙正在使用模板制作一个复杂的方法网络,将 A = B + C + D 从:
// Taking liberties with pseudocode and notation for clarity. This is
// a very rough oversimplification
// Create a new, wasteful, vector to store a temporary result
vector tmp(C.x + D.x, C.y + D.y, C.z + D.z)
// Create another new, wasteful, vector to store a temporary result:
vector tmp2(B.x + tmp.x, B.y + tmp.y, B.z + tmp.z)
// Waste even more time copying the result.
A.x = tmp2.x; A.y = tmp2.y; A.z = tmp2.z;
// In reality, your optimized compiler isn't even remotely this stupid.
并且他正在使用模板预处理器来消除临时对象和最终副本的创建。他正试图达到执行以下操作的目的:
A.x = B.x + (C.x + D.x);
A.y = B.y + (C.y + D.y);
A.z = B.z + (C.z + D.z);
请记住,如果您将此类内容放入其他工程师必须阅读的生产代码中,那么您最好有一个很好的理由。例如,您将必须证明使用这种乱码会产生关键任务的性能改进。为什么?因为维持这种精神错乱的成本是不可忽略的。在您使用类似的东西之前,请先获取性能数据。
【讨论】:
正如@Severin 提到的,这类似于表达式模板。但是根据你的考虑,这个技术还在用吗? @user2345484 是的,它正在被使用。看看现代 C++ 线性代数犰狳,arma.sourceforge.net,它用于统计,在 R,...【参考方案2】:技术被称为“表达模板”,它的发明者 Todd Veldhuizen 有几篇论文, 以
开头http://ubietylab.net/ubigraph/content/Papers/pdf/ExpressionTemplates.pdf
和
http://www.drdobbs.com/cpp/expression-templates/184401656
最后是托德报告
http://www.cs.indiana.edu/pub/techreports/TR542.pdf
【讨论】:
第二个链接的解释很容易理解。我将把我的理解放在主要问题上。【参考方案3】:整个概念依赖于存储对原生类型的引用,或评估折叠为原生类型的表达式。
在此过程中有临时对象参与其中,但它们存储的只是对源数据和目标数据的引用,这些数据在某个时候得到解决。不进行优化的编译可能确实会分配空间,但是如果启用了优化,优化器会注意到没有理由这样做。
也就是说,如果这是为使用寄存器的 CPU 编译的。基于堆栈的机器可能会有不同的行为,因为操作仍然需要推送和弹出操作数。
【讨论】:
以上是关于在进行添加时如何通过使用 c++ 模板来避免临时对象? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章
使用 TailwindCSS 时,如何避免模板在渲染列表时将分隔符添加到分隔符?