2007-06-21
■ [tips]ActionScript3 最適化・高速化Tips 簡易まとめ

優れたインタラクションを支えるのは優れた実行時パフォーマンスであり、快適な操作を支えるのは1msecでも処理時間を減らす努力である。
そんなわけでAS3の最適化に関するTipsを英語圏の記事からまとめてみた。
Daniel Hai ≫ Re-use your BitmapData objects, speed up your apps
フレーム毎にビットマップ処理が必要な場合、前もってビットマップデータを作成しておき、fillRect()/copyPixels()を使う
1) When doing per-frame bitmap operations, create and store a bitmap outside of the per-frame operation, and do a fillrect per-frame.
10000 operations of “BitmapData.draw()” 4068 ms 10000 operations of “new BitmapData()” : 2324 ms 10000 operations of “BitmapData.clone()” 1012 ms 10000 operations of “BitmapData.copyPixels()” 962 ms 10000 operations of “BitmapData.fillRect()” : 515 ms
BitmapData.draw()をBitmapData.fillRect()に変えると約790%の高速化になる。実際にはfillRect()だけでは十分でないのでcopyPixels()をメインに使うことになるだろう。それだけでも約420%の高速化だ。
Some ActionScript 3.0 Optimizations - Web Development Blog
forループで配列内走査をする場合は前もって配列の長さを所得しておき、イテレータ変数をint/uint型にする
Array indexing
AS3に限った話ではないが、ごくごく基本的な高速化処理。特に必要がない限り、この書き方にすべき。これだけで約147%の高速化。
余談だが、以前LightboxJSのソースを読んだときに、この処理をしていなくて驚いた。世界中で使われることを考えると、全体ではとんでもないムダをしていることになる。個人的にAjaxを信用していない理由のひとつだ。JavaScript開発者の実行時パフォーマンスに対する意識は正直もっと高くてよい。
他のクラスの定数は一旦ローカルの変数に値を代入してからループでつかう
Constants from other classes
約115%の高速化。
ローカル変数宣言は1行にまとめる
This can be speeded up if you use the "var" keyword one time and then declare the variables on a single line
Some ActionScript 3.0 Optimizations - Web Development Blog
Cなどでよく見かける書き方ではあるが、同時に注意が必要な書き方でもある。約135%の高速化が実現。
ビット演算を用いる
AS1/2では効果がなかったが、AS3では多くのプログラミング言語と同じく、ビット演算は高速である。
どれぐらい速いかは、後述。
andre michelle - gamedev | musicware - berlin is in germany ≫ Blog Archive ≫ AS3 optimations & suggestions
(AS2のように)条件文や命令文を書き換えない
Do not overload condition and statements as in AS2
andre michelle - gamedev | musicware - berlin is in germany ≫ Blog Archive ≫ AS3 optimations & suggestions
どうやらAS2では高速化されたようだが、AS3では素直に書いた方が速いらしい。
Object型は使わない
Do not use Objects, if you know, which properties will be finally involved.
できる限りクラスを定義する。同じ意味で*型もできるだけ避けるべき。
配列の要素を読むときは型のキャストをする
Cast instances, while reading from an Array
andre michelle - gamedev | musicware - berlin is in germany ≫ Blog Archive ≫ AS3 optimations & suggestions
たしかそのままだと*型と同じように処理されるはずなので、できるかぎりキャスト。
mxmlcで有効なメタデータタグ、[ArrayElementType("hogehoge")]が効果があるかどうかは不明。
できる限り、int型を使う
Try to use Integers (int) for all possible cases
andre michelle - gamedev | musicware - berlin is in germany ≫ Blog Archive ≫ AS3 optimations & suggestions
Number型よりもint/uint型の方がサイズが小さいので速くなる。
とはいえ、AS3は内部的にはNumber型で扱われるので、かならずしも速くなるわけではないことに注意。
ビット演算はとても早い
Bitoperators are lightning fast
andre michelle - gamedev | musicware - berlin is in germany ≫ Blog Archive ≫ AS3 optimations & suggestions
さらに後述。
Daniel Hai ≫ More performance tuning in Actionscript 3
Point型やRectangle型などよく使うオブジェクトの定数をつくって使いまわす
Create constants for commonly used objects, such as new Point(0,0), new Rectangle(0,0,320,240), etc. This reduces overhead of creating new objects.
Daniel Hai ≫ More performance tuning in Actionscript 3
AS3になって使う機会の増えたPoint型/Rectangle型を使いまわす。これで838%の高速化。
パッケージ変数・定数やパッケージ関数をつかってできる限りクラス名参照を減らす
Reduce pointers to static class names as much as possible, use package variables and functions instead.
Daniel Hai ≫ More performance tuning in Actionscript 3
ネイティブのMathクラスの関数などにも有効などの複雑な数学演算は大分効果が期待できる。
ファイルサイズ削減にも有効な場合が。
(.graphicsや.transformなどの)グローバルgetterプロパティを2回以上呼ぶ場合は、ローカル変数に代入して使う
Store getter properties as local variables when using them more than once in a method (such as .graphics, .transform)
Daniel Hai ≫ More performance tuning in Actionscript 3
これは書きやすさ、という意味でも効果的。一般的に実行時パフォーマンス最適化のTipsは開発パフォーマンスを下げるもの*1だが、これは珍しくどちらにも効果アリ。…というかAS3の設計ミスじゃないかと思ったり思わなかったり。必ず使うようにしたい。18%の高速化。
なお、.stageプロパティはパッケージ変数に入れておくと118%の高速化になるらしい。ひとつまえとの組み合わせ技。
getDefinitionByName(getQualifiedClassName(object))を使う代わりにカスタムリフレクションメソッドを作る
Create custom reflection methods instead of using: getDefinitionByName(getQualifiedClassName(object))
Daniel Hai ≫ More performance tuning in Actionscript 3
なんと、5,489%の高速化。あまりつかいそうではないとはいえ、無視できない数字。
polygonal labs ≫ Bitwise gems - fast integer math
2の累乗を掛ける場合は左シフトを使う
Left bit shifting to multiply by any power of two
300%の高速化。
2の累乗を割る場合は右シフトを使う
Right bit shifting to divide by any power of two
350%の高速化。
int整数変換は「>> 0」
Number to integer conversion
10%高速化。
int型の変数の入れ替えにはXOR演算
Swap integers without a temporary variable using XOR
こういう処理をするかどうかはともかく。20%高速化。
var t:int = a; a = b; b = t; //equals: a ^= b; b ^= a; a ^= b;
追記:aとbが同じ変数だった場合は必ず0になるので注意。同じ値の別の変数の場合は問題ない。詳細はコメント参照。
符号の変換にはNOT演算やXOR演算を使う
Sign flipping using NOT or XOR
300%高速化。
2の累乗で剰余を取る時は、AND演算をつかう
Fast modulo operation using bitwise AND
modulus = numerator & (divisor - 1);
600%高速化。
AND演算で偶数判断する
Check if an integer is even/uneven using bitwise AND
600%高速化。
Math.abs()を使わず絶対値を求める
Absolute value
//version 1 i = x < 0 ? -x : x; //version 2 i = (x ^ (x >> 31)) - (x >> 31);
この単純なコードでなんと2,500%高速化。さらにビット演算を組み合わせるとさらに加えて20%高速化。
2つのint型変数の符号が一致するかどうかを調べるときは、「eqSign = a ^ b >= 0;」
Comparing two integers for equal sign
35%高速化。
R5G5B5からR8G8B8へのピクセルフォーマット変換にはビットシフトをつかう
Fast color conversion from R5G5B5 to R8G8B8 pixel format using shifts
まとめ
- 基本
forループで配列内走査をする場合は前もって配列の長さを所得しておき、イテレータ変数をint/uint型にする- 他のクラスの定数は一旦ローカルの変数に値を代入してからループでつかう
- ローカル変数宣言は1行にまとめる
- (AS2のように)条件文や命令文を書き換えない
Object型は使わない- 配列の要素を読むときは型のキャストをする
- できる限り、
int型を使う Point型やRectangle型などよく使うオブジェクトの定数をつくって使いまわす- パッケージ変数・定数やパッケージ関数をつかってできる限りクラス名参照を減らす
- (
.graphicsや.transformなどの)グローバルgetterプロパティを2回以上呼ぶ場合は、ローカル変数に代入して使う getDefinitionByName(getQualifiedClassName(object))を使う代わりにカスタムリフレクションメソッドを作る
- 描画
- フレーム毎にビットマップ処理が必要な場合、前もってビットマップデータを作成しておき、
fillRect()/copyPixels()を使う mx.utils.GraphicsUtil.drawRoundRectComplex()よりflash.display.Graphics.drawRoundRectComplex()を使う
- フレーム毎にビットマップ処理が必要な場合、前もってビットマップデータを作成しておき、
- ビット演算化
- 2の累乗を掛ける場合は左シフトを使う
- 2の累乗を割る場合は右シフトを使う
int整数変換は「>> 0」int型の変数の入れ替えにはXOR演算- 符号の変換にはNOT演算やXOR演算を使う
- 2の累乗で剰余を取る時は、AND演算をつかう
- AND演算で偶数判断する
Math.abs()を使わず絶対値を求める- 2つの
int型変数の符号が一致するかどうかを調べるときは、「eqSign = a ^ b >= 0;」 - R5G5B5からR8G8B8へのピクセルフォーマット変換にはビットシフトをつかう
なお、数値は実際に私が測定したわけではないので、実際は変わる可能性が高い。
おまけ:経験則での最適化Tips
未確認多数につき、要確認。
TextField.textに文字列を追加するときは「+=」でつなげるのではなくappendText()をつかう*2- フィルタの
blurX,blurYプロパティの値は2の累乗にする*3 - 変数のうち、可能なものはできるかぎり定数化
- ループの内部からできる限り処理を追い出す
- 参照はできるかぎり短く
追記:注意点
多くの人に注目されたようなので注意点。基本的にこういう最適化は「わかっている人」向けなので、よく吟味してから使用されたし。特にビット演算を使った最適化は直感的でないので、仕組みをよく理解していないと思わぬバグの原因になりかねない。
ちなみに、ビット演算系の最適化は他の堅めの言語だとポピュラーな手法で、そのあたりのノウハウはマダマダAS3でも使えそうな感じ。AS3のいいところは、基本は超高級言語ながら、やろうと思えばこういうある意味古典的なこともできるところだと思う。
a == b の時は使えないですね。
a=0; b=0;
a ^= b; //→(1,0)
b ^= a; //→(1,1)
a ^= b; //→(0,0)
a=1; b=1;
a ^= b; //→(0,1)
b ^= a; //→(1,0)
a ^= b; //→(1,1)
だから使える気がする
ちょっと調べてみましたが、svaさんのコメントの通り、「a == b」つまりaとbが同じ値でも別の変数である時は使えます。
問題は、【aとbが同じ変数】の時ですね。
つまり、
a ^= a;
a ^= a;
a ^= a;
の時は必ずaは0になってしまいます。
これを変数の入れ替えと呼ぶかはともかく、関数化していた場合などには注意が必要そうです。