CPU 使用率の最小化

最適化の重要な領域の一つに、CPU 使用率があります。CPU 処理を最適化すると、パフォーマンスを改善し、モバイルデバイスのバッテリー寿命を延ばすことができます。

Flash Player 10.1 における CPU 使用率の強化機能

Flash Player 10.1 には、CPU 処理の軽減に役立つ 2 つの新機能が導入されています。SWF コンテンツが画面外に移動したときに再生を一時停止および再開する機能と、1 ページあたりの Flash Player のインスタンス数を制限する機能です。

一時停止、スロットル、再開

注意: 一時停止、スロットルおよび再開機能は、Adobe® AIR® アプリケーションには適用されません。

CPU 使用率とバッテリー使用率を最適化するため、Flash Player 10.1 には非アクティブなインスタンスに関する新機能が導入されています。この機能を使用すると、コンテンツが画面に表示されたり表示されなくなったときに SWF ファイルを一時停止および再開することで、CPU 使用率を制限できます。Flash Player は、この機能を使用して、コンテンツ再生を再開したときに再作成すればよいオブジェクトをすべて削除することにより、できる限り多くのメモリを解放します。コンテンツ全体が画面外にある場合、コンテンツはオフスクリーンと見なされます。

SWF コンテンツがオフスクリーンになるシナリオは 2 つあります。

  • ユーザーがページをスクロールして、SWF コンテンツを画面外に移動させた場合。

    オーディオまたはビデオ再生があるときは、コンテンツの再生は続けられますが、レンダリングは停止します。再生中のオーディオまたはビデオがない場合に、再生または ActionScript の実行が一時停止されないようにするには、hasPriority HTML パラメーターを true に設定します。ただし、SWF コンテンツがオフスクリーンまたは非表示の場合は、hasPriority HTML パラメーターの値に関わらず、コンテンツのレンダリングが一時停止することに注意してください。

  • ブラウザーのタブが開かれ、SWF コンテンツがバックグラウンドに移動した場合。

    hasPriority HTML タグの値に関わらず、SWF コンテンツの速度が 2 ~ 8 fps に下げられます(つまり、「スロットル」されます)。SWF コンテンツが再度表示されるまで、オーディオとビデオの再生は停止し、コンテンツのレンダリングは処理されません。

Flash Player 11.2 以降で、Windows および Mac のデスクトップブラウザーで実行されている場合は、アプリケーションで ThrottleEvent を使用できます。ThrottleEvent は、Flash Player が再生を一時停止、スロットルまたは再開したときに送出されます。

ThrottleEvent はブロードキャストイベントです。したがって、このイベントに対して登録されているリスナーを持つすべての EventDispatcher オブジェクトがこのイベントを送出します。ブロードキャストイベントについて詳しくは、「DisplayObject」クラスを参照してください。

インスタンスの管理

注意: インスタンスの管理機能は、Adobe® AIR® アプリケーションには適用されません。
オフスクリーンの SWF ファイルのロードを遅らせるには、hasPriority HTML パラメーターを使用してください。

Flash Player 10.1 には、hasPriority という新しい HTML パラメーターが導入されています。

<param name="hasPriority" value="true" />

この機能を使用すると、ページ上で開始される Flash Player のインスタンス数を制限できます。インスタンス数を制限すると、CPU およびバッテリーリソースを節約することができます。この意図は、SWF コンテンツに特定の優先度を割り当て、ページ上にある一部のコンテンツを他のコンテンツよりも優先させることにあります。次の簡単な例について考えてみましょう。例えば、ユーザーが閲覧中の Web サイトで、インデックスページに 3 つの異なる SWF ファイルがホストされているとします。そのうちの 1 つは表示され、もう 1 つは部分的に表示されています。最後のファイルは画面外にあり、表示するにはスクロールする必要があります。最初の 2 つのアニメーションは正常に開始されますが、最後のアニメーションは表示されるまで開始が保留されます。このシナリオは、hasPriority パラメーターが存在しないか、 false に設定されている場合のデフォルトの動作です。SWF ファイルが画面外にあっても開始されるようにするには、hasPriority パラメーターを true に設定します。ただし、hasPriority パラメーターの値に関わらず、ユーザーには表示されない SWF ファイルでは、常にレンダリングが一時停止されます。

