在进行添加时如何通过使用 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),对轻量级对象的引用(其中包含对vBvC 的两个先前引用)并且可以创建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 时,如何避免模板在渲染列表时将分隔符添加到分隔符?

当 Fortran 生成大型内部临时数组时,如何避免堆栈溢出?

C++ 避免将 NULL 与 0 进行比较

避免 iOS 中的代码临时性

具有临时消除功能的 C++ OpenCL 矩阵库

模板表达式如何摆脱临时变量