这是我在 C++ 表达式模板编程中没有明确解决的一件事,以避免构建不必要的临时对象(通过创建仅在赋值运算符处折叠的“可内联模板对象”树)。假设为了说明我们正在对一维值序列进行建模,并按元素应用算术运算符,如 +、* 等。为完全创建的序列调用基本类 Seq(它包含一个固定长度的 double 列表,用于为了具体起见)并考虑以下说明性的伪 C++ 代码。
void f(Seq &a,Seq &b,Seq &c,Seq &d,Seq &e){
AType t=(a+2*b)/(a+b+c); // question is about what AType can be
Seq f=d*t;
Seq g=e*e*t;
//do something with f and g
}
在其他地方有 + 等的表达式模板化重载。对于定义 t 的行:
如果我将 AType 设为 Seq,我就可以实现此代码,但是当我不需要它时,我创建了这个完整的中间变量(除了它如何启用 f 和 g 的计算)。但至少只计算了一次。
我还可以实现此功能,使 AType 成为适当的模板化表达式类型,这样就不会在注释行创建完整的 Seq,而是在 f 和 g 中逐 block 使用。但是随后创建每个特定 block 所涉及的相同计算将在 f 和 g 中重复。 (我想在理论上,一个非常聪明的编译器可能会意识到相同的计算被执行了两次并进行了 CSE-it,但我认为没有人这样做,我也不想依赖一个总是能够发现机会的优化器。 )
我的理解是,没有巧妙的代码重写和/或使用模板来允许 t 的每个 block 只计算一次和 t 是分块计算而不是一次全部计算?
(我可以模糊地想象 AType 可能是某种对象,它既包含表达式模板类型,又包含在第一次计算后写入的缓存值,但这似乎无助于同步f 和 g 的赋值中有两个隐式循环。)
在谷歌搜索中,我遇到了一篇关于另一个主题的硕士论文,其中提到手动“公共(public)子表达式消除”应该避免使用表达式模板,但我想找到一个更权威的“这是不可能的”或者一个“这是怎么做的”。
最接近的 stackoverflow 问题是 Intermediate results using expression templates 这似乎是关于类型命名问题,而不是创建完整中间体的效率问题。
最佳答案
由于您显然不想对整个计算进行两次,因此您必须以某种方式对其进行缓存。缓存它的最简单方法似乎是让 AType 成为 Seq。你说 This 有一个完整的中间变量的缺点,
但这正是你在这种情况下想要的。那个完整的中间体就是你的缓存,不能轻易避免。
如果您分析代码并且这是一个瓶颈,那么我能想到的唯一更快的方法是编写一个特殊的函数来并行计算 f 和 g,但这太棒了-令人困惑,非常不推荐。
void g(Seq &d, Seq &e, Expr &t, Seq &f, Seq &g)
{
for(int i=0; i<d.size(); ++i) {
auto ti = t[i];
f[i] = d[i]*ti;
g[i] = e[i]*e[i]*ti;
}
}
void f(Seq &a,Seq &b,Seq &c,Seq &d,Seq &e)
{
Expr t = (a+2*b)/(a+b+c);
Seq f, g;
g(d, e, t, f, g);
//do something with f and g
}
关于c++ - 在 C++ 表达式模板编程中是否可以实现高效的 "repeatedly used intermediates"?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7260904/