在回收价值类型统一是否有一点

时间:2016-10-26 03:22:51

标签: c# optimization unity3d stack heap

我发现文章指出回收和重复使用变量是团结的良好做法。所以我采纳了它。 但有一点不清楚:这是否适用于值类型变量(整数,向量)?

有一点我使用它:

int x;
Vector3 v;
void functionCalledVeryOften(){
   x=SomeCalculation();
   v=SomeCalc();
   //do something with x and v
}

而不是:

void functionCalledVeryOften(){
   int x=SomeCalculation();
   Vector3 v=SomeCalc();
   //do something with x and v
}

2 个答案:

答案 0 :(得分:5)

  

回收价值类型统一是否有意义

是的,有些数据类型不是全部。

  

这适用于值类型变量(整数,向量)吗?

这取决于变量类型。

适用于intdoublefloatboolVector3Vector2和其他类似的数据类型。它甚至不适用于string因为已经,string无法在C#中重复使用。 strings是不可变的。

事实上,使用本地变量中的int,假设在while循环中比使用声明为全局的int更快。

*何时应该声明变量一次并重复使用它或用您自己的话说,在Unity *中回收或重复使用变量的示例。

<强>阵列

如果函数包含数组,并且通常会调用该函数。

void functionCalledVeryOften()
{
    float[] playerLives = new float[5]; //This is bad because it allocates memory each time it is called
    for (int i = 0; i < playerLives.Length; i++)
    {
        playerLives[i] = UnityEngine.Random.Range(0f,5f);
    }
}

每次分配内存,可以通过使数组全局并在函数外部初始化一次来解决。您可以创建一个简单的函数,将数组中的数据重置为0。

float[] playerLives = new float[5]; 
void functionCalledVeryOften()
{
    for (int i = 0; i < playerLives.Length; i++)
    {
        playerLives[i] = UnityEngine.Random.Range(0f,5f);
    }
}

创建新对象

创建新对象会占用资源,并可能导致移动设备出现问题。这取决于你这样做的频率。

下面的代码创建了一个GameObject(项目符号),然后将Rigidbody附加到它上面然后进行拍摄。这种情况发生在每一帧,同时按住空格键,最后在10秒后摧毁子弹。 / p>

void functionCalledVeryOften()
{
    if (Input.GetKey(KeyCode.Space))
    {
        //Create new Bullet each time
        GameObject myObject = new GameObject("bullet");
        Rigidbody bullet = myObject.AddComponent<Rigidbody>() as Rigidbody;
        //Shoot Bullet
        bullet.velocity = transform.forward * 50;
        Destroy(myObject);
    }
}

上面的代码很糟糕,因为每次创建新的GameObject时都会分配内存,当GameObject被销毁时,它也会触发垃圾收集器。这可能会减慢并导致游戏中的打嗝。

上述代码的解决方案是对象池。您可以在此处了解详情:Object Pooling tutorial from Unity

使用全局变量进行简单修复的示例

List<GameObject> reUsableBullets;
int toUseIndex = 0;

void Start()
{
    intitOnce();
}

//Call this function once to create bullets
void intitOnce()
{
    reUsableBullets = new List<GameObject>();

    //Create 20 bullets then store the reference to a global variable for re-usal
    for (int i = 0; i < 20; i++)
    {
        reUsableBullets[i] = new GameObject("bullet");
        reUsableBullets[i].AddComponent<Rigidbody>();
        reUsableBullets[i].SetActive(false);
    }
}

void functionCalledVeryOften()
{
    if (Input.GetKey(KeyCode.Space))
    {
        //Re-use old bullet
        reUsableBullets[toUseIndex].SetActive(true); 
        Rigidbody tempRgb = reUsableBullets[toUseIndex].GetComponent<Rigidbody>();

        tempRgb.velocity = transform.forward * 50;
        toUseIndex++;

        //reset counter
        if (toUseIndex == reUsableBullets.Count - 1)
        {
            toUseIndex = 0;
        }
    }
}

所以基本上,你在游戏开始之前在函数内创建一个Object,然后将引用存储在 global 变量中。然后,您将重新使用您在函数中创建的对象,因为它的引用保存在全局变量中。

<强>实例化

Instantiate功能用于创建预制件的副本。 下面的代码将实例化一颗子弹,然后每按一次射击它,同时按住空格键,最后在10秒后将其销毁。

public GameObject bulletPrefab;
void functionCalledVeryOften()
{
    if (Input.GetKey(KeyCode.Space))
    {
        //Create new Bullet each time
        Rigidbody bullet = Instantiate(bulletPrefab, new Vector3(0, 0, 0), Quaternion.identity) as Rigidbody; 
        //Shoot Bullet
        bullet.velocity = transform.forward * 50;
        Destroy(myObject,10f);
    }
}

上面的代码很糟糕,因为它根据子弹预制件附加的组件数量以及游戏对象下面的子游戏数量来分配内存。该解决方案还使用对象池。在函数中实例化GameObject,将引用存储在 global 变量中,然后重新使用它们。解决方案与上述解决方案相同。

总之,您问题中的示例代码不适用于此

您可以在Unity here中了解有关内存管理的更多信息。

答案 1 :(得分:2)

这完全取决于您希望如何处理此对象。

让我们举一个例子,说我们想要访问变量x&amp; v来自第二个函数functionCalledEveryOnceSoOften()这个函数不需要任何重载来传递变量,并且可以直接访问类实例中的变量。

使用第二个例子,如果我们想做同样的事情。我们必须调用functionCalledEveryOnceSoOften(int, vector3)因为函数不能直接访问变量。

在统一中,通常情况是函数需要使用与另一个函数相同的值,尽管它们可能并不总是在链中被调用。为了适应你的第二个例子,我们必须在我们的函数中添加if语句来过滤它。

然而,在您的第一个示例中,我们可以使用这些变量而不会出现问题。这是通常建议这样做的原因之一。

根据性能,在第二个示例中,变量存储在堆栈中而不是堆中,因为它是在方法的范围内定义的,一旦方法结束执行,它将被销毁。因此,变量的内存使用并不是真正的问题。重复创建和销毁的开销可能很小,但这应该是微不足道的。

在第一个示例中,您将变量存储在堆上,因为它是在类的范围内定义的,它只会与类一起销毁,并在其实例上创建。这意味着内存可能会在更长的时间内使用,但创建/销毁变量不会产生任何开销。这通常也是微不足道的。

总之,除非您实例化数千个这些对象,否则快速连续访问变量,您很可能不会注意到性能上的很多差异。

最大的区别很可能是编写代码的方式。不论结果好坏。