VBAHaskellの紹介 その9 (明示的なループの性能がいまいち)
ここでの「明示的なループ」とはVBAコード中のループのことで、VBAHaskellの関数適用関数である applyFun 等を繰り返し呼び出したときの性能が良くない。mapFやfoldlなどのリスト処理関数でもループ処理はしているが、それはdllの中で行われているのでこの話とは関係ない。
原因は applyFun の毎回の呼び出しの中で、VBA配列の中にネストされている関数をC++側の構造に展開するのに最低でも2回は new が走るためである。
典型的に現われるのが、繰り返し処理そのものと言える repeat_while 関数で、サンプルの中では、以下のように円周率を確率的に求めている。
4 * repeat_while (0, _
p_equal(0, 0), _
p_plus(p_less(p_distance( _
p_makePair(p_rnd(0, 1), p_rnd(0, 1)), _
Array(0, 0)), 1.0)), _
N) _
/ N
repeat_while を使って以下のことをやっている。
- 「区間 [0,1] の一様変数のペアを作り、原点からの距離が1.0未満であれば1を、そうでなければ0を加える」関数をファンクタとして作り、
- 0を初期値として、述語 p_equal(0, 0) が満たされている間、
- 最大N回繰り返す
- 結果を4倍してNで割る
述語 p_equal(0, 0) は要するに 0 = 0 なので恒真式となり、とにかくN回繰り返すわけだが、これが遅い。Core i5マシンでN=10000だと250msくらいかかるのだ。ファンクタは比較的複雑だし、述語の分もある。
lower_bound や upper_bound の中でも同様のことが起きているはずだが、述語の呼出し回数はデータ長の対数比例なので問題ないだろう。
これを改善する方法はいくつかあるはずだが、思いついたものはどれも冴えない感じなので止まっている。