サイトトップ

Director Flash 書籍 業務内容 プロフィール

Optimizing Performance of ActionScript 3.0

Chapter 04 数値の演算

□04-05 Math.max()/Math.min()/Math.abs()メソッドより条件演算子:?で計算した方が速い

Mathクラスのメソッドには、簡単な条件判定で演算でき、その方が速い場合もあります。そうした例として、Math.max()Math.min()およびMath.abs()メソッドを採上げ、条件演算子:?を使った計算に書替えてみます。これらのメソッドは引数の数値から、Math.max()Math.min()がそれぞれ最大値と最小値、Math.abs()メソッドは絶対値を返します(表04-05-001)。

表04-05-001■Math.max()/Math.min()/Math.abs()メソッド
メソッド 引数 戻り値
Math.max() 複数の数値(Number型) 引数の中のもっとも大きい値(Number型)
Math.min() 複数の数値(Number型) 引数の中のもっとも小さい値(Number型)
Math.abs() 数値(Number型) 引数の絶対値(Number型)

04-05-01 ふたつの数から最大値・最小値を取出す
Math.max()およびMath.min()メソッドの演算を条件演算子?:で書替えるには、ふたつの数値の大きさをただ比べるだけです(表04-05-002)。演算子?:のごく基本的な使い方といえます(なお、条件演算子については、02-02「条件演算子?:は値を返す処理に使う」でご説明しました)。

表04-05-002■ふたつの数値のうちの最大値・最小値を求める
Mathクラスのメソッド 条件演算子?:による書替え
Math.max(a, b) (a > b) ? a : b
Math.min(a, b) (a < b) ? a : b

たとえば、Numberベース型のVectorオブジェクトから最大の数値エレメントを取出してみましょう。Math.max()メソッドで調べて最大値を返す関数(xGetMax())は、つぎのスクリプト04-05-001のように定められます。

スクリプト04-05-001【△】Vectorオブジェクトの数値エレメントからMath.max()メソッドで最大値を取出す関数
  1. function xGetMax(values:Vector.<Number>):Number {
  2.   var nLength:uint = values.length;
  3.   var nMax:Number = values[0];
  4.   for (var i:uint = 1; i < nLength; i++) {
  5.     var myValue:Number = values[i];
  6.     nMax = Math.max(nMax, myValue);
  7.   }
  8.   return nMax;
  9. }

スクリプト04-05-001第3行目で最初のエレメント(インデックス0)を変数(nMax)に取出し、第4〜7行目のforループで残りのすべてのエレメントについてMath.max()メソッドで変数値と大きさを比べます。大きな数値が見つかるたびに変数の数値を書替え、第8行目で調べ終えた最大値を返します。

[ムービープレビュー]で関数(xGetMax())をつぎのステートメントのように試せば、最大値2が[出力]されます。

var numbers:Vector.<Number> = new <Number>[-2, -1, 0, 1, 2];
trace(xGetMax(numbers));   // 出力: 2

処理の速さがさほど問われないなら、前掲スクリプト04-05-001のようにMathクラスのメソッドを用いても差支えありません。けれど、数値が多かったり、計算の頻度が高かいなど、速い演算が求められるときは条件判定に書替えた方がよいでしょう。

スクリプト04-05-002【○】Vectorオブジェクトの数値エレメントから条件演算子?:で最大値を取出す関数
  1. function xGetMax(values:Vector.<Number>):Number {
  2.   var nLength:uint = values.length;
  3.   var nMax:Number = values[0];
  4.   for (var i:uint = 1; i < nLength; i++) {
  5.     var myValue:Number = values[i];
  6.     nMax = (nMax > myValue) ? nMax : myValue;
  7.   }
  8.   return nMax;
  9. }

Tips 04-05-001■代入しなくてよい場合があるならifステートメントを用いる
前掲スクリプト04-05-002の関数(xGetMax())におけるforループでは、変数(nMax)に値を必ず代入しています(第6行目)。しかし、エレメント値の方が小さい場合は、変数を同じ値で上書きしているだけですので、代入する必要はありません。

このような無駄な代入を避けるには、つぎのスクリプト04-05-003のようにifステートメント(第6〜8行目)を用いればよいでしょう。

