优化分配的模板技巧

时间:2010-12-24 16:51:46

标签: c++

我有:

struct DoubleVec {
  std::vector<double> data;
};

DoubleVec operator+(const DoubleVec& lhs, const DoubleVec& rhs) {
  DoubleVec ans(lhs.size());
  for(int i = 0; i < lhs.size(); ++i) {
    ans[i] = lhs[i]] + rhs[i]; // assume lhs.size() == rhs.size()
  }
  return ans;
}

DoubleVec someFunc(DoubleVec a, DoubleVec b, DoubleVec c, DoubleVec d) {
  DoubleVec ans = a + b + c + d;
}

现在,在上面,“a + b + c + d”将导致创建3个临时DoubleVec - 是否有一种方法可以通过某种类型的模板魔法来优化它...即优化它等同于:

DoubleVec ans(a.size());
for(int i = 0; i < ans.size(); i++) ans[i] = a[i] + b[i] + c[i] + d[i];

您可以假设所有DoubleVec都具有相同的元素数。

高层次的想法是在“+”上做一些模板魔术,“延迟计算”直到=,此时它会自我调查,嗯...我只是添加了数字,合成a [i] + b [i] + c [i] + d [i] ...而不是所有的临时数。

谢谢!

3 个答案:

答案 0 :(得分:14)

是的,这正是表达模板(例如,请参阅http://www.drdobbs.com/184401627http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Expression-template)。

这个想法是让operator+返回某种代表对象,代表要评估的表达式树。然后编写operator=来获取这样的表达式树并立即对其进行全面评估,避免创建临时值,并应用可能适用的任何其他优化。

答案 1 :(得分:2)

查看Boost.Proto,它是一个直接在C ++中编写EDSL(嵌入式域特定语言)的库。甚至还有example显示您需要的内容。

答案 2 :(得分:1)

http://codeidol.com/cpp/cpp-template-metaprogramming/Domain-Specific-Embedded-Languages/-10.5.-Blitz-and-Expression-Templates/

如果我们不得不将Blitz ++解决的问题归结为单句,我们会说,“对于任何有趣的计算,数组数学的天真实现都是非常低效的。”要明白我们的意思,请采取无聊的陈述

x = a + b + c;

这里的问题是上面的操作符+签名太贪婪了:它试图尽快评估a + b,而不是等到整个表达式,包括添加c,都可用。

在表达式的解析树中,评估从叶子开始,然后向上进行到根。这里需要的是一种延迟评估的方法,直到库具有所有表达式的部分:即,直到赋值运算符被执行。 Blitz ++采取的策略是为整个表达式构建编译器解析树的副本,允许它从上到下管理评估

这不能是任何普通的解析树:因为数组表达式可能涉及其他操作,如乘法,这需要自己的评估策略,并且因为表达式可以任意大和嵌套,所以使用节点和指针构建的解析树必须由Blitz ++评估引擎在运行时遍历以发现其结构,从而限制性能。此外,Blitz ++必须使用某种运行时调度来处理不同的操作类型组合,这再次限制了性能。

相反,Blitz ++使用表达式模板构建编译时解析树。以下是它的工作原理:运算符只是在Expression实例中打包对其参数的引用,而不是返回一个新计算的数组,标记为操作:

// operation tags
struct plus; struct minus;

// expression tree node
template <class L, class OpTag, class R>
struct Expression
{
    Expression(L const& l, R const& r)
      : l(l), r(r) {}

    float operator[](unsigned index) const;

    L const& l;
    R const& r;
};

// addition operator
template <class L, class R>
Expression<L,plus,R> operator+(L const& l, R const& r)
{
    return Expression<L,plus,R>(l, r);
}

请注意,当我们编写一个+ b时,我们仍然拥有在Expression类型中进行编码所需的所有信息,并且可以通过表达式的存储引用访问数据。当我们写一个+ b + c时,我们得到一个类型的结果:

Expression<Expression<Array,plus,Array>,plus,Array>