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 を使って以下のことをやっている。

  1. 「区間 [0,1] の一様変数のペアを作り、原点からの距離が1.0未満であれば1を、そうでなければ0を加える」関数をファンクタとして作り、
  2. 0を初期値として、述語 p_equal(0, 0) が満たされている間、
  3. 最大N回繰り返す
  4. 結果を4倍してNで割る

 述語 p_equal(0, 0) は要するに 0 = 0 なので恒真式となり、とにかくN回繰り返すわけだが、これが遅い。Core i5マシンでN=10000だと250msくらいかかるのだ。ファンクタは比較的複雑だし、述語の分もある。

lower_bound や upper_bound の中でも同様のことが起きているはずだが、述語の呼出し回数はデータ長の対数比例なので問題ないだろう。

これを改善する方法はいくつかあるはずだが、思いついたものはどれも冴えない感じなので止まっている。

 

VBAHaskellの紹介 その8 (ソート関連) - mmYYmmdd’s blog

github.com