优化分配的模板技巧
Posted
技术标签:
【中文标题】优化分配的模板技巧【英文标题】:Template trick to optimize out allocations 【发布时间】:2010-12-24 16:51:46 【问题描述】:我有:
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] ... 而不是所有的临时变量。
谢谢!
【问题讨论】:
由于 operator+ 的参数是 const 引用,智能编译器可能会自动执行此操作。你检查了吗? 对于这种特殊情况,最简单的做法是重载operator+=
并改用它。
【参考方案1】:
是的,这正是 表达式模板(例如参见 http://www.drdobbs.com/184401627 或 http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Expression-template)的用途。
这个想法是让operator+
返回某种代理对象,代表要评估的表达式树。然后编写operator=
来获取这样一个表达式树并一次对其进行评估,避免创建临时变量,并应用任何其他可能适用的优化。
【讨论】:
正是我需要的。会再做生意。 A++++++++++++++++++++++++++++++【参考方案2】:看看Boost.Proto,它是一个用于直接用 C++ 编写 EDSL(嵌入式领域特定语言)的库。甚至还有一个 example 可以准确显示您的需求。
【讨论】:
【参考方案3】:http://codeidol.com/cpp/cpp-template-metaprogramming/Domain-Specific-Embedded-Languages/-10.5.-Blitz-and-Expression-Templates/
如果我们必须将 Blitz++ 解决的问题归结为一句话,我们会说:“简单的数组数学实现对于任何有趣的计算都非常低效。”要明白我们的意思,请采取无聊的陈述
x = a + b + c;
这里的问题是上面的 operator+ 签名太贪心了:它试图尽快评估 a + b,而不是等到整个表达式(包括 c 的加法)可用。
在表达式的解析树中,求值从叶子开始,向上进行到根。这里需要一些延迟评估的方法,直到库拥有表达式的所有部分:也就是说,直到执行赋值运算符。 Blitz++ 采取的策略是为整个表达式构建编译器解析树的副本,使其能够自上而下管理评估
这不可能是任何普通的解析树,但是:由于数组表达式可能涉及乘法等其他操作,这需要它们自己的评估策略,并且由于表达式可以任意大和嵌套,因此使用节点和指针构建的解析树必须在运行时由 Blitz++ 评估引擎遍历以发现其结构,从而限制性能。此外,Blitz++ 必须使用某种运行时调度来处理操作类型的不同组合,这再次限制了性能。
相反,Blitz++ 使用表达式模板构建编译时解析树。简而言之,它的工作原理如下:操作符不是返回一个新计算的数组,而是将对其参数的引用打包到一个表达式实例中,并用操作标记:
// 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);
请注意,当我们编写 a + b 时,我们仍然拥有进行计算所需的所有信息,它被编码在表达式类型中,并且可以通过表达式的存储引用访问数据。当我们写 a + b + c 时,我们得到一个类型的结果:
Expression<Expression<Array,plus,Array>,plus,Array>
【讨论】:
一些描述以及链接会很有帮助。 :) 你需要什么样的描述? 我?没有任何。但是阅读这个问题并略读答案的人可能不想为了获得答案的概述而必须单击每个答案的链接。如果有很多答案,很高兴能够大致了解每个答案所涵盖的内容,而无需访问外部页面。如果人们在一两年后阅读此答案,当您链接到的页面被更改甚至被删除时,它也会有所帮助。最后,它给了我们一个理由来支持你的答案,当它实际上读起来像一个答案时,而不是简单地指示去哪里找到一个答案。 在链接中发布简短摘要只是一种礼仪,而不是仅提供链接的答案。 :) 我认为你是对的。我把同样的事情联系起来,你得了 10 分,而我一无所获。所以我从里面复制了一些句子。以上是关于优化分配的模板技巧的主要内容,如果未能解决你的问题,请参考以下文章