D中的表达式模板

D中的表达式模板,d,template-meta-programming,D,Template Meta Programming,目标是实现与中相同的效果:避免创建临时对象。我尝试过把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("

目标是实现与中相同的效果:避免创建临时对象。我尝试过把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.");
}
导入标准日期时间:基准;
导入标准stdio:writefln,writeln;
无效工作台(别名fun,字符串时间=“毫秒”)(字符串消息,uint n=1_000_000){
自动b=基准!乐趣(n);
writefln(“%s:%s ms”,msg,b[0]。到!(时间,int));
}
别名双实;
结构表达式(字符串op、E1、E2){
E1 v1;
E2-v2;
别名v1;
别名_v2;
自动opIndex(尺寸i){
返回mixin(“v1[i]”~op~“v2[i]”);
}
自动opBinary(字符串op,E)(自动引用E){
返回表达式!(op,typeof(this),E)(this,E);
}
}
结构扩展向量{
实际[40]_数据=无效;
别名_此数据;
这(真实数据)是纯粹的nothrow{u data=datum;}
自动opBinary(字符串op,T)(自动引用T其他){
返回表达式!(op,typeof(this),T)(this,other);
}
无效opAssign(E)(自动参考E exp){
foreach(i,参考基准;_数据)
基准=exp[i];
}
}
结构向量{
实际[40]_数据=无效;
别名_此数据;
这(真实数据)是纯粹的nothrow{u data=datum;}
自动操作二进制(字符串操作)(自动参考向量其他){
向量ret;
foreach(i,基准;_数据)
ret[i]=mixin(“数据”~op~“其他[i]”);
返回ret;
}
}
void main(){
ExpVector e1=ExpVector(1.5);
ExpVector e2=ExpVector(7.3);
ExpVector-ef;
void T1(){
ef=(e1+e2)*(e1+e2);
}
bench!T1(“使用模板表达式的向量操作”);
向量v1=向量(1.5);
向量v2=向量(7.3);
向量vf;
void T2(){
vf=(v1+v2)*(v1+v2);
}
bench!T2(“正则向量运算”);
void T3(){
对于(int i=0;i

表达式模板版本比非表达式模板版本慢。我希望表达式模板的版本更快,更接近预期。那么,我的表达式模板有什么问题?什么是正确的方式做表达式模板在D?< /P> < P>在维基百科的C++示例类中保存对临时的引用,并且在表达式的情况下…

Vec a,b;
双c;
向量计算结果=(a-b)*c
…在分配给结果向量之前,我们在内存中有一些树:
ab
\        /
\      /
\    /
矢量差
\
\
\
VecScaled(将“c”的副本直接嵌入到对象中)
VECKOLL只保留对A和B的引用,VCSCSCAP持有引用临时VECCORE(根据C++规则,直到表达式结束)才存活。最后,我们没有初始数据的重复,也没有不必要的计算(如果我们只使用向量的一些组成部分,而不是像简单地分配给向量那样全部使用)
在结构表达式(字符串op、E1、E2)中,初始数据和中间数据仅复制到数据成员,在上一个表达式的末尾,您将看到

表情!(“*”,表达式!(“-”,向量,向量),双精度)
其中,外部表达式将具有内部表达式的副本(“-”,Vec,Vec)和双参数,而内部表达式将具有a和b向量的副本。因此,在一天结束时,您避免了临时Vec,而是复制了4份a和b(不包括结果向量的最终赋值)。 尽管如此,我不知道在这种情况下如何在D中避免复制。可能是指向结构的指针?

(顺便说一句,c++11的自动类型推断和表达式模板引用了临时变量,但它们之间存在不幸的交互,这可能会导致错误:)

好吧,甚至不考虑代码的作用,我会指出,你需要编译一个编译器,它与C++编译器共享一个后端,以便能够真正地比较语言而不是编译器实现。如果你使用C++的GDC来做C++的东西,你会得到更好的语言本身的比较。dmd代码可能会慢一些,因为dmd的后端优化不如gcc。可能还有更多,但是比较dmd和gcc可能只是比较两个编译器的优化器。我想你误解了我说的。我发布的代码的速度是没有表达式模板的版本的两倍多,也就是说,只有一个常规的
Vector
结构实现了
opBinary
成员函数,两个版本都使用DMD编译。我不把它与维基百科中的C++代码进行比较,然后你应该发布两个版本。当你只有两个中的一个时,很难将某个东西与另一个东西进行比较。@JonathanMDavis我已经发布了完整的程序。你可以试试这个技巧,看看是否可以删除结构:我知道复制,但D不会让我把它们作为参考。我确实尝试过指针(我的印象是,指针会使我的代码变得不安全),它确实会产生很大的不同,但仍然不太一样。e、 例如,表达式模板版本需要
250ms
,而
150ms
是预期的。是的,指针使事情变得不安全。在我的答案的最后一页显示了这样的危险(关于C++,但是我认为我们可以用指针在D中使用表达式模板得到相同的结果) Vec a, b; double c; Vec result = ( a - b ) * c a b \ / \ / \ / VecDifference \ \ \ VecScaled( has copy of 'c' embedded directly in object )
Expression!( "*", Expression!( "-", Vec, Vec ), double )