テラシュールブログ

旧テラシュールウェアブログUnity記事。主にUnityのTipsやAR・VR、ニコニコ動画についてのメモを残します。

【Unity】IJobParallelForTransformでTransformを並列処理する際でも、スレッドをフル活用する為のポイント

f:id:tsubaki_t1:20181114222538g:plain

今回はC# Job SystemでTransformAccessArrayを利用した際の、ジョブの分割数についてのお話です。

TransformAccessArrayが並列処理されない?

TransformAccessArrayは現行のGameObjectベースの処理を並列処理する上で非常に便利な機能です。上手く利用することで、Transformの座標取得や更新(実は意外とコストが高い)といった項目を別スレッドに逃がす事が期待できます。

さて以前に TransformAccessArrayIJobParallelForTransform の組み合わせでについての記事を書いたのですが、その際にジョブが必ず一つしか発行されていませんでした。
そうなると、単純にTransformを他の NativeArray<float3> に移すとかなら兎も角、普通に計算して云々すると一本に時間が集約されすぎて余り良くありません。Transformの結果を使うなり、結果を書き込むなりする場合でも、 処理完了まで詰まる 事になります。

f:id:tsubaki_t1:20181114224421j:plain

親が同じならば単一のジョブで実行される

回避方法ですが、非常に単純なものでした。端的にいえば Transformの親が同じならば単一のジョブで実行される というものです。 例えば上のジョブの実行結果ですが、アクセスを簡略化するためParentを用意し、 GetComponentInChildren を利用してアクセスしていました。

これを、transform.DetachChildren()で親子構造を解除してやった所、ちゃんと並列で動くようになりました。 処理はオブジェクト単位で実行されるので、処理が完了するまでの時間は短くなっています。

f:id:tsubaki_t1:20181114224629j:plain

f:id:tsubaki_t1:20181114224638j:plain

f:id:tsubaki_t1:20181114224432j:plain

ジョブの分割数を制限

ただし良いことばかりではありません。ジョブがコアを全て使って処理することは喜ばしい事ですが、ジョブは増えれば増えるほどに処理の効率は下がりますし、ジョブを発行する処理の時間が増加します。 例えばジョブが一つしか発行されていなかった時のジョブ発行は 0.015ms程度で収まっていたのですが、ジョブの分割数を上げる事で0.073ms程度まで肥大化しています。 また全体の処理時間も0.7ms程度だったのが1.29まで上がっています。
処理効率は処理完了までの時間とトレードオフ になるので、出来ればその辺りはコントロールしたい所です。

そこでTransformAccessArrayのコンストラクタの第二引数、desiredJobCountを設定してやることでジョブをN分割します。通常のIJobParallelForはバッチ数という…考え方が逆なので、そこんとこ注意です。
なおdesiredJobCountよりもルートの数の方が優先されます。

f:id:tsubaki_t1:20181114225529j:plain

f:id:tsubaki_t1:20181114234943j:plain

gist.github.com

複数のIJobParallelForTransformを発行するとどうなるの?

さて色々と並列処理を試していると、同一のTransformを参照しているTransformAccessArrayに対してIJobParallelForTransformを発行するとどうなるのか興味が出てきます。

結論としては、「同一のTransformを参照しているTransformAccessArrayを参照するIJobParallelForTransform」は、順次実行されます。これはTransformAccessArrayが同一インスタンスへ参照している場合も同様です。 例えば下のように2つのジョブを発行し2つ完了するまで待つ…とすると、片方が終わるまで待機するという形を取ります(2つ目の画像)。下の例ではジョブを一つにまとめていますが、複数に分割した場合は「同じTransformへアクセスするジョブが同時に走らない」だけでジョブはガンガン詰まります(3つ目の画像)。

f:id:tsubaki_t1:20181114231004j:plain

f:id:tsubaki_t1:20181114231240j:plain

f:id:tsubaki_t1:20181114231403j:plain

逆を言えば、同一のTransformAccessAray(ルートのTransform)のオブジェクトはどう頑張っても並列処理されません。そのため、一気に処理を行いたい場合には一旦NativeArray<Vector3>へ格納するようなジョブを発行し、一気に計算、最後に戻す…といった形のジョブを検討しても良いかもしれません。

f:id:tsubaki_t1:20181114233221j:plain

gist.github.com

関連

TransformAccess を使用するタイプです。

kou-yeung.hatenablog.com

ハイブリットECSのCopyTransformFromGameObjectを使用するパターンです。一旦Positionをコピーして最後に書き戻すタイプ

learning.unity3d.jp

前に書いたC# Job Systemの解説ですが、そろそろ書き直す必要があります(書き直しています)

tsubakit1.hateblo.jp