2013年8月15日木曜日

Unityにてマルチスレッドを使う

Unityのビヘイビア等は全て1つのメインスレッドにて動作します。

物理演算・衝突処理・レンダリング辺りはマルチスレッドで動いているかと思いますが、ユーザーの書いたスクリプトはメインスレッドで動くのが原則です。

そこで疑問になってくるのは、マルチスレッド化出来るのか否か、というところです。


考えてみても仕方がないので実装してみましょう。

作成中のシューティングゲームは、弾の位置更新や敵のルーチンが比較的重く、Updateメソッドの完了に3ミリ秒程度掛かってしまっています。

そこで、Updateメソッド内の処理を別スレッドに任せるよう改造してみます。

GameObjectへのアクセス

但し、マルチスレッド化にはちょっと問題がありまして、GameObjectやそのコンポーネントに対する更新処理を別スレッドやUpdate以外のタイミングで行うと、エラーで止まってしまいます。

※因みにUnityEngine.Randomクラスも別メソッドからアクセス出来ない・・・
 ので、.net標準のRandomクラスを使うように変更します。

ですので、GameObjectへのアクセスとEntityの更新を分離させてあげる必要があります。


ざっくりこんな感じのシーケンスになるでしょうか。
ポイントは、GameObjectを所有するEntityが、GameObjectへの変更を保持しておき、Performメソッドにて反映するというところ。

値のコピーで出来ない部分は、更新するデリゲートを生成し、Perform時にデリゲートをコールします。

さて、この辺まで設計してみると、「パフォーマンスは改善しませんでした」というオチが見えてしまっているのですが、実際にパフォーマンスを取ってみるまでは手を止めるわけにはいきません。

無駄だと思いつつも実装

というわけで突貫工事で実装してみました。

シングルスレッド
OnFrame処理 2.7597ms

マルチスレッド
Perform処理 0.1305ms
OnFrame処理 2.9320ms

トータルでの処理時間は増えていますが、マルチスレッド版のOnFrameは別スレッドなのであまり関係ありません。
そういう意味では目論見通りの計測値です

フレームレート

じゃあ、フレームレートはどうなったのかといえば、あまり変化しませんでした。

シングルスレッド:33.79fps
マルチスレッド:34.36fps

ほぼ誤差ですね。

気になった点としては、シングルスレッドのGC間隔が1秒強だったのに対し、マルチスレッドでは0.2秒と間隔が短くなっている点。
恐らくデリゲートを生成しなければならない点でGCにて回収すべきオブジェクトが増えたと思われます。

それにしても、Update高速化の影響が殆どありません。
フレームレートに関しては、表示周りが与える影響が支配的な領域なのでしょう。

結論

ゲームオブジェクトに触らない範囲でのロジックがかなり重い場合においてはマルチスレッド化する価値はあるかも知れません。
通常のケースにおいては、シングルスレッドでの実装で問題ないようです。

フレームレートを上げたい場合、表示周りを改善するのが一番のようです。

0 件のコメント:

コメントを投稿