GMOインターネットグループの技術情報は新しいサイトに移管しています。 新しいサイトはこちら

社内レポート

2014年1月29日(水)

ランタイムを活用して性能改善に挑む

アプリ開発で遭遇した問題にどのように取り組んできたか

スマートフォン市場で勝ち残るためのアプリ/サービス開発の要点を紹介するGMO最新ネット業界レポート「スマートフォン編」。前回までに引き続き、GMOゲームセンター株式会社(以下GMOゲームセンター)でプラットフォーム開発部の部長を務める庄司拓郎氏が解説する。

GMOゲームセンターでは、ゲームプラットフォーム「Gゲーby GMO(以下Gゲー)」の運営や、モバイルプラットフォーム向けアプリケーションランタイム「VIVID Runtime」の開発を行っている。その経験を通じて、同社にはモバイル端末向けのゲーム開発に必要となる多くのノウハウが蓄積されているという。そこで今回は、庄司氏がこれまでの開発で経験した様々な事例の中から、主にパフォーマンスにまつわる問題とその対応策などについて紹介する。

記事INDEX

アプリとOSの間で処理を制御できることがランタイムの強み

Gゲーでは、自前でランタイムを開発している関係から、様々な端末における非常に多くの問題を経験しています。「VIVID Runtime」自身や、自社タイトルの開発で生じた問題もあれば、Gゲーを利用するゲームベンダーやSAPから調査を依頼されて詳細が判明した問題もあります。そこで今回は、これまでのアプリ開発で経験した問題を取り上げながら、私たちが端末の性能や端末間の挙動の違いにどのように向き合ってきたのかを紹介したいと思います。

Gゲーの強みは、ランタイムを自社開発しているためにAPI内部の挙動を詳細に把握することができるという点です。ランタイムでは、本来は隠蔽されているライブラリやAPI内部の処理をフックすることができます。これは、本来はフレームワークなどに隠蔽されているレイヤーにも手を加えることが可能だということです。ランタイム内部の情報を活用して性能問題への対策を効率的に進められるだけでなく、場合によってはランタイム側で命令をフックして性能が上がるような処理方法に書き換えるといったことすらもできるわけです。

具体的な例を挙げてみましょう。OpenGL ESを利用したアプリで、グラフィック描画が遅いので調査して欲しいという依頼を受けたことがあります。OpenGL ESを使う場合、まずマトリクスを設定し、マテリアルを設定し、ポリゴンを描画するというのが一般的な手順になります。しかし、ポリゴンを一枚描く度にこの手順を繰り返し行うのではまともな速度での描画にはなりません。マトリクスの設定は負荷が大きいので、できるだけひとつのマトリクスで処理できるように記述するのが定石になっているのですが、調査対象となったアプリでは、この対策がとられていなかったために性能が出ていないことがわかりました。

そこで、ランタイム側で強制的に処理順序を修正することによって、アプリのコードに直接手を加えることなく描画速度を向上させることに成功しました。この方法であれば、仮にバイナリ形式で納品されてソースコードが無いアプリに対しても手を加えることができます。もっとも、このような特定の問題への対策をランタイム本体に組み込むことは現実的ではありません。実際には、この方法でどれくらい性能が上げられるのかを検証し、具体的にどのような改善を施せば良いのかを開発側にフィードバックするという形を取るケースが多いです。

アプリ開発者をサポートする機能も提供

もちろん、一般化できる問題に対してはランタイム本体に対策を組み込んだり、ライブラリやツールを作って開発をサポートするといったことも行っています。例えば、グラフィックのレンダリング性能を向上させる一般的な方法として、複数のテクスチャを1枚のシートにまとめて使うスプライトシートの利用があります。そこで、実行時にスプライトシートを生成し、利用できるような簡易的なライブラリを開発して提供しています。

