Does reading/changing Static variables constantly affect Performance or get Garbage Collected?
From the last question that i asked, @Bunny83 said:
GUI.enabled
is just a state stored in an internal field. There is no overhead involved setting that state.
So GUI.enabled
is a static bool, and i think static variables only have 1 reference, so when you access/change it somewhere, it will be accessed/changed for the others. So i'm mostly talking about Performance Improvement, does the last line improve performance at all?
bool enabled = false;
//Set it to (enabled) bool
GUI.enabled = enabled;
//Check if it isn't equal to (enabled) bool, so it isn't necessary to change it to the same value if
//it is already (false), which it is equal to (enabled) bool, so the condition won't pass
if(GUI.enabled != enabled)GUI.enabled = enabled;
Also Event.current
is a static variable as well, so if i do:
Event current = Event.current;
if(current.button == 0)
print("Left Mouse Button is pressed);
Does the (current) variable that i just created affect performance? or get garbage collected?
Event.current.button
is a public non static variable, so it has a reference to itself, so caching it into a variable is a good practice to increase performance?
Also GetComponent()
isn't static as well, people suggest caching it, so it looks like Event.current.button
should be cached when it gets used a lot!
And List.Count
isn't static too, so i ran 2 different tests showing the difference between a cached List.Count
and a non cached List.Count
and it turned out that the cached List.Count
is way too much faster than a non cached List.Count
.
So can somebody explain it to me or confirm if it is true?
Thanks.
[EDIT] Just for your info, i have experience of about 3 years in Unity3D, so you don't need to explain the very basic stuff, i just talk about the Performance, so yeah..
Any time you create a reference to something, C# is creating an int pointer to that object which takes up 4 bytes. That's not really something to worry about.
edit: In case you are new to this or C# in general, your line Event current = Event.current;
is creating a reference to an existing object already referred to as Event.current
. This happens because Event is a Unity class and classes in C# only exist in a single location. For things like structs and other value-types, those values are copied entirely.
I know how it works, but i meant Performance, is it good or bad to cache a static existing variable like Event.current
?
Let me make a comparison, which one of them runs faster or is better for performance?
[1] Non cached Static Variable
if(Event.current.button == 0)
print("Left $$anonymous$$ouse Button is pressed);
[2] Cached Static Variable
Event current = Event.current;
if(current.button == 0)
print("Left $$anonymous$$ouse Button is pressed);
Or maybe if i put these lines in a for()
loop, that loops about 1000 times, which one will do better in Performance and speed?
In that example, the Event instance Event.current
would be caught by GC if nothing referenced it at the next GC cycle. If you are using a temporary variable to spare yourself from typing out Event.current each time, then you're adding 4 bytes. I don't think you are going to see any noticeable performance difference, but run it a billion times if you want to.
For the question, is it good or bad to cache a static existing variable,
your concern should typically be whatever makes it easiest for yourself and others to read / maintain. If you and your $$anonymous$$m prefer just seeing "current" ins$$anonymous$$d of "Event.current", then go ahead and do it. There is no tangible downside to doing this for readability.
Answer by Bunny83 · Jan 19, 2018 at 09:07 PM
First of all you do put way too much efford into micro optimisations here. As i said in the enabled case it's complete nonsense. Yes GUI.enabled is a property and therefore actually a pair of two methods (a get and a set method). Of course calling those methods which actually call a function in the C++ engine core has a slight overhead. However just reading the property has the same overhead compared to writing to it. So if you check the current state before you set it you actually make the performance worse.
Event.current is a static property just like GUI.enabled. However unlike the enabled property which is a boolean value, the current property is of type "Event" which is a class and therefore a reference type. Storing the same reference in a local variable and using the local variable has only a minimal improvement on the access time. The main reason why you usually cache the current Event at the top is because it makes the code more readable as the variables gets shorter.
I highly doubt that you will access current.button that many times that its even in the range that the difference can be measured. Local variables do not get garbage collected. Local variables are allocated on the stack when you enter the method.
List.Count is also a property. Therefore reading the value has a slight overhead since it's actually a method call. However testing such things in the editor can lead to wrong results since in a built game the JIT compiler can optimise getter methods by inlining them. I also run test on the performance of List.Count and compared to a local variable the rough difference is that a local variable is about two times faster. This is not even worth mentioning. Pretty much anything you actually do with your list elements will be several times slower than the for loop.
Actually caching the Length of an array can actually have a negative effect on certain optimisations. Reading the length of a native array is actually a seperate IL opcode (ldlen).
You seem to be heavily affected by permature optimisation. For example as i said caching the count of a list decreases the access time of the count variable by a factor of 2. However if your loop actually draws each item, the drawing will probably be 95% - 99% of the time spent per iteration. So changing to a cached count most likely can't be even measured. Especially if you just have a few elements.
One other point to mention regarding List.Count()-- flexibility: If you add or remove items from your List inside the loop, List.Count will CHANGE, and return the correct value. Using a cashed value mean you would need to adjust that manually (which the list is already doing for you, anyway).
Not mentioned: Caching the result of some kind of search operation like GetComponent(), or List.Contains(), (rather than making it re-run the search routine by calling it again), IS usually a good idea. Alas, there is no way, that I know of, other than testing or (sometimes) common sense applied to the function name, to deter$$anonymous$$e if a long routine is being called, or not. To add to the difficulty of figuring it out, some classes may even cache certain long-routine results internally.
According to a test that i did about 5 $$anonymous$$utes ago, it looks like caching the List.Count
is better than using List.Count
again and again, here is the test script:
private void Update ()
{
var list = new List<$$anonymous$$aterial>(1000);
for(int a = 0; a < 1000; a++)list.Add(null);
var sw = new System.Diagnostics.Stopwatch();
sw.Reset();
sw.Start();
for(int a = 0; a < list.Count; a++)
{
}
print("NON CACHED: " + sw.ElapsedTicks);
sw.Reset();
sw.Start();
for(int a = 0,countA = list.Count; a < countA; a++)
{
}
print("CACHED: " + sw.ElapsedTicks);
sw.Reset();
}
Having a List of $$anonymous$$aterials with the count of 1000, the results:
The Cached result is about 94 ticks, but the Non Cached result is about 243 ticks, so the Cached result is about 62% ticks faster than the Non Cached result, which is good i think :D
There is a 149 tick difference between them, so it is a lot, 1 tick is important!
So isn't this type of optimization important? reducing unnecessary method calls and variables will increase performance and speed, right?
since you have it setup to test.. what happens if you call list.Remove(a) inside the loop? (You will obviously need to adjust your cashed value also.)
You might also try adding some medium length-operations inside the loop, so you can see how much TOTAL performance improvement the cache would yield in those situations.
Take that to the next step, (Bunny and Owens point), if you, test the different methods in a scene with a few complex meshes, and rigid bodies, you probably won't even be able to detect the TOTAL improvement, at all.
Yes, this kind of stuff DOES improve performance but can lead to longer development time, particularly if one's assumptions lead to unexpected results.
Well, if i want to call List.RemoveAt(a)
, i will simply do:
bool remove = false;
for(int a = 0,countA = list.Count; a < countA; a++)
{
//You will need to set the (remove) bool to true and it will do everything for you
if(remove)
{
list.RemoveAt(a);
countA--;
}
}
If i want to change something in the list, i will simply apply it to the cached variable once.
I feel like the atmosphere in UA is responsible for many of these worries. So many beginner Qs get unrelated comments about caching the transform, never using GameObject.Find, using an object pool, changing magnitude to sqr$$anonymous$$agnitude .. .
But i think they're right!
Because calling expensive $$anonymous$$ethods and changing a variable constantly to the same value will affect performance, and it wouldn't be necessary to do that!
For example let's say i have a variable, which is Integer, in Update() $$anonymous$$ethod, if you do:
public int myInt = 0;
void Update ()
{
myInt = 1234567890;
}
It will set (myInt) to (1234567890) each frame, so it isn't necessary to do that if it is already (1234567890)!
Which can be optimized like:
public int myInt = 0;
void Update ()
{
if(myInt != 1234567890)
myInt = 1234567890;
}
So they're right, you might want to reduce unnecessary changes to Increase Performance!
I did a test for GUI.enabled, the results:
[1] Un-conditional -> 368 - 410 Ticks
[2] Conditional ------> 385 - 453 Ticks
I don't know why the conditional one is higher in this test, but i guess static variables can be changed freely without checking if they're already equal to a value or not.
Also i did a test for a cached Event.current
the results:
[1] Non-cached -> 1215 - 2082 Ticks
[2]Cached --------> 812 - 894 Ticks
So it looks like caching List.Count
, Event.current
, etc.. will improve performance, but just a few ticks, or maybe a lot.
According to all of the tests that i did, for performance, it looks like this:
-To Change Static variables, you shouldn't check for the previous value, you should change it constantly, like:
bool enabled = false;
//Sets it to (false)
GUI.enabled = enabled;
//Sets it to (false) again, without checking if it was previously (true) to change it to (false)
GUI.enabled = false;
-To Read Static Variables, you should cache it, like:
Event current = Event.current;
if(current.button == 0)
print("Left $$anonymous$$ouse Button pressed");
-To Iterate through Lists/Arrays, you should cache the List.Count/Array.Length(Array.Length might not be necessary to cache, it won't affect performance that much), like:
for(int a = 0,countA = list.Count; a < countA; a++)
So i hope this helps.
Your answer
Follow this Question
Related Questions
Static variable resetting when it shouldn't! 0 Answers
Object reference not set to an instance of an object 0 Answers
Change value of a static variable multiple times in one script 2 Answers
a static variable resets whenever i enter again the update method 0 Answers
How do I reset a static gameObject? 1 Answer