5

目標は、この C++ の例と同じ効果を達成することです: 一時的な作成を回避します。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で式テンプレートを行う正しい方法は何ですか?

4

1 に答える 1

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 への単純な割り当てのようにすべてではありません)
。構造体 Expression(string op, E1, E2) initial中間データは単にデータメンバーにコピーされ、前の式の最後に
Expression!( "*", Expression!( "-", Vec, Vec ), double )
、外側の Expressionに内側の Expression( "-", Vec, Vec ) のコピーと double パラメーターがあり、内側の Expression にコピーがある場所があります。a および b ベクトルの。したがって、1 日の終わりに、一時的な Vec を回避しましたが、a と b の 4 つのコピーを作成しました (結果ベクトルへの最終代入はカウントしません)。ただし、D でこの状況でコピーをうまく回避する方法がわかりません。 structres へのポインターでしょうか?
(ところで、一時変数への参照を含む c++11 の自動型推論と式テンプレートには、不幸な相互作用があり、バグにつながる可能性があります: http://lanzkron.wordpress.com/2011/02/21/inferring-too-much/ )

于 2012-04-13T16:13:26.393 に答える