[ZScript] new(object) vs Performance

Tue Feb 01, 2022 7:44 am

Hello, can anybody shed some insight on the performance issues that might come along with this piece of code?

My main issue right now is creation of a new ArrayShuffler instance each second. I'm trying to streamline my code and couldn't really find much info on the new() method. Will this be safe to use in such a context? Will the object created with new() be destroyed and cleaned up after EachSecond()? I don't want to clog the memory or waste resources, and rewriting the array shuffling code in multiple places just feels wrong. If there's anything else you'd change in the code you see below - please let me know! Thank you for your time and effort.

Code:
class ExampleClass : Actor
{
    int internalTick;
   
    override void Tick()
    {
        Super.Tick();
       
        internalTick += 1;
       
        if (internalTick >= 35)
        {
            internalTick = 0;
            EachSecond();
        }
    }
   
    void EachSecond()
    {
        Array<int> intArray = {0, 1, 2, 3};
        ArrayShuffler aShuffler = new("ArrayShuffler");
        aShuffler.intArray.Copy(intArray);
        aShuffler.ShuffleInt();
        intArray.Copy(aShuffler.intArray);
    }
}

class ArrayShuffler
{
    // WHAT IS MY PURPOSE?
    // YOU SHUFFLE ARRAYS.
    // OH MY GOD.
   
    Array<int> intArray;
   
    void ShuffleInt()
    {
        if (intArray.Size() > 1)
        {
            for (int i = 0; i < intArray.Size(); i++)
            {
                int rID = random(i, intArray.Size() - 1);
                int rOrg = intArray[i];
               
                intArray[i] = intArray[rID];
                intArray[rID] = rOrg;
            }
        }
    }
}

Re: [ZScript] new(object) vs Performance

Tue Feb 01, 2022 8:00 am

You can put ArrayShuffler into ExampleClass:
Code:
class ExampleClass : Actor
{
    int internalTick;
    ArrayShuffler aShuffler;

and then make a new only if it doesn't exist yet:
Code:
void EachSecond()
    {
        ...
        if (aShuffler == NULL) aShuffler = new("ArrayShuffler");
        ...
}


Also, you may want to use Move instead of Copy, it's a cheaper operation:
Code:
 void Move(array<T> other)
    Moves all of the elements of other into this array, emptying the original array completely.


Edit: to answer your questions: yes, an object created in a function will be eventually destroyed, and memory will be cleaned up by garbage collector. It will cost a little bit cost of performance each time, though, so it's recommended to avoid creating objects repeatedly if you can cache them somewhere.

Re: [ZScript] new(object) vs Performance

Tue Feb 01, 2022 8:23 am

Thank you very much for your insight on this.

I was initially thinking about referencing only one instance of the ArrayShuffler at the beginning and reusing it every time without creating new instances - but! My fear was that the way it's written now it would override its internal intArray value if used recursively or without retrieving the shuffled value before using it again in the same function. Same thing with array.Move(). Sure, it could and should be used like you mentioned in cases that don't need to worry about such intense array juggling. Maybe creating an individual ArrayShuffler once for different use cases and storing those in class references could be a (bit messy name-wise) middle ground solution...

Anyway, thank you for your fast response! It cleared things up and made me understand the new() method a bit more for future uses.

Edit: Ended up using one static reference to the ArrayShuffler instance, created at PostBeginPlay() and the array.move() method wherever I can.

Re: [ZScript] new(object) vs Performance

Thu Feb 03, 2022 10:44 pm

Quick tip: You can avoid that internalTick thing if you just use a modulus:
Code:
if (level.time % 35 == 0){EachSecond();}

level.time is the count of ticks since you started playing this hub, so modulus it by 35 will break it down by seconds.

Re: [ZScript] new(object) vs Performance

Tue Feb 08, 2022 1:09 pm

@SirRobin, a very curious, nice snippet, thank you! Might use it in some cases, but

Code:
if (!actPlayer.paused)
{
    internal.tick += 1;
   
    if (internal.tick >= 35) { EachSecond() };
}


Allows me to introduce a custom state of "pause" for certain game elements during some special menus / cutscenes, while I doubt I could pause level.tick. Either way, great stuff!