スクリプト04-05-003【◎】Vectorオブジェクトの数値エレメントからifステートメントで最大値を取出す関数
  1. function xGetMax(values:Vector.<Number>):Number {
  2.   var nLength:uint = values.length;
  3.   var nMax:Number = values[0];
  4.   for (var i:uint = 1; i < nLength; i++) {
  5.     var myValue:Number = values[i];
  6.     if (nMax < myValue) {
  7.       nMax = myValue;
  8.     }
  9.   }
  10.   return nMax;
  11. }

04-05-02 数の絶対値をとる
絶対値は、計算する値が負のときマイナス記号を除きます。つまり、数値に-1を乗じるのです。この演算も、条件演算子?:を使って簡単に表せます(表04-05-003)。

表04-05-003■数値の絶対値を求める
Mathクラスのメソッド 条件演算子?:による書替え
Math.abs(n) (n < 0) ? -n : n

また、サンプルとなる関数を定めましょう。引数にはNumberベース型のVectorオブジェクトを渡します。エレメントの数値は正負の値を混ぜて、それらすべて絶対値を新たなVectorオブジェクト(Numberベース型)に納めて返します。各数値エレメントの絶対値をMath.abs()メソッドで求めたのが、つぎのスクリプト04-05-004の関数(xGetAbs())です。

スクリプト04-05-004【△】Vectorオブジェクトの数値エレメントをMath.abs()メソッドで絶対値にする関数
  1. function xGetAbs(values:Vector.<Number>):Vector.<Number> {
  2.   var nLength:uint = values.length;
  3.   var absValues:Vector.<Number> = new Vector.<Number>(nLength);
  4.   for (var i:uint = 0; i < nLength; i++) {
  5.     var myValue:Number = values[i];
  6.     absValues[i] = Math.abs(myValue);
  7.   }
  8.   return absValues;
  9. }

スクリプト04-05-004第3行目で戻り値となるNumberベース型のVectorインスタンスをつくり、第6行目で絶対値をそのオブジェクトに加えて返したこと以外、基本的な処理の流れは前項のスクリプト04-05-001や04-05-002に準じます。ですから、とくに新たな説明は要らないでしょう。

[ムービープレビュー]で関数(xGetAbs())をつぎのステートメントのように試すと、引数のVectorオブジェクトの数値エレメントすべてを絶対値を納めた新たなVectorインスタンス(Numberベース型)が返され、それらの絶対値が[出力]されます。

var numbers:Vector.<Number> = new <Number>[-2, -1, 0, 1, 2];
trace(xGetAbs(numbers));   // 出力: 2,1,0,1,2,0

数の絶対値についても、演算の速さが求められるときは、条件演算子を使うことで改善できます。前掲スクリプト04-05-004第6行目のMath.abs()メソッドを演算子?:の式に書替えたのが、つぎのスクリプト04-05-005です(図04-05-001)。

スクリプト04-05-005【○】Vectorオブジェクトの数値エレメントを条件演算子により絶対値にする関数
  1. function xGetAbs(values:Vector.<Number>):Vector.<Number> {
  2.   var nLength:uint = values.length;
  3.   var absValues:Vector.<Number> = new Vector.<Number>(nLength);
  4.   for (var i:uint = 0; i < nLength; i++) {
  5.     var myValue:Number = values[i];
  6.     absValues[i] = (myValue < 0) ? -myValue : myValue;
  7.   }
  8.   return absValues;
  9. }

図04-05-001■引数にしたVectorオブジェクトの数値エレメントがすべて絶対値に変わる

引数に渡したVectorオブジェクトの数値エレメントから求めた絶対値が、新たなVectorオブジェクトに納められて返される。

[*筆者用参考]

Getting the absolute value with Math.abs() vs ?: operator - wonderfl build flash online


04-05-03 なぜMathクラスのメソッドより条件演算子:?で計算した方が速いのか
結びとして、なぜMathクラスのメソッドより条件演算子:?で計算した方が速いのか、その理由をふたつ補足しておきます。

第1は、Math.max()Math.min()メソッドの引数の数です。これらのメソッドの引数はふたつにかぎらず、いくつでも渡せ、戻り値はその中の最大値もしくは最小値になります。すると、メソッドの実装としては、まず引数の数を確かめたうえで、それらすべての大きさを比べなければならないのです。

