Previewで開発してます。今回はuSpringBoneの基盤を作ってく上で得たJobSystemの知識についてとか、気をつけておきたいこととか、そこらへんをメモしておこうと思います。現状のJobSystemについてのメモなので、今後色々変わると思います。エディタと実機での負荷計測JobSystemを使う上で、実機だとそこまで高負荷にならないような部分がエディタ上では負荷が高く見える事があるので注意が必要です。一部処理については、UnityのJobsメニューから各種チェックを外したりすれば多少実機での実行速度に近付きますが、ちゃんとした計測は実機で行うことをお勧めします。Jobはなるべく減らすJobは可能な限り減らしましょう。当然ですが、JobをScheduleするのにも多少コストがかかります。Jobを細分化しすぎると、JobのScheduleがちょっとした負荷になってしまう場合があります。}}阿保みたいな例ですが、上記コードは適当なJobを1000個Scheduleしてみるやつです。エディタとビルド後で実行してプロファイラーでScheduleの負荷を計測してみました。エディタで計測ビルド後計測これくらいだと「そこまで高負荷じゃないじゃん!」と思われるかもしれませんが、塵も積もればです。JobSystemに重い計算処理を退避させるので、MainThreadにはちょっとでも楽させてあげたいところ。上記のような簡単な例だと、単純にIJobParallelForを使って以下のように書き換えられます。Schedule呼び出し回数が減ったのでMainThreadに掛かる負荷が少なくなりました。今回のように単純には解決できないケースもあります。いうより殆どがそういったケースだと思います。Jobがあまりにも多くなる場合はより良い設計を考え直しましょう。NativeContainerへのアクセスについて下記2つのスクリプトを実行して、NativeArrayへのアクセス部分の負荷の変化を見てみました。}}2つのコードの違いはNativeArrayに直接アクセスするか、配列にコピーしてアクセスするかです。【Editor】NativeArrayに直接アクセス【Editor】配列にコピーしてアクセス【Build】NativeArrayに直接アクセス【Build】配列にコピーしてアクセス要素数及びアクセス回数が多ければ多いほど、配列にコピーしてそちらを使うようにした方がいいです。【Build】NativeArrayコピーコピーのコスト合わせても、NativeArrayのインデクサーから各要素にアクセスするより安定して速いです。tmp[i];としてBuildして計測したところ、以下のようになりました。【Build】NativeArray要素へのアクセス回数を増やした【Build】コピー後の配列要素へのアクセス回数を増やしたコピー安定かなと思います。ちなみに、アクセス部分をunsafeコードに置き換えて以下のような感じにしてみます。Profiler.EndSample();}【Build】ポインタでアクセスコピーの必要がない上高速なので、使えるのであればガシガシ使っていきたいところ。uSpringBoneはガシガシ使う方針にしました。Debuggingをfalseにし、StandaloneでビルドしてProfilerで測定した結果、以下のようになりました。小さくて見辛いですが、NativeArrayへのアクセス速度はインデクサーを用いた一般的なアクセスが一番高速でした。BackendをMonoにし、先ほどと同様ビルド後に測定した結果です。アクセス速度はインデクサーが一番遅く、コピーした配列へのアクセスとポインタでのアクセスの方が速いという結果でした。要素へのアクセス方法を変更するのが良いかもしれません。因みにIL2CPPでのNativeArrayに関するパフォーマンスについてはJacksonDunstan.comにとても詳しく書かれています。IJobParallelForTransformについてIJobParallelForTransformを使う際には色々気をつけるべきことがあります。TransformAccessArray内のTransformが親子関係にあるかどうかでの違いについて。【Editor】親子関係がある場合WorkerThreadの使われ方に注目してください。親子関係がある場合、子は親のTransformに依存することになるので、1つのThreadしか使われません。関係がない場合は複数のThreadで処理してくれています。Editorでの実行結果のみ載せていますが、ビルドしても同様の使われ方になります。親子階層がめちゃんこ深いので、負荷も爆上がりしています。因みに兄弟でも1つのThreadしか使われないようでした。兄弟の場合でも、親子関係同様で1つのThreadしか使われません。親子関係に比べると負荷は大分マシです。Transform変更処理の負荷は階層構造が複雑であればあるほど重くなります。シンプルな構造にし、深くネストしないことが大事です。ちなみにIJobParallelForTransformなJobを複数扱う場合についてもメモしておきます。普通にいい感じですね。次は親子関係がある場合です。}}【Editor】親子関係があり、複数のJobを扱う場合Jobが分割されてはいますが、Transformへのアクセスが直列になります。具体的には、1つめのJob実行中は関連するTransformへのAccessがブロックされるため、2つのJobが並列に実行されません。あと親子関係が複雑で激重です。次は兄弟関係がある場合です。}}【Editor】兄弟関係があり、複数のJobを扱う場合これも同様ですね。そもそも系譜の違う2つのJobにした場合はどうなるでしょうか。}}【Editor】系譜が違うオブジェクトに対して複数のJobを扱う場合TransformAccessArrayに登録されているTransformの関係が疎であれば並列化できています。一言でまとめるとTransformAccessArrayに登録するTransformの階層構造に注意することが大切です。今回の例では、TransformAccess.rotationに対して回転計算を行い更新していました。TransformAccess.localRotationに対して更新を行なった場合には、親子関係が複雑であっても今回の例ほど負荷が高くなりません。Transformが保持しているのはローカル座標です。グローバル座標で値の更新を行う場合はオブジェクトの親のグローバル座標を求め、その値から自身の新しいローカル座標を計算する作業が必要になります。Transformの更新では、可能な場合はローカル座標を更新するようにしましょう。uSpringBone開発中に、複数のIJobParallelForTransformでシーンルートオブジェクトのTransformを更新しようとすると時々上記GIFのようにScheduleに非常に時間がかかる現象に遭遇しました(ビルド後の実行結果です)。原因が究明出来てないのですが、ScheduleするJobを1つにまとめることで回避出来ます。厄介なので、IJobParallelForTransformを扱う際には注意が必要かもしれません。NativeContainerへのポインタを使うJobで複数のNativeContainerを扱いたいけれど、その数が動的に変化するケースがあります。いったケースに遭遇した場合、NativeContainerへのポインタを取得して、ポインタをNativeContainerに詰めてJobへの入力としてやるのが楽です。uSpringBoneでは複数のJobで計算処理を実行後、計算結果が格納されている複数のNativeArrayへのポインタをTransform更新用の1つのJobに渡して更新処理を行っています。}}boneHeadPtrArrayは連結している各ボーンの先頭のボーンデータへのポインタが入っています(ボーンデータには計算後の位置・回転情報が入っています)。boneLengthArrayは連結しているボーンの連結数です。この2つの情報からGetDataPtr()で、現在変更しようとしているTransformAccessに対応するボーンデータを取得し、その情報を使ってTransformを書き換えます。tsubakit1.hateblo.jpScheduleのタイミングesprog.hatenablog.comこちらでも書いてますが、Jobはできる限りなるべく早くScheduleしてFlushしたほうがいいです(ただ、JobをFlushするのにもコストがかかるので、1つのJobをScheduleする度にFlushすべきではないです。用法・用量を守って正しく使用しましょう)。他の処理実行中にWorkerThreadが計算してくれます。Unityの各種イベント関数のはじめに実行したい場合、ScriptExecutionOrderを設定すると良いです。設定が面倒であれば、こういったものを利用しましょう。GitHubuSpringBoneでは、JobのScheduleを管理するクラスに上記コードを適用して、他のUpdate処理よりも前にScheduleをFlushさせるようにしています。具体的には以下のようなコードになっています。*********/PlayerLoopで云々も出来そうですが、特に理由がなければこちらは多用しないほうが良さそうです。さいごにJobSystemを利用する上で一番大事なのは、その設計です。少ないJobで最大限の効果を発揮し、MainThreadにかかる負荷を少しでも減らすよう心がけましょう。全部Uniteのせい。)はてなブログをはじめよう!es_programさんは、はてなブログを使っています。ブログを報告するスターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。