unity3d 使用“if(gameobject == null)"的代价昂贵吗?

q5lcpyga  于 2023-01-05  发布在  其他
关注(0)|答案(2)|浏览(442)

我通常使用if ( gameobject == null ),但我听说这是非常昂贵的游戏。
我检查了if ( gameobject == null )if ( boolvalue == false ),但没有发现任何差异。
你能解释一下为什么这张支票很贵吗?先谢谢你。

5gfr0r5j

5gfr0r5j1#

一般来说,像这样的问题可以归入“意见”类别,但在这种情况下,我们可以通过运行一个小测试来量化答案。
事实上,这里发生了一些事情,还有一个更深层次的问题。
我将从测试代码开始:

public class Test : MonoBehaviour
{
    public GameObject prefab;

    private IEnumerator Start ( )
    {
        var go = Instantiate(prefab);
        Destroy(go);
        Debug.Log ( $"go is null: {go is null}. go == null: {go == null}." ); // False False.
        yield return null; // skip to next frame
        Debug.Log ( $"go is null: {go is null}. go == null {go == null}." ); // False True.
        yield return null; // skip to next frame

        var sw = new Stopwatch();
        sw.Start ( );
        for ( var i = 0; i < 10_000_000; ++i )
            _ = go is null;
        sw.Stop ( );
        Debug.Log ( $"go is null: {sw.ElapsedMilliseconds} ms" ); // Test machine : ~10 ms

        sw.Reset ( );

        sw.Start ( );
        for ( var i = 0; i < 10_000_000; ++i )
            _ = go == null;
        sw.Stop ( );
        Debug.Log ( $"go == null: {sw.ElapsedMilliseconds} ms" ); // Test machine : ~4000 ms

        yield return null; // skip to next frame
        Debug.Log ( $"go is null: {go is null}. go == null {go == null}." ); // STILL False True.

#if UNITY_EDITOR
        EditorApplication.ExitPlaymode ( );
#endif
    }
}

因此,首先要注意的是,在示例化并立即销毁一个对象之后,在该特定帧中,该对象在托管或非托管“内存”中均未被“销毁”,如“False False”所示。
然而,当我们跳到下一帧时,你会注意到unmanaged(Unity后端处理)已经将对象标记为null。尽管有一个问题,托管代码仍然看到go引用了一个对象,它有点不再是一个有效的后端对象。这就是为什么下一个调试行将为null检查指示“False True”。
我提出这个问题的原因是因为一些开发人员错误地认为短路和绕过重载的'=='操作符是好的。它在大多数情况下都有效,对吗?很明显,它没有,正如这里所演示的。
那么,现在来看看使用“==”的时机。使用object == nullobject is null之间有着巨大的差异。正如您所看到的,使用“==”要慢400倍。这是因为Unity重载了“==”操作符,不仅要检查引用是否为空,还要检查底层后端对象是否也为空。这是一个代价高昂的过程(相对而言),这就是为什么人们说要避免它。但是,仅仅因为其他东西更快,并不能使它更好,在这种情况下,使用更快的检查可能会导致错误。
解决方案是自己跟踪托管引用。要做到这一点,您需要执行以下操作:

var go = Instantiate(prefab);
Destroy(go);
go = null;
Debug.Log ( $"go is null: {go is null}. go == null: {go == null}." ); // True True.

如果每次销毁Unity对象时都将引用设置为null,则可以使用object is null安全地检查引用的null状态,object is nullobject == null快得多。

*说了这么多

请注意,为了在StopWatch中获得任何有意义的毫秒值,我不得不循环检查1000万次!这就引出了我的最后一点......除了担心“==”是否昂贵之外,还有很多其他地方可以提高游戏的性能。使用它,并专注于代码的其余部分。

k5hmc34c

k5hmc34c2#

对支票昂贵的回答如下:
“转换到本机代码的成本可能很高,因为Unity将执行查找和验证,以将脚本引用转换为本机引用。比较Unity对象与null的成本虽然很小,但比与普通C#类进行比较的成本高得多,因此应避免在性能关键上下文和循环中使用。”

**尽管如此,我还是听到许多开发人员同事说,检查根本不是很密集,除非是垃圾邮件或大量使用。**只有当您的项目性能开始下降时,您才应该真正担心它。

但是,该代码的一个不太密集的版本如下所示:

if (!System.Object.ReferenceEquals(gameObject, null)) {

//使用gameObject执行操作}
以下是两个答案和进一步解释的链接:
https://github.com/JetBrains/resharper-unity/wiki/Avoid-null-comparisons-against-UnityEngine.Object-subclasses
https://subscription.packtpub.com/book/game+development/9781785884580/2/ch02lvl1sec23/faster-gameobject-null-reference-checks

相关问题