注意: 使用可能な CPU リソースが減少すると、hasPriority パラメーターが true に設定されていても、Flash Player のインスタンスは自動的に開始されません。ページのロード後、JavaScript 経由で新しいインスタンスが作成されると、その新しいインスタンスは hasPriority フラグを無視します。Web 管理者が hasPriority フラグを含めることに失敗した場合、任意の 1 x 1 ピクセルまたは 0 x 0 ピクセルのコンテンツが開始され、SWF ヘルパー ファイルを保留できなくなります。ただし、SWF ファイルはクリックすると開始できます。この動作を、「クリックして再生」と呼びます。

次の図は、hasPriority パラメーターを別の値に設定したときの効果を示しています。

hasPriority パラメーターを別の値に設定したときの効果

hasPriority パラメーターを別の値に設定したときの効果

スリープモード

Flash Player 10.1 および AIR 2.5 には、モバイルデバイス向けの新機能が導入されています。この機能を使用すれば、CPU 処理を軽減してバッテリ寿命を延ばすことができます。この機能では、多くのモバイルデバイスに実装されているバックライトの動作を利用します。例えば、モバイルアプリケーションを実行中のユーザーが、何らかの理由でデバイスの使用を中止すると、ランタイムはバックライトのスリープモードへの移行を検出します。スリープモードを検出すると、フレームレートを 4 フレーム/秒(fps)に下げ、レンダリングを一時停止します。AIR アプリケーションの場合、アプリケーションがバックグラウンドに移動したときにもスリープモードが開始されます。

ActionScript コードはスリープモードで引き続き実行されます。つまり、Stage.frameRate プロパティを 4 fps に設定したときと同様の動作です。ただし、レンダリング処理は行われないので、ユーザーはプレーヤーが 4 fps で実行中であることに気づきません。フレームレートを 0(ゼロ)ではなく 4 fps に設定するのは、すべての接続(NetStream、Socket および NetConnection)で開いた状態を維持できるからです。0(ゼロ)に切り替えると、開いている接続が切断されます。更新頻度に 250 ミリ秒(4 fps)が選択されている理由は、多くのデバイスメーカーがこのフレームレートを更新頻度として使用しているからです。この値を使用することにより、ランタイムとデバイスのフレームレートを同じ範囲内に収めることができます。

注意: ランタイムがスリープモードのときは、Stage.frameRate プロパティは 4 fps ではなく、オリジナルの·SWF ファイルのフレームレートを返します。

バックライトがオンモードに戻ると、レンダリングが再開されます。フレームレートは元の値に戻ります。ユーザーがメディアプレーヤーアプリケーションで音楽を再生している場合について考えてみましょう。画面がスリープモードに移行すると、ランタイムは再生中のコンテンツの種類に基づいて応答します。状況に応じたランタイムの動作を以下に示します。

  • バックライトがスリープモードに移行し、非 A/V コンテンツが再生中の場合:レンダリングは一時停止され、フレームレートが 4 fps に設定されます。

  • バックライトがスリープモードに移行し、A/V コンテンツが再生中の場合:ランタイムはバックライトを強制的に常時オンモードにし、ユーザーエクスペリエンスを維持します。

  • バックライトがスリープモードからオンモードに移行した場合:ランタイムはフレームレートを元の SWF ファイルのフレームレート設定に合わせ、レンダリングを再開します。

  • A/V コンテンツの再生中に Flash Player を一時停止した場合:Flash Player は、A/V コンテンツが再生されていないので、バックライトの状態をデフォルトのシステム動作にリセットします。

  • A/V コンテンツの生成中にモバイルデバイスが電話の呼び出しを着信した場合:レンダリングは一時停止され、フレームレートが 4 fps に設定されます。

  • モバイルデバイスでバックライトのスリープモードを無効にしている場合:ランタイムは通常の動作をします。

バックライトがスリープモードに移行すると、レンダリングが一時停止され、フレームレートが低速に設定されます。この機能は CPU 処理を軽減しますが、ゲームアプリケーションなどでは一時停止が実際に正しく機能しない場合もあります。

注意: ランタイムがスリープモードに移行するか、スリープモードを終了するときは、ActionScript イベントは送出されません。

オブジェクトのフリーズとフリーズ解除

