Expression Templateを試してみる

Expression Template (ET)とは数式の式木をC++のテンプレートによって表現し、実際の値をあとから計算(遅延評価)することによって、途中のメモリ生成のコストを避けてパフォーマンスを向上する仕組みだ。とくに行列演算においては途中計算でメモリを大量に確保するのはコストがかかるのでこの方法が有効だとされている。

参考になるページ

ちょっと前に自前実装して試したことがあったのだが、あまりパフォーマンスが変わらなかったので使わずにいた。
ふと思い立って、もう一度実装してみて調べてみることにした。

実装したコードはこれ

稚拙な実装でごめんなさい。かなり危険なコード(一時オブジェクトのconst参照をしている)なので、コンパイラを選ぶようだ。Macの場合、MacPortsのgcc44でないと正常に計算できなかった。同じ動作内容で安全なコードにするにはどうすればいいのか、どなたか教えて…

パラメータとして、

  • 要素の型 (今回はdoubleで固定)
  • 素数
  • ExpressionTemplateを有効化するか
  • 配列を動的確保するか静的確保するか

を指定できる。

計算しているのは調和振動子の時間発展。性能は一秒間に何回更新できたかで表せる。生の数値だと大きすぎるのでMegaの単位でmupsと表す。

エネルギーが1から大きく離れず、かつp, qが常に変化していれば正常に計算されている証拠。

計算に使用したコンピュータのCPUはIntel Xeon W3680 3.33GHz
コンパイラはg++-mp-4.4を使用する。

最適化の影響も調べるために、要素数3、静的確保で固定して、最適化オプションを変えた結果をとってみる。最適化無効化の場合はなぜかET有のコードが正常に計算できなかったので1,2,3のみ。

最適化 ET無効 ET有効
1 88 155
2 223 155
3 223 223

この結果は解釈に困るけれど、コンパイラの最適化は何してるか詳しくないからなあ…最大最適化すればどっこいどっこいになってしまうようだ。


そこで、最大最適化(-O3)のもとで、ETの有無や、メモリ確保を変えて結果をとってみる。

素数 ET無効、静的 ET有効、静的 ET無効、動的 ET有効、動的
3 223 223 2.7 119
4 223 223 2.6 119
8 79 155 2.4 64
16 35 52 2.5 26
32 19 60 1.6 18

次元が小さいときは、静的確保の場合、ETの有無で性能の差が出ないが、動的確保の場合は顕著に差があらわれる。
次元が大きくなると、静的確保の場合でもETの有無による性能の差が現れてくる。
動的確保の方が静的確保より性能がかなり低く出ているのはstd::vectorのインデックスアクセスをそのまま呼び出しているからだろうか? いったんポインタに変換してからの方が良いのだろうか…
素数が表の範囲であれば静的確保で大丈夫だが、もっと大きくなると静的確保できなくなるので動的確保は不要というわけではないことに注意。

やはり、ExpressionTemplateが生まれた背景のとおりメモリを確保するのは足かせになりやすいが、要素数が少なければそのオーバーヘッドは静的確保ならば問題ない程度に最適化されるということのようだ。