在执行添加时如何使用 c++ 模板避免临时对象

How temporary objects get avoided by using c++ templates when doing additions?

本文关键字:临时对象 c++ 何使用 执行 添加      更新时间:2023-10-16

我正在阅读Tomas Arce的一篇文章。本文讨论了如何使用模板提高矢量添加性能。但有些部分我跟不上。

笔者说下面的代码可以避免在评估时生成临时对象 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,并且在计算运算符时再次完成添加=

把这篇文章拿起底特律大小的盐。它已经有 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);

记住,如果你把这样的东西放在其他工程师必须阅读的生产代码中,你最好有一个很好的理由。例如,您必须证明使用此胡言乱语可以提高性能。为什么?因为维持这种精神错乱的成本是不可忽视的。在使用类似内容之前,请获取性能数字。

技术被称为"表达式模板",它的发明者托德·维尔德惠森(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

整个概念依赖于存储对本机类型的引用,或计算折叠到本机类型的表达式。

在此过程中有临时对象参与,但它们存储的只是对源数据和目标数据的引用,这些数据在某个时候被解析。在没有优化的情况下进行编译确实可以分配空间,但如果启用了优化,优化程序会注意到没有理由这样做。

也就是说,如果这是为使用寄存器的 CPU 编译的。基于堆栈的机器的行为可能有所不同,因为操作仍然需要推送和弹出其操作数。