オブジェクトプロパティのフリーズとフリーズ解除には、REMOVED_FROM_STAGE イベントおよび ADDED_TO_STAGE イベントを使用します。

コードを最適化するには、オブジェクトのフリーズとフリーズ解除を常に実行します。フリーズとフリーズ解除はすべてのオブジェクトにとって重要ですが、特に表示オブジェクトにとって重要です。表示オブジェクトは、既に表示リストになく、ガベージコレクションで収集されるのを待機している状態でも、CPU 負荷の高いコードを使用している可能性があります。例えば、Event.ENTER_FRAME を引き続き使用している場合などです。その結果、Event.REMOVED_FROM_STAGE イベントおよび Event.ADDED_TO_STAGE イベントを使用してオブジェクトを正しくフリーズおよびフリーズ解除することが重要です。次の例は、キーボードの操作によってステージ上で再生されるムービークリップを示しています。

// Listen to keyboard events 
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyIsDown); 
stage.addEventListener(KeyboardEvent.KEY_UP, keyIsUp); 
  
// Create object to store key states 
var keys:Dictionary = new Dictionary(true); 
  
function keyIsDown(e:KeyboardEvent):void 
{ 
    // Remember that the key was pressed 
    keys[e.keyCode] = true;     
  
    if (e.keyCode==Keyboard.LEFT || e.keyCode==Keyboard.RIGHT) 
    { 
        runningBoy.play(); 
    } 
} 
  
function keyIsUp(e:KeyboardEvent):void 
{ 
    // Remember that the key was released 
    keys[e.keyCode] = false; 
  
    for each (var value:Boolean in keys) 
          if ( value ) return; 
    runningBoy.stop(); 
} 
  
runningBoy.addEventListener(Event.ENTER_FRAME, handleMovement); 
runningBoy.stop(); 
  
var currentState:Number = runningBoy.scaleX; 
var speed:Number = 15; 
  
function handleMovement(e:Event):void 
{ 
    if (keys[Keyboard.RIGHT]) 
    { 
        e.currentTarget.x += speed; 
        e.currentTarget.scaleX = currentState;     
    } else if (keys[Keyboard.LEFT]) 
    { 
        e.currentTarget.x -= speed; 
        e.currentTarget.scaleX = -currentState; 
    } 
}

フルサイズのグラフィックを表示
キーボードの操作によるムービークリップ

「Remove」ボタンをクリックすると、表示リストからムービークリップが削除されます。

// Show or remove running boy 
showBtn.addEventListener (MouseEvent.CLICK,showIt); 
removeBtn.addEventListener (MouseEvent.CLICK,removeIt); 
 
function showIt (e:MouseEvent):void 
{ 
    addChild (runningBoy); 
} 
 
function removeIt(e:MouseEvent):void 
{ 
    if (contains(runningBoy)) removeChild(runningBoy); 
}

ムービークリップは、表示リストから削除された状態でも、引き続き Event.ENTER_FRAME イベントを送出します。ムービークリップはまだ実行されていますが、レンダリングは行われません。この状況を正しく処理するには、適切なイベントを監視し、イベントリスナーを削除して、CPU を集中的に使用するコードが実行されないようにします。

// Listen to Event.ADDED_TO_STAGE and Event.REMOVED_FROM_STAGE 
runningBoy.addEventListener(Event.ADDED_TO_STAGE,activate); 
runningBoy.addEventListener(Event.REMOVED_FROM_STAGE,deactivate); 
 
function activate(e:Event):void 
{ 
    // Restart everything 
    e.currentTarget.addEventListener(Event.ENTER_FRAME,handleMovement); 
} 
 
function deactivate(e:Event):void 
{ 
    // Freeze the running boy - consumes fewer CPU resources when not shown 
    e.currentTarget.removeEventListener(Event.ENTER_FRAME,handleMovement); 
    e.currentTarget.stop(); 
}

「Show」ボタンをクリックすると、ムービークリップが再び開始され、再度 Event.ENTER_FRAME イベントが監視されて、キーボードは正しくムービークリップを制御します。

