Debug版FlashPlayerの関数呼び出しの重さについて

11 月 12, 2009 on 10:31 pm | In Flash.AS |

かねてから私は考えていました。
AS3の関数呼び出しは、重いのではないでしょうか?
AS3は気軽にgetterとかsetterの関数を使っていいような言語なのでしょうか?

というわけで、検証用にwonderflってみたのがこちらです。

関数呼び出しは重く、入れ子の関数は更に重い | wonderfl build flash online
http://wonderfl.net/code/616f81c36e13d5611f7f60849a0cc27205829584

確かに関数の呼び出しは重い気がします。
手元の環境では関数を呼び出すだけでループ速度が数分の1以下になりました。
Adobeはさっさとinline関数を実現してください。

と思った。
・・・手元の環境?
微妙な怪しさを判明させるブログ記事に当たったのはちょうどその頃でした。

Debug 版 Flash Player の罠【閃光的網站・弛緩複合体 -Review Division-】
http://aquioux.blog48.fc2.com/blog-entry-650.html

どうやら、関数呼び出しによってパフォーマンスが大幅に低下するのはDebug版のFlashPlayerのみで、Debug版じゃないFlashPlayerではgetter/setter関数を定義しても有意なパフォーマンス低下は起こらないということらしいです。
しかし上記のブログ記事では具体的なデータを確認する事ができませんでした。
そこで、実験してみました。

実験環境

  • 20inchで2.66GHzくらいのIntel iMac
  • Mac OS X 10.5.8くらい
  • メモリ4GB
  • Adobeのサイトから取って来た最新のプラグイン版Flash Player(10.0.32.18)
  • 何回か『再計算』を連打して安定した値を採る

まずは非Debug版の結果がこちら。


各テスト 200000 回処理させた計算結果 [単位 : ミリ秒]
(誤差は多少生じます)
 
[ ループのみ ] –> 0 ms
[ 文字列を捨てるループ ] –> 1 ms
[ 文字列を代入するループ ] –> 1 ms
[ XMLリテラルを捨てるループ ] –> 1 ms
[ XMLリテラルを代入するループ ] –> 429 ms
[ 暗黙のメンバ関数呼び出し ] –> 2 ms
[ this付きのメンバ関数呼び出し ] –> 2 ms
[ apply()経由でthis付きのメンバ関数呼び出し ] –> 2 ms
[ apply()経由で直接this付きのメンバ関数呼び出し ] –> 20 ms
[ 変数に代入したメンバ関数呼び出し ] –> 15 ms
[ 匿名関数呼び出し ] –> 15 ms
[ 文字列リテラルを捨てる匿名関数呼び出し ] –> 16 ms
[ 文字列リテラルを返す匿名関数呼び出し ] –> 18 ms
[ XMLリテラルを捨てる匿名関数呼び出し ] –> 15 ms
[ XMLリテラルを返す匿名関数呼び出し ] –> 335 ms
[ 入れ子の匿名関数を捨てる呼び出し ] –> 252 ms
[ 入れ子の匿名関数を呼び出す匿名関数呼び出し ] –> 270 ms
 
結果を説明するスペースなんてなかった

続いて以下がDebug版の結果。


各テスト 200000 回処理させた計算結果 [単位 : ミリ秒]
(誤差は多少生じます)
 
[ ループのみ ] –> 1 ms
[ 文字列を捨てるループ ] –> 1 ms
[ 文字列を代入するループ ] –> 2 ms
[ XMLリテラルを捨てるループ ] –> 1 ms
[ XMLリテラルを代入するループ ] –> 349 ms
[ 暗黙のメンバ関数呼び出し ] –> 16 ms
[ this付きのメンバ関数呼び出し ] –> 16 ms
[ apply()経由でthis付きのメンバ関数呼び出し ] –> 14 ms
[ apply()経由で直接this付きのメンバ関数呼び出し ] –> 35 ms
[ 変数に代入したメンバ関数呼び出し ] –> 26 ms
[ 匿名関数呼び出し ] –> 29 ms
[ 文字列リテラルを捨てる匿名関数呼び出し ] –> 29 ms
[ 文字列リテラルを返す匿名関数呼び出し ] –> 31 ms
[ XMLリテラルを捨てる匿名関数呼び出し ] –> 27 ms
[ XMLリテラルを返す匿名関数呼び出し ] –> 398 ms
[ 入れ子の匿名関数を捨てる呼び出し ] –> 360 ms
[ 入れ子の匿名関数を呼び出す匿名関数呼び出し ] –> 491 ms
 
