Checking another script on update/Efficiency

Hello All!

I currently have this setup, see below. I’ve done this for quite a few scripts, but have been wondering if there’s a better way, possibly more efficient with less memory usage?

	void Update ()
    {
        if (playerScript.points > 19)
        {
            if (!spawnedOnce)
            {
                 DoSomething();
            }
        }
	}

Also, just FYI, I’m requesting to find playerScript on Start(). So, what I’m wondering is, is there a better way to approach this? As it stands, it’s currently checking/requesting info from this script on another script on each Update(). Surely, I feel there’s a better way to do this. Thank you for your help!

Without knowing more about the design, I can only discuss generalities.

In order to perform the comparison like this (it appears to be on an integer), a CPU will load the operand in register, perform a partial subtraction (it discards the answer), then use the CPU flags to decide if the comparison operator results in a true or false statement.

Where it gets the value, and how, is what you’re really asking about.

If ‘points’ were a local or intermediate value, it may already be held in a register (as the compiler optimized), so the comparison just executes.

If ‘points’ were a local variable on the stack (declared at the start of the function and local to the function), there is a register dedicated to pointing to the stack already, which is used to fetch the value in one step (if it were not held in a register as above).

If ‘points’ were a member of ‘this’ class (the owner of Update), there is a presumed register or stack entry identifying the location in memory of ‘this’ object, where ‘points’ is known to be some distance from the start of that location - a vector calculating a distance from the top. The CPU would fetch the value from that calculated location. This could be as fast as the previous (local stack variable) if the optimizer loads the ‘this’ pointer for speed optimization. If ‘this’ had to be loaded for the memory access, this would be a two step process, one to fetch ‘this’, the second to fetch ‘points’.

Under the hood, playerScript is a smart pointer to an instantiation of class, with a numeric member ‘points’. While C# calls them references, they are an atomic value (64 bits on a 64 bit computer) identifying a memory location where the class that owns ‘points’ can be found (the ‘this’ pointer for playerScript in this case). The CPU may have to fetch playerScript’s location from the current object, then use that to calculate the location of ‘points’ within that object, making this up to 3 steps in the very worst case, but most likely 2.

This can happen tens of millions of times per second on a phone.

The impact can only matter if this were performed on the interior of a long loop, were thousands or millions of these calls might accumulate a measurable amount of time.

So, if ‘point’ were accessed inside a loop, it would be best to consider moving that loop to playerScript so it ran locally over there, on playerScript’s ‘this’ pointer, instead of from out here.

But for a single dereference like this, it is not worth effort to alter this when ‘points’ probably belongs in playerScript.

For another perspective on this, consider the modern cache on all major CPU designs. RAM is connected such that the CPU can read perhaps 20 to 60 GBytes per second on typical hardware. The internal cache of the CPU is wired to read perhaps 500 to 1000 Gbytes per second. Further, when RAM is instructed to switch to a new memory address, the delay is large, sometimes 10, 20 or 30 cycles of the machine. Cache has very little delay. In general operation, code is burst into cache, and looping code will run in cache for a while at high speed. Periodically, though, this gets exhausted because the code must branch to other functions not currently in the cache, so there’s a long stall. This happens throughout the execution of software at such speeds humans can never witness it, so we experience the average overall throughput. My point is that the cost of the dereferencing of ‘points’ vs a local member variable of the same type vs a stack allocated local variable is likely already burst into cache, and may be on the order of 2 or 3 cycles, while other things over which we have very little control can impose 10 or 20 cycles of delay. That said, if ‘points’ is in RAM not cached recently, that dereference can hit a 20 cycle delay, where a local value would be maybe 2 or 3. However, any reconstruction of your code is likely to cause the same compromise from an opposing perspective. Update, of code this small, runs a grand total of a few dozen cycles at most, and then only at frame flips. There will be a billion cycles of CPU work done between frames before this code becomes a performance cost at all.

If it were inside a loop that took a few hundred thousand cycles, it would be worth considering a refactor.

I would say it depends on the frequency you pass the 19 mark. In my opinion an event in the other class, called when points change, is less noisy. furthermore, since you’re trying to spawn something just once, you can then just unregister from the call and nothing is executed anymore.
when it comes to performance, absolutely insignificant.