注意: 表示リストから表示オブジェクトを削除する場合、削除後にその参照先を null に設定しても、オブジェクトがフリーズされるとは限りません。ガベージコレクションを実行しないと、オブジェクトが表示されていない場合でも、そのオブジェクトはメモリおよび CPU を消費し続けます。オブジェクトの CPU 消費量を最小限に抑えるには、オブジェクトを表示リストから削除するときに、オブジェクトが完全にフリーズされていることを確認してください。

また、Flash Player 10 および AIR 1.5 以降では、次のように動作します。再生ヘッドが空のフレームを検出すると、フリーズ機能が実装されていない場合でも、表示オブジェクトが自動的にフリーズされます。

フリーズの概念は、Loader クラスでリモートコンテンツを読み込む場合にも重要です。Flash Player 9 および AIR 1.0 で Loader クラスを使用している場合は、コンテンツを手動でフリーズする必要があります。これを行うには、LoaderInfo オブジェクトが送出する Event.UNLOAD イベントを監視します。すべてのオブジェクトを手動でフリーズするのは面倒な作業です。Flash Player 10 および AIR 1.5 では、Loader クラスに unloadAndStop() と呼ばれる新しい重要なメソッドが導入されています。このメソッドを使用すると、SWF ファイルをアンロードし、ロード済みの SWF ファイルに含まれているすべてのオブジェクトを自動的にフリーズして、ガベージコレクターを強制的に実行できます。

次のコードは、SWF ファイルをロードした後で unload() メソッドを使用してアンロードします。この処理はリソースを消費し、手動によるフリーズが必要になります。

var loader:Loader = new Loader(); 
 
loader.load ( new URLRequest ( "content.swf" ) ); 
 
addChild ( loader ); 
 
stage.addEventListener ( MouseEvent.CLICK, unloadSWF ); 
 
function unloadSWF ( e:MouseEvent ):void 
{ 
    // Unload the SWF file with no automatic object deactivation 
    // All deactivation must be processed manually 
    loader.unload(); 
}

ベストプラクティスは、unloadAndStop() メソッドを使用して、フリーズをネイティブに処理し、ガベージコレクション処理を強制的に実行することです。

var loader:Loader = new Loader(); 
 
loader.load ( new URLRequest ( "content.swf" ) ); 
 
addChild ( loader ); 
 
stage.addEventListener ( MouseEvent.CLICK, unloadSWF ); 
 
function unloadSWF ( e:MouseEvent ):void 
{ 
    // Unload the SWF file with automatic object deactivation 
    // All deactivation is handled automatically 
    loader.unloadAndStop(); 
}

unloadAndStop() メソッドが呼び出されたときに発生するアクションを以下に示します。

  • サウンドが停止します。

  • SWF ファイルのメインタイムラインに登録されたリスナーが削除されます。

  • Timer オブジェクトが停止します。

  • ハードウェア周辺機器(カメラやマイクなど)が解放されます。

  • すべてのムービークリップが停止します。

  • Event.ENTER_FRAMEEvent.FRAME_CONSTRUCTEDEvent.EXIT_FRAMEEvent.ACTIVATE および Event.DEACTIVATE イベントが送出されなくなります。

イベントのアクティブ化と非アクティブ化

バックグラウンドのアクティビティを検出したり、アプリケーションを適切に最適化するには、Event.ACTIVATE イベントと Event.DEACTIVATE イベントを使用します。

2 つのイベント(Event.ACTIVATEEvent.DEACTIVATE)を使用して、最小限の CPU サイクルを使用するようにアプリケーションを微調整できます。これらのイベントを使用すると、ランタイムがフォーカスを取得したことや失ったことを検出できます。そのため、コンテキストの変更に応じてコードを最適化できます。次のコードでは、両方のイベントをリッスンし、アプリケーションがフォーカスを失ったときに、動的にフレームレートをゼロに変更しています。例えば、ユーザーが別のタブに切り替えたり、アプリケーションをバックグラウンドに移動したときに、アニメーションのフォーカスを失うことができます。

var originalFrameRate:uint = stage.frameRate; 
var standbyFrameRate:uint = 0; 
  
stage.addEventListener ( Event.ACTIVATE, onActivate ); 
stage.addEventListener ( Event.DEACTIVATE, onDeactivate ); 
  
function onActivate ( e:Event ):void 
{ 
    // restore original frame rate 
    stage.frameRate = originalFrameRate; 
} 
  