結果を説明するスペースなんてなかった

表にまとめると、このような結果になりました。

テスト項目 非Debug Debug
ループのみ 0 ms 1 ms
文字列を捨てるループ 1 ms 1 ms
文字列を代入するループ 1 ms 2 ms
XMLリテラルを捨てるループ 1 ms 1 ms
XMLリテラルを代入するループ 429 ms 349 ms
暗黙のメンバ関数呼び出し 2 ms 16 ms
this付きのメンバ関数呼び出し 2 ms 16 ms
apply()経由でthis付きのメンバ関数呼び出し 2 ms 14 ms
apply()経由で直接this付きのメンバ関数呼び出し 20 ms 35 ms
変数に代入したメンバ関数呼び出し 15 ms 26 ms
匿名関数呼び出し 15 ms 29 ms
文字列リテラルを捨てる匿名関数呼び出し 16 ms 29 ms
文字列リテラルを返す匿名関数呼び出し 18 ms 31 ms
XMLリテラルを捨てる匿名関数呼び出し 15 ms 27 ms
XMLリテラルを返す匿名関数呼び出し 335 ms 398 ms
入れ子の匿名関数を捨てる呼び出し 252 ms 360 ms
入れ子の匿名関数を呼び出す匿名関数呼び出し 270 ms 491 ms

まず、テスト項目『XMLリテラルを代入するループ』ですが、このテスト項目は試行ごとに結果のばらつきが大きかったです。今回は非DebugプレイヤーがDebugプレイヤーより遅いという結果が出ましたが、何回か試行すると非Debugプレイヤーでも速い数値が出たりDebugプレイヤーでも遅い数値が出たりしたので、この差は有意ではないと考えることにします。これはあくまで予想ですが、GCが起きるか起きないかで速度が変わってきているのではないでしょうか?

次に、その下3つのテスト項目、『暗黙のメンバ関数呼び出し』『this付きのメンバ関数呼び出し』『apply()経由でthis付きのメンバ関数呼び出し』です。明らかに、この3つには有意な差が出ています。いずれも、非Debugプレイヤーではわずかなコストにしかなっていませんが、Debugプレイヤーにするととたんに呼び出しのコストが跳ね上がっています。

それ以外の関数呼び出しに関わるテスト項目も、いずれもDebugプレイヤーの方が10ms〜14msほど遅いという結果になりました。なお、入れ子の匿名関数を記述すると、それを呼び出すかどうかに関係なく非常に遅くなるという事は非Debugプレイヤー、Debugプレイヤーと共に真実であるようです。そのため、速度が求められる場面では関数の中で匿名関数を定義しないほうがよいでしょう。

まとめると、非Debugプレイヤーでは関数呼び出しのコストは大した事はないが、Debugプレイヤーにすると関数呼び出しが飛躍的に遅くなるという結論になりました。なぜ、Debugプレイヤーにするだけで関数呼び出しがここまで遅くなるのでしょうか?Debugプレイヤーは関数呼び出しのたびに何か余計な事をしているのでしょうか?そもそも、Debugプレイヤーに特有の機能って何かあったっけ・・・

あ、Error.getStackTrace()・・・

(このオチは全くの憶測であり、検証は一切行っておりません。あらかじめご了承ください。)

No Comments yet »

このコメント欄の RSS フィード TrackBack URI

コメントをどうぞ

XHTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Powered by WordPress with Pool theme design by Borja Fernandez.
Entries and comments feeds. Valid XHTML and CSS. ^Top^