5

I found article stating that recycling and reusing variables is good practice in unity. So I adopted it. But one thing not clear : does this apply to value type variables (integers, vectors)?

is there a point i using this:

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

instead of this:

void functionCalledVeryOften(){
   int x=SomeCalculation();
   Vector3 v=SomeCalc();
   //do something with x and v
}
trincot
  • 317,000
  • 35
  • 244
  • 286
ShoulO
  • 448
  • 2
  • 7
  • 20
  • There is no 'value type recycling' going on there: it is just using Fields vs. Local Variables. *Variables* are usually not 'recycled' in this sense and it is usually *objects* that are pooled/recycled (to minimize allocations - and hiccups! - and resource management overheads). – user2864740 Oct 26 '16 at 03:25
  • It saves you some stack memory. – Niyoko Oct 26 '16 at 03:25
  • I'm not entire sure "saves" is the correct term there.. – user2864740 Oct 26 '16 at 03:33

2 Answers2

6

Is there a point in recycling value types unity

Yes, some datatypes not all.

does this apply to value type variables (integers, vectors)?

No.

It depends on the variable type.

This does not apply to int, double, float, bool, Vector3 and Vector2 and other similar datatypes. It does not even apply to string because already, string cannot be re-used in C#. strings are immutable.

In-fact, using int from a local variable, lets say in a while loop is faster than using int declared as global.

*Examples of when you should declare variable once and re-use it or in your own words, recycle or re-use variables in Unity*.

Arrays:

If a function contains array and that function is often called.

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);
    }
}

This allocates memory each time and can be solved by making the array global and initializing it outside the function once. You can create a simple function that resets the data in the array into 0.

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

Creating new Objects:

Creating new Objects takes resources and can cause problems on mobile devices. This depends on how often you do this.

The code below creates a GameObject (bullet) then attaches Rigidbody to it and then shoots it.This happens every frame while space bar is held down and finally destroys the bullet 10 seconds later.

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);
    }
}

The code above is bad as it allocates memory each time new GameObject is created and when the GameObject is destroyed, it will also trigger garbage collector. This can slow down and cause hiccups in your game.

The solution to the above code is Object pooling. You can learn more about it here: Object Pooling tutorial from Unity

Example of simple fix for this with a global variable:

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;
        }
    }
}

So basically, you create an Object inside a function before Game begins, then store the reference in a global variable. You will then re-use that Object you created in the function since its reference is held in a global variable.

Instantiate:

The Instantiate function is used to create of copy of a prefab. The code below will instantiate a bullet then shoots it every frame while space bar is held down and finally destroys it 10 seconds later.

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);
    }
}

The code above is bad as it allocates memory depending on how many components is attached to the bullet prefab and how much child GameObject is under it. The solution is also use Object Pooling. Instantiate the GameObject in a function, store the reference in a global variable then re-use them. The solution is the-same with the solution above.

In conclusion, the example code in your question does not apply this.

You can learn more about Memory Management in Unity here.

Graham
  • 7,431
  • 18
  • 59
  • 84
Programmer
  • 121,791
  • 22
  • 236
  • 328
2

This is fairly dependent on what you wish to do with this object.

Lets take your first example, say we would want to access the variables x & v from a second function functionCalledEveryOnceSoOften() This function won't need any overloads to pass the variables, and can just directly access the variables in the instance of the class.

With the second example, if we wanted to do the same thing. We would have to call functionCalledEveryOnceSoOften(int, vector3) As the function would not have direct access to the variables.

In unity it is often the case that a function will need to use the same values as another function, all though they might not always be called in chain's. To accommodate this in your 2nd example, we would have to add if statements inside our function to filter this out.

In your first example however, we could use these variables without a issue. This is one of the reasons it is often advised to do so.

As per the performance, in your 2nd example the variable is stored in the stack as opposed to the heap, because it is defined within the confines of a method which will get destroyed once the method ends executing. So the variable's memory usage is not really a concern. There might be a small overhead for the repeated creation and destruction, but this should be insignificant.

In your first example you will store the variable on the heap, as it is defined within the scope of the class, it will only be destroyed along with the class, and created on it's instantiation. This means that memory might be used over longer periods of time, but there will be no overhead for creating/destroying the variable. This also is usually insignificant.

All together, unless you are instantiating thousands of these objects, accessing the variables in rapid succession you will most likely not notice a lot of difference in performance.

The biggest difference will most likely be the way code is written. For better, or for worse.

MX D
  • 2,453
  • 4
  • 35
  • 47
  • i wonder does repeated variable creation and destruction on stack produce more battery usage/heat ? – ShoulO Oct 26 '16 at 11:34
  • 1
    @ShoulO Personally I haven't noticed a difference in heat or battery usage in any of my projects, when a is used over b or vice versa. Actually, it hasn't even made a significant enough impact for me to even consider optimizing it. 9 out of 10 times there is a way more significant and impact full bottleneck located elsewhere – MX D Oct 26 '16 at 13:42
  • 1
    @ShoulO No. Local *variable* access is more efficient. In addition, the objects that the variables represent might not even be "on the stack". The purpose of using a field member is to allow *object* re-use or sharing. Object re-use does not apply to value types that only contain value types. – user2864740 Oct 28 '16 at 04:54