function onDeactivate ( e:Event ):void 
{ 
    // set frame rate to 0 
    stage.frameRate = standbyFrameRate; 
}

アプリケーションが再びフォーカスを取得すると、フレームレートは元の値にリセットされます。フレームレートを動的に変更する代わりに、オブジェクトのフリーズとフリーズ解除などのほかの最適化を作成しておくこともできます。

アクティブ化イベントと非アクティブ化イベントを使用すると、一部のモバイルデバイスやネットブックで提供されている「一時停止と再開」機能と同様のメカニズムを実装できます。

マウスの操作

可能な場合は、マウスの操作を無効にすることを検討します。

MovieClip、Sprite オブジェクトなどのインタラクティブオブジェクトを使用すると、ランタイムはネイティブコードを実行してマウスの操作を検出および処理します。数多くのインタラクティブオブジェクトが画面に表示されていると(特にオーバーラップする場合)、マウス操作の検出により、CPU が集中的に使用される場合があります。この処理を避ける簡単な方法は、マウスの操作を必要としないオブジェクトではマウス操作を無効にすることです。次のコードは、mouseEnabled プロパティおよび mouseChildren プロパティの使用方法を示しています。

// Disable any mouse interaction with this InteractiveObject 
myInteractiveObject.mouseEnabled = false; 
const MAX_NUM:int = 10; 
  
// Create a container for the InteractiveObjects 
var container:Sprite = new Sprite(); 
  
for ( var i:int = 0; i< MAX_NUM; i++ ) 
{ 
    // Add InteractiveObject to the container 
    container.addChild( new Sprite() ); 
} 
  
// Disable any mouse interaction on all the children 
container.mouseChildren = false;

可能な場合は、マウスの操作を無効にすることを検討します。これにより、アプリケーションによる CPU 処理の使用が減り、結果としてモバイルデバイス上でのバッテリー使用が減ります。

タイマーと ENTER_FRAME イベント

コンテンツをアニメーション化するかどうかに応じて、タイマーまたは ENTER_FRAME イベントのどちらかを選択します。

長時間実行されるアニメーション化されないコンテンツには、Event.ENTER_FRAME イベントよりもタイマーをお勧めします。

ActionScript 3.0 では、特定の間隔で関数を呼び出すために 2 つの方法があります。最初の方法は、表示オブジェクト(DisplayObject)によって送出される Event.ENTER_FRAME イベントの使用です。2 番目の方法は、タイマーの使用です。ActionScript 開発者は、ENTER_FRAME イベントによる方法をよく使用します。ENTER_FRAME イベントは各フレームで送出されます。その結果、関数を呼び出す間隔は、現在のフレームレートに関連します。フレームレートは、Stage.frameRate プロパティによってアクセス可能です。ただし、場合によっては、タイマーを使用する方が ENTER_FRAME イベントを使用するよりも良い選択であることがあります。例えば、アニメーションを使用しないが、特定の間隔でコードを呼び出す場合は、タイマーを使用することをお勧めします。

タイマーは ENTER_FRAME イベントと同様に動作しますが、フレームレートとは無関係にイベントを送出できます。このビヘイビアーにより、大幅な最適化を実現できる場合があります。例えば、ビデオプレーヤーアプリケーションを考えてみましょう。この場合、高いフレームレートを使用する必要はありません。移動するのはアプリケーションコントロールのみであるためです。

注意: ビデオはタイムラインに埋め込まれないので、フレームレートはビデオに影響しません。その代わり、ビデオはプログレッシブダウンロードまたはストリーミングによって動的にロードされます。

この例では、フレームレートは 10 fps の低い値に設定されます。タイマーは、1 秒あたり 1 回の更新レートでコントロールを更新します。より高い更新レートは、updateAfterEvent() メソッドによって可能になります。このメソッドは TimerEvent オブジェクトで利用できます。このメソッドはタイマーがイベントを送出するたびに、必要に応じて画面を更新します。次のコードは、この考えを示しています。

// Use a low frame rate for the application 
stage.frameRate = 10; 
  
// Choose one update per second 
var updateInterval:int = 1000; 
var myTimer:Timer = new Timer(updateInterval,0); 
  
myTimer.start(); 
myTimer.addEventListener( TimerEvent.TIMER, updateControls ); 
  
