テラシュールブログ

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

【Unity】独自に作成したスレッドの処理時間をCPU Profiler(Timeline)で確認する

まんまタイトルの通りで、.NETで作成したスレッド上の処理時間をProfilerに乗せる方法についてです。

 

 

Profilerで負荷のかかっている場所を見つける

処理負荷や処理時間を知るのに、Profilerは非常に有用なツールです。
パレートの法則で言われるように、大抵の負荷は2割がボトルネックになっている事が多く、Profilerはその2割を見つけるのに有効です。

f:id:tsubaki_t1:20180430232723j:plain

さて、Profilerに処理時間を乗せたい訳ですが、特に何も設定しないならばMonoBehaviour.UpdateやECSのSystemといった単位でCPU Profilerに計上されます。
これでは「◯◯コンポーネントが遅い」事は解っても、何が問題とはハッキリ出来ません。

そこでProfiler.CustomSamplerで特定の処理時間をピックアップします。
サンプルとしてVector3を最後にApplyするだけのモノと、transformを取得してユニットを動かす場合、transformをキャッシュして行う場合の処理時間を見てみます。

gist.github.com

結果はProfilerのTimelineで確認できます。確認すると、Vector3だけ取り出しておいて最後に反映させるモノが、Transformに毎回数値を入れてるモノよりとても速い事が確認出来ます。
transformのposition等を上書きするだけでもソレナリのコストが発生していることです。あとgameObject.transformが内部的にキャッシュされるというのは迷信だと予想出来ます。

f:id:tsubaki_t1:20180501132033j:plain

ちなみにBeginSampleではなくCustomSamplerを使用しているのは、こちらのほうがオーバーヘッドが少なく、正確な値が取れるからです。

 

非同期処理とProfiler

負荷を計上するProfilerですが、実はスレッドが関わってくると途端に面倒になってきます。
C# Job SystemであればC# Job System側である程度の処理時間がProfilerのTimelineに出力されるので、そこまで致命的な問題にならないのですが、.NETの機能で作成したThreadはUnityのProfilerに計上されない為、どの程度の時間がかかっているのかが把握しにくくなっています。
またBeginSampleはメインスレッド上でしか動作しません。

 

この問題を解決するのが、Profiler.BeginThreadProfilingです。このAPIで囲った範囲のsampler.Begin~sampler.Endの範囲はProfilerのTimelineに乗るようになります。
例えば下のようなコードを記述すると、Profilerから処理時間が確認できます。

gist.github.com

f:id:tsubaki_t1:20180501135619j:plain

またsampler.Beginの引数にgameObjectをセットすれば、Profiler Timelineに計上される処理時間とHierarchy上のGameObjectを紐付ける事が出来ます。

 

複数フレームを跨げる

この処理時間は複数のフレームを跨いでもOKで、特にコルーチンのような遅延処理に対してThreadを使用する場合には有用そうです。
ただバージョンによっては載らない事もあるかもしれません。以前はsampler.Endまで到達した処理が無ければ載らなかった気がするので(Unity 2018.1f1で確認)

f:id:tsubaki_t1:20180501220611j:plain

逆にCPU ProfilerのHierarchyに処理時間が載らない点ので、その勘違いは気をつける必要があります。

 

関連

async/awaitのコードはココのを真似させてもらいました。

satoshi-maemoto.hatenablog.com

samplerについての説明。BeginSamplerは止めよう。
どちらかと言えばProfilerで見るような物をゲーム画面に乗せられる方が重要

tsubakit1.hateblo.jp

Profiler便利便利。

tsubakit1.hateblo.jp