GameMaker Best Practices for Accessing Variables

C

Crypten

Guest
I have a question but let me give some context first.

I have a project with three distinct modes.
Each mode requires their own set of global variables that are unused by the other modes.
Along with that some of these global variables will be quite large in size. (Large Data Structures)
So, for the purpose of saving memory and for good practice, it is my understanding that the best practice is to store my "global but not really global to the whole program variables" in an object or objects.
That way I can create and remove the object/s as needed. (The hidden global object can only have variables added to it and modified as far as I know)
Simple enough.

But this brings me to my question.
How should variables like these be accessed?

I read through this thread: https://forum.yoyogames.com/index.php?threads/are-global-variables-worth-it.33787/
There was some interesting discussion on which variable variations were the fastest to read and write.
YoYo Staff member Russell gave a link to a project file he made for testing this: https://www.dropbox.com/s/5gmewcwmdjhyer8/TestVarTiming.yyz?dl=0

Compiling the above code I got these results:

GMS2
Instance Write - elapsed time=0.05us
Other Instance Write - elapsed time=0.08us
Object Write - elapsed time=0.06us
Global Write - elapsed time=0.06us
Local Write - elapsed time=0.05us
Instance Read - elapsed time=0.06us
Other Instance Read - elapsed time=0.09us
Object Read - elapsed time=0.07us
Global Read - elapsed time=0.06us
Local Read - elapsed time=0.06us

GMS1.4
Instance Write - elapsed time=0.06us
Other Instance Write - elapsed time=0.09us
Object Write - elapsed time=0.07us
Global Write - elapsed time=0.07us
Local Write - elapsed time=0.06us
Instance Read - elapsed time=0.07us
Other Instance Read - elapsed time=0.09us
Object Read - elapsed time=0.07us
Global Read - elapsed time=0.06us
Local Read - elapsed time=0.06us

(The code was written for GMS2 but I modified the syntax to compile in GMS1.4)

For reference:

Code:
var instance = instance_create_depth( 10, 10, -100, object0 );
var ten = 10;
Instance Write
Code:
foo = 10;
Other Instance Write
Code:
instance.foo = 10;
Object Write
Code:
object0.foo = 10;
Global Write
Code:
global.foo = 10;
Local Write
Code:
var foofoo = 10;
Instance Read
Code:
foofoo = foo;
Other Instance Read
Code:
foofoo = instance.foo;
Object Read
Code:
foofoo = object0.foo;
Global Read
Code:
foofoo = global.foo;
Local Read
Code:
foofoo = ten;
Going from fastest to slowest the results roughly read
Local and Instance < Global and Object < Other Instance

But, before this project and results were posted in the thread above some community members were saying that they believed the best practice was to use the object id (Other Instance) when reading and writing to variables in another object. They believed that the Other Instance method would be faster than either the Global or Object methods and were surprised when the results showed otherwise. That thread never really reached a consensus or conclusion.

So I ask: When accessing objects that will never have more than one instance do you think their id should be used (Other Instance) or simply their name (Object)?

On a semi-related note. Did GMS2 patch 2.1.3.273 (released Wed, 13 Dec 2017) affect anything related to accessing object variables? That was the patch that added the Variables section to the Object editor. I don't believe so but let me know if anything has changed because of the patch.

Feel free to share any thoughts on this discussion.
 
C

Crypten

Guest
Some more thoughts about the test results.

I believe it makes sense that the Local and Instance Read/Write would be roughly equal and the fastest because they do not use the dot operator.
I believe it makes sense that the Global and Object Read/Write would be roughly equal because Global is essentially a hidden object. (To my knowledge)
But it is interesting that the Other Instance, accessing an object by its id, would be slower than the Global and Object methods.
 

Yal

🐧 *penguin noises*
GMC Elder
You should've opened your own topic instead of ressurecting a one-year-old topic. I mean, it's almost halloween, but geez! Necrobumping is against the rules, seasonal or not.

To access data from an object, the fast way is id.variablename = ..., the slightly safer way is with(id){variablename = ...}. You can store the id in a variable when creating the object:

global.datacarrier_char1 = instance_create(0,0,obj_datacarrier)

You might wanna make the objects persistent so they don't go away until destroyed.


The main thing you need to be aware of is that only arrays and normal variables will be garbage-collected when you destroy the instances. Data structures, surfaces etc won't be.

In GMS1, deactivated instances' variables could still be read if you had their ID and used the dot notation (with statements, instance_exists(), and events wouldn't work), not sure if this is still in GMS2 or not. The logic behind it was that they were removed from the main instance list, but direct references still would work. If this is still in GMS2 (experiment a bit to see), you should have your data carriers deactivated all the time to save CPU power. (Lightweight data objects currently are on the roadmap, but has no set release date at the time of writing - if they're available when you're reading this, use those instead)
 
C

Chupiperasaurio Studios

Guest
You should've opened your own topic instead of ressurecting a one-year-old topic. I mean, it's almost halloween, but geez! Necrobumping is against the rules, seasonal or not.

To access data from an object, the fast way is id.variablename = ..., the slightly safer way is with(id){variablename = ...}. You can store the id in a variable when creating the object:

global.datacarrier_char1 = instance_create(0,0,obj_datacarrier)

You might wanna make the objects persistent so they don't go away until destroyed.


The main thing you need to be aware of is that only arrays and normal variables will be garbage-collected when you destroy the instances. Data structures, surfaces etc won't be.

In GMS1, deactivated instances' variables could still be read if you had their ID and used the dot notation (with statements, instance_exists(), and events wouldn't work), not sure if this is still in GMS2 or not. The logic behind it was that they were removed from the main instance list, but direct references still would work. If this is still in GMS2 (experiment a bit to see), you should have your data carriers deactivated all the time to save CPU power. (Lightweight data objects currently are on the roadmap, but has no set release date at the time of writing - if they're available when you're reading this, use those instead)
Okay I am so sorry for the necrobumping thing. So the conclusion is that instance.variable is faster and better than obj_thing.variable, right?
 

Yal

🐧 *penguin noises*
GMC Elder
They should be the same for most cases, either will check exactly 1 instance and read / write its value. Since the experimental values only has one decimal, it's hard to tell if the 0.06 / 0.07 difference is significant (are those 0.06es closer to 0.060 or 0.069? Even if they're properly rounded, 0.065 still rounds upwards to 0.07), so I'd say they're close enough you don't need to worry about which one to use unless you will do several thousands of accesses per step, and if you need even that 0.01us performance boost, you should do your own experiments to see if the difference is noticeable or not.
 
Top