(この記事ははてなブログ http://mmyymmdd.hatenablog.com/ の再掲です)
2015-04-23
ここでの「明示的なループ」とは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 の中でも同様のことが起きているはずだが、述語の呼出し回数はデータ長の対数比例なので問題ないだろう。
これを改善する方法はいくつかあるはずだが、思いついたものはどれも冴えない感じなので止まっている。