D中的表达模板

时间:2012-04-12 22:41:20

标签: d template-meta-programming

目标是达到与this C++ example相同的效果:避免创造临时性。我试过将C ++示例翻译成D但没有成功。我也尝试了不同的方法。

import std.datetime : benchmark;
import std.stdio    : writefln, writeln;

void bench(alias fun, string time = "msecs")(string msg, uint n = 1_000_000) {
  auto b = benchmark!fun(n);
  writefln(" %s: %s ms", msg, b[0].to!(time, int));
}

alias double Real;

struct Expression(string op, E1, E2) {
  E1 _v1;
  E2 _v2;
  alias _v1 v1;
  alias _v2 v2;

  auto opIndex(size_t i) {
    return mixin("v1[i]" ~ op ~ "v2[i]");
  }

  auto opBinary(string op, E)(auto ref E e) {
    return Expression!(op, typeof(this), E)(this, e);
  }
}

struct ExpVector {

  Real[40] _data = void;
  alias _data this;

  this(Real datum) pure nothrow { _data = datum; }

  auto opBinary(string op, T)(auto ref T other) {
    return Expression!(op, typeof(this), T)(this, other);
  }

  void opAssign(E)(auto ref E exp) {
    foreach(i, ref datum; _data)
      datum = exp[i];
  }
}

struct Vector {

  Real[40] _data = void;
  alias _data this;

  this(Real datum) pure nothrow { _data = datum; }

  auto opBinary(string op)(auto ref Vector other) {
    Vector ret;
    foreach(i, datum; _data)
      ret[i] = mixin("datum" ~ op ~ "other[i]");
    return ret;
  }
}

void main() {

  ExpVector e1 = ExpVector(1.5);
  ExpVector e2 = ExpVector(7.3);
  ExpVector ef;
  void T1() {
    ef = (e1 + e2) * (e1 + e2);
  }
  bench!T1(" vector operations using template expression");

  Vector v1 = Vector(1.5);
  Vector v2 = Vector(7.3);
  Vector vf;
  void T2() {
    vf = (v1 + v2) * (v1 + v2);
  }
  bench!T2(" regular vector operations");

  void T3() {
    for(int i = 0; i < vf.length; ++i)
      vf[i] = (v1[i] + v2[i]) * (v1[i] + v2[i]);
  }
  bench!T3(" what is expected if template expressions worked and temporaries were not created.");
}

表达式模板版本比非表达式模板版本慢。我期待表达式模板版本更快,接近预期。那我的表达模板有什么问题?在D?

中进行表达模板的正确方法是什么?

1 个答案:

答案 0 :(得分:2)

在维基百科的c ++示例中,表达式类包含对临时数的引用,并且在表达式的情况下......

Vec a, b;
double c;
Vec result = ( a - b ) * c
...在分配给结果向量之前,我们在内存中有一些树:
a           b
 \        /
  \      /
   \    /
 VecDifference
      \
       \
        \
       VecScaled( has copy of 'c' embedded directly in object )
VecDifference仅将引用保存到a和b,而VecScaled将引用保存到临时VecDifference(根据c ++规则,它将一直存在,直到表达式结束)。最后,我们没有初始数据的重复,也没有不必要的计算(如果我们只使用向量的一些组件,但不是所有这些组件都像对Vec的简单分配那样)
在您的结构表达式(字符串op,E1,E2)中,初始和中间数据只是复制到数据成员,并且在前一个表达式的末尾,您将拥有
Expression!( "*", Expression!( "-", Vec, Vec ), double )
其中外表达式将具有内部表达式(“ - ”,Vec,Vec)的复制,并且双重参数和内部表达式将具有a和b向量的副本。因此,在一天结束时,您避免使用临时Vec但是制作了4份a和b(不计算结果向量的最终分配)。 虽然,不知道在D中这种情况下如何避免复制可能指向结构体? (BTW,c ++ 11的自动类型推理和表达模板以及对临时工具的引用有不幸的相互作用,这可能导致错误:http://lanzkron.wordpress.com/2011/02/21/inferring-too-much/