function updateControls( e:TimerEvent ):void 
{ 
    // Update controls here 
    // Force the controls to be updated on screen 
    e.updateAfterEvent(); 
}

updateAfterEvent() イベントを呼び出しても、フレームレートは変更されません。ランタイムが、変更されたコンテンツを画面上で更新するだけです。タイムラインは引き続き 10 fps で実行されます。低パフォーマンスのデバイスの場合や、イベントハンドラー関数に負荷の高い処理が含まれている場合、タイマーおよび ENTER_FRAME イベントは完全には正確ではありません。SWF ファイルフレームレートと同様に、タイマーの更新フレームレートは、特定の状況では変化する可能性があります。

アプリケーション内の Timer オブジェクト数および登録する enterFrame ハンドラー数は最小限に抑えます。

各フレームでは、ランタイムから表示リストの各表示オブジェクトに対して enterFrame イベントが送出されます。複数の表示オブジェクトで enterFrame イベントのリスナーを登録できますが、この場合、各フレームで実行されるコードが増えます。代わりに、一元的に enterFrame ハンドラーを使用して、各フレームで実行する必要があるコードをすべて実行する方法があります。このコードを一元的にまとめることにより、頻繁に実行するすべてのコードを管理しやすくなります。

また、Timer オブジェクトを使用している場合、複数の Timer オブジェクトによる作成や送出のイベントに関係するオーバヘッドが発生します。異なる操作を異なる間隔でトリガーする必要がある場合の実現方法をいくつか次に示します。

  • 発生する頻度に従って、Timer オブジェクトおよびグループ操作の数を最小限に抑えます。

    例えば、頻度が高い操作に 1 つの Timer を使用し、100 ミリ秒ごとにトリガーするように設定します。頻度が低い操作またはバックグラウンド操作には別の Timer を使用し、2000 ミリ秒ごとにトリガーするように設定します。

  • 単一の Timer オブジェクトを使用し、Timer オブジェクトの delay プロパティ間隔の倍数で操作をトリガーします。

    例えば、100 ミリ秒ごとに発生させる操作と、200 ミリ秒ごとに発生させる操作があるとします。この場合、100 ミリ秒の delay 値を指定した単一の Timer オブジェクトを使用します。timer イベントハンドラーに、1 回おきに 200 ミリ秒の操作だけを実行するという条件文を追加します。次の例は、この方法を示しています。

    var timer:Timer = new Timer(100); 
    timer.addEventListener(TimerEvent.Timer, timerHandler); 
    timer.start(); 
         
    var offCycle:Boolean = true; 
      
    function timerHandler(event:TimerEvent):void 
    { 
        // Do things that happen every 100 ms 
         
        if (!offCycle) 
        { 
            // Do things that happen every 200 ms 
        } 
         
        offCycle = !offCycle; 
    }
使用していないときは Timer オブジェクトを停止します。

Timer オブジェクトの timer イベントハンドラーが特定の条件でのみ操作を実行する場合、該当する条件が 1 つも成立しないときは stop() メソッドを呼び出します。

enterFrame イベントまたは Timer ハンドラーでは、表示オブジェクトの外観に対する変更(画面の再描画の原因となります)の数を最小限に抑えます。

各フレームのレンダリングフェーズでは、そのフレームで、変更されたステージの部分が再描画されます。再描画の領域が大きい場合や、小さくても大量な、または複雑な表示オブジェクトが含まれる場合、ランタイムのレンダリングにかかる時間は長くなります。必要な再描画の量をテストするには、デバッグの Flash Player または AIR で「再描画する領域を表示」機能を使用します。

繰り返しのアクションに関するパフォーマンスを改善する方法について詳しくは、次の記事を参照してください。

トゥイーン使用による問題

CPU の処理能力を節約するため、トゥイーンの使用は控えます。これにより、CPU 処理およびメモリが節約され、バッテリーの寿命が延びます。

デスクトップ上の Flash 用コンテンツを作成するデザイナーや開発者は、アプリケーションで多くのモーショントゥイーンを使用する傾向があります。低パフォーマンスのモバイルデバイス用コンテンツを作成するときは、モーショントゥイーンの使用を最小限にするようにしてください。これにより、低パフォーマンスのデバイスでコンテンツの実行速度が高まります。