先日#UnityTipsにて、中々に面白いTipsが紹介されました。
#UnityTips object.ReferenceEquals(someObject, null) is faster than someObject == null.
— Mike Geig (@mikegeig) 2015, 5月 26
#UnityTipsとは
火曜日の大体夕方から夜にかけて、#UnityTipsのタグを付けてTipsを自慢する奴です。
#unitytips : You can edit Stage and UI same time. Add scene tab. pic.twitter.com/lEmYr8kMiE
— 椿 (@tsubaki_t1) 2015, 5月 26
object.ReferenceEquals(someObject, null) の方が someObject == null より早い?
早速試してみたところ、中々に興味深い結果が出ました。
下の図は、上のコードを64bitPC向けにビルドしたアプリ上で10000000回実行した結果です。
興味深いことに大体一桁近い違いが出ています。
ただ、この規模で実行してこの程度しか変化がないので、正直書き換える必要があるかと言われると微妙な気がします。
毎フレームnullチェック入れたり数万回nullチェックするのは、そもそも設計が間違っている気がしますし。
何故そんな事が起こるのか
これについては以下の記事で紹介しています。
要するに「Unityのオブジェクトは実はC++で管理されており、C#はアクセス用ポインタのみを持つので、DestoryでC++のオブジェクトを削除してもC#的にはnullと判定されない」という所が問題になるみたいです。なので「== オペレーターを拡張して両方チェックするようにした」との事。これが単に参照のみをチェックするReferenceEqqualとの違いみたいです。
例えばオブジェクトをDestroyしたオブジェクトの確認を行う場合、 == オペレータではnullと判断してくれますがobject.ReferenceEquals(someObject, null) では null と判定されません。
つまり?
個人的に紹介しといて何ですがこれは
事故のもとな気がします。
安全に運用するには、Destroyしたオブジェクトは自分でちゃんとnullで埋める、Destroyされる可能性のあるシングルトンには使わない、使用はエディタを使いシーン上で設定するオブジェクトへの参照に限定する、…といった事が必要かなと。
パフォーマンスって言葉は魔法みたいですが、実際はほんとうに怖い。
ちなみに
オブジェクト、Destroy直後はnullになりません。同一フレームでnullにしたければDestroyImmediateを使用します。
またカスタム演算子が??に使えないので、??はC#的な判定(つまりDestroy後nullにならない)が発生します。