また、圧縮テクスチャの利用も性能向上のための一般的な方法のひとつです。しかし、端末によってグラフィックチップが異なり使えるフォーマットが統一されていないという問題があるため、マルチプラットフォーム対応のアプリではあまり活用されていないのが実情です。そこで「VIVID Runtime」では、iOS向けアプリの開発者に馴染みのあるPVRTC形式(iPhoneやiPadで採用されているPowerVR用の圧縮フォーマット)で記述すれば、ランタイム側でそれぞれの端末向けのフォーマットに自動で変換するという機能を用意しています。端末に依存する部分をランタイムで吸収するという一例です。

端末固有の問題をランタイムで解消する

端末固有の問題に対しても、ランタイム側で対策を講じることがよくあります。端末ごとの細かな挙動の違いやバグをアプリ開発者が意識しなくていいようにすることも、ランタイムの重要な役割だからです。

ある端末では、OpenGLの特定のAPIの実行だけが極端に遅いという問題がありました。直線を描画するだけの単純な処理なのですが、ポリゴンを描画するよりも遅いという不自然な挙動を示します。そこで「VIVID Runtime」では、この端末で問題のAPIが呼び出された場合だけ、直線ではなく幅1ピクセルのポリゴンを描画する処理に自動で置き換えるという対策を実装しています。

別の端末では、呼び出した後に必ず特定のAPIを続けて呼ばなければ描画がくずれるという特性がありました。このときは、ランタイムで命令の実行順序を制御し、対象のAPIの呼び出しを強制的に行うようにして対処しました。

似たような経験はグラフィックだけでなくオーディオ出力でもあります。ある端末では、オーディオ出力が極端に遅く、音を鳴らすたびに全ての処理が数十ミリ秒停止してしまうという問題がありました。メインスレッドが止まってしまうため、グラフィックの描画が停止するだけでなく、全ての入力を受け付けなくなってしまいます。これはアクション系やシューティング系のゲームにとって致命的でした。調査の結果、この遅延はAPI呼び出しのオーバーヘッドとして発生していることがわかったので、この端末で実行したときだけ音を鳴らしていない間でもストリームを開いたままにするという対策を施しました。

特定のAPIが必ずメモリオーバーランを起こすという問題もありました。3バイトを書き込んだつもりが、実際には余分な1バイトを含む4バイトのデータが書き込まれてしまって、次のアドレスのフラグが消えてしまうというバグです。このときも、ランタイムで命令をフックし、正しく3バイトが書き込まれるように処理を制御するという対策を行いました。

「iPhoneでは快適だったのに」と言われないために

マルチプラットフォーム向けの開発をしていると、「特定の端末だけうまく動かない」という現象に遭遇します。同じように、「機種変更したら重くなった」というような理由で低い評価が付けられるケースもよくあります。私たちも、iOS版からAndroid版への移植では、ハードウェアの性能の差にも苦労しました。「iPhoneでは快適だったのに」などと言われないためには、Androidでも同等のパフォーマンスを実現する必要があります。しかし、端末全体の平均値で見ればiPhoneはハードウェアスペックが高いため、ただそのまま移植しただけではまともに動作しない端末も出てきてしまいます。

Gゲーではできるだけ多くの端末をカバーすることを目標としているので、iPhoneよりもスペックが低いからといって諦めるわけにはいきません。一般的な対策としては、テクスチャの解像度を落としたり、背景の描き込みを減らすなどといった手段が考えられます。また、コードの再利用性や可読性を犠牲にしてチューニングすることも多々あります。経験談で言えば、iPhoneは十分なスペックを持っているので、それほど極端なチューニングは行われていないケースが多いです。したがってその一歩先まで突き詰めていけば、最適化の余地はまだまだあります。

バグや性能問題が発生するのが特定の端末だけだったとしても、その問題を無視しても良いかと言えば、必ずしもそれは得策ではありません。その特定の端末のユーザーによる評価が、アプリ全体への評価に影響してしまうということもよくあるからです。せっかく開発したゲームをより多くのユーザーに楽しんでもらうためにも、性能を向上させるための基本的なノウハウを身に付け、積極的に改善に取り組んでいくことが大切です。


取材日:2013.12.11