ところが、前述04-05-01「ふたつの数から最大値・最小値を取出す」では、数値はふたつと定めて条件演算子を使いました。予め数は決まっていて、しかもたったふたつなのですから余計な手間が要りません。その分速さが稼げるのです。サッカーでゴールを狭くしてしまえば、ボールの来る範囲は絞られてキーパーが楽に守れるのと同じです。

Tips 04-05-002■関数に不定の引数を宣言する
関数やメソッドを定義するとき、予め数が決まっていない(不定の)引数も宣言できます。引数の前にパラメータ定義キーワード...を添えると、その引数名で配列がつくられ、後のすべての引数はその配列エレメントとして納められます。

function functionName(...引数を納める配列):戻り値のデータ型 {
  // ステートメント
}

このキーワード...を用いれば、Math.max()メソッドと同じく、不定の引数値から最大値を返す関数(xMax())がつぎのスクリプト04-05-005のように定められます。これで関数には、Math.max()メソッドと同じく、数値の引数をいくつでも渡せます。

スクリプト04-05-006【○】不定の引数値から最大値を返す関数
  1. function xMax(...values):Number {
  2.   var nLength:uint = values.length;
  3.   var nMax:Number = values[0];
  4.   for (var i:uint = 1; i < nLength; i++) {
  5.     var myValue:Number = values[i];
  6.     if (myValue > nMax) {
  7.       nMax = myValue;
  8.     }
  9.   }
  10.   return nMax;
  11. }

関数(xMax())につぎのように複数の数値を引数として渡せば、[ムービープレビュー]でその中の最大値が[出力]されます。もっとも、この関数の演算の速さは、もはやMath.max()メソッドに劣ります。

trace(xMax(-2, -1, 0, 1, 2));   // 出力: 2

条件演算子が速い理由の第2は、:?演算子を用いた式がステートメントとして直接書かれていること(これを「インライン」といいます)です。実際、前掲スクリプト04-05-002や04-05-005では、最大値または絶対値を求める関数というかたちで定められてはいません。

関数やメソッドを呼出すと、その呼出しそのものがひとつの処理になります。Math.max()Math.min()あるいはMath.abs()はメソッドですから、条件演算子をインラインで書いた場合と比べて、呼出しというひと手間が多くかかってしまうのです。

Word 04-05-001■インライン
処理を関数として定めず、その内容となるステートメントを直接スクリプトに書込むことをいいます。そのようにして書かれた処理は「インライン・コード」と呼ばれます。[ヘルプ]の[モバイル]<http://help.adobe.com/ja_JP/as3/mobile/index.html>/[Flash Platformのパフォーマンスの最適化]/[ActionScript 3.0のパフォーマンス]/[その他の最適化]は、インライン化についてつぎのように説明します。

関数呼び出しは負荷を増加させる可能があります。コードをインライン化して、関数呼び出しの回数を削減するようにします。コードのインライン化は、純粋なパフォーマンスの最適化に適した方法です。ただし、インラインコードではコードの再利用が困難で、SWFファイルのサイズが大きくなる可能性があるので注意してください。

処理を直に書込めば、速さは稼げます。その代わり、関数のような使い回しが難しくなります。同じ処理が求められるたびに、同じステートメントを書かなければならなくなり、スクリプトのサイズも増えます。

インラインによる最適化は、速さがきわめて大事な場合や、本節でご紹介したMathラスのメソッドのようにステートメント数が少ない処理にかぎって用いるのがよいでしょう。

[*筆者用参考] IT用語辞典バイナリ「インラインコード」、Wikipedia「インライン展開」。


[*筆者用参考]

ふたつの数値の最大値・最小値を得るにはMathクラスより条件演算子?:の方が速い

「ActionScript 3.0におけるパフォーマンス向上のヒント」06「数値の演算

nulldesign「Mathクラスでよくやる高速化備忘録

Math.absとMath.min Math.maxは何のためにあるの? - wonderfl build flash online

Math.max/minとただのifの速度比較 - wonderfl build flash online



作成者: 野中文雄
作成日: 2011年5月21日


Copyright © 2001-2011 Fumio Nonaka.  All rights reserved.