repeatedly execute optimisation

Started by lafouine88, Wed 03/07/2024 21:12:33

Previous topic - Next topic

lafouine88

Hi guys

Just a quick question about the repeatedly execute function. I'm not sure how it actually works and I'm a bit concerned about wasting memory because of lazy coding.

I'm using quite a lot of this technique :

Code: ags
function cd_countdown(int i){
//this function aims at displaying the cooldown of a spell once you've played it. (When you cast the spell,it sets Spell[i].cd to a positive value, which triggers the following codes)

 if(Spell[i].cd>0){
   Spell[i].cd--;
   gSpellbar.Controls[i].AsLabel.Text=String.Format("%d", 1+(Spell[i].cd/40)); //a label from the gui that appears on top,
 }
 else{
   Spell[i].cd=-1;
   gSpellbar.Controls[i].AsLabel.Visible=false;  
 } 
}

//but since there are 10 buttons in my spell bar I want it to be checked for every button and spell so I use this :

function repeatedly_execute(){

for(int j=0;j<10;j++){
cd_countdown(j);
}

}

It works fine, but it looks like a huge waste of memory. I feel like the system is checking every second for every Spell.cd to be positive while I would like it to only activate when it's necessary. Do you know if this is the correct way to put it, or if I should add some more conditions to make it less memory consuming?

Thanks a lot :)

Khris

#1
In general this is how you have to do it, yes.

There is room for optimization though: instead of setting the label each frame it's enough to update it each second:

Code: ags
function cd_countdown(int i) {
  //this function aims at displaying the cooldown of a spell once you've played it. (When you cast the spell,it sets Spell[i].cd to a positive value, which triggers the following codes)

  if (Spell[i].cd > 0) {
    Spell[i].cd--;
    gSpellbar.Controls[i].AsLabel.Text = String.Format("%d", Spell[i].cd + 1); //a label from the gui that appears on top,
  } else {
    Spell[i].cd = -1;
    gSpellbar.Controls[i].AsLabel.Visible = false;
  }
}

int frame_count;

function repeatedly_execute() {

  frame_count++;

  if (frame_count == GetGameSpeed()) {
    frame_count = 0;
    // the following runs once per second
    for (int j = 0; j < 10; j++) {
      cd_countdown(j);
    }
  }

}

Also, this is not really a waste of memory, especially since all variables are already declared. No new memory is reserved here, let alone each frame.

lafouine88

Thanks Khris. Two little questions then :

-With the code optimisation that you propose, I havent' tried it yet but it seems like the data will be updated every second. Problem is, if I cast a spell at 00.00.00.00 and a second different one at 00.00.00.20, the second spell's infos will also be displayed at the exact second(t), which means there will be a small delay with reality, right?

-I'm intrigued by your last sentence, but to be honest I don't really understand "^^ could you please explain to me differently?
Does that mean that the amount of variables I create directly increases the memory consumption,"reserves it", but then no problem with scrolling through them?
If that is the case, is it necessary to lower the variables (global variables, or inside a struct?) to the strict minimum? Because I sure did use a lot and didn't really worry about optimisation on that.

Thanks and sorry for not getting it clearly^^

Khris

True, if you want the spell counters to tick down exactly from when they're cast, you need to go back to a higher frame resolution. You can in theory pick any amount of frames, like 5. It should be a divider of 40 though, so the conversion to seconds doesn't have rounding issues.

Memory is reserved whenever a new variable is declared. A single AGS int is probably a 32 bit signed integer, which means a gigabyte of RAM can hold 268.435.456 ints.
Changing an int takes up a (tiny, tiny, tiny) bit of processing time but doesn't use additional memory; it just changes the bits that were already reserved at declaration time.
It's like a massive blackboard where you wipe away a number and replace it with a different one, no additional space is needed.

Crimson Wizard

#4
Game memory is not only occupied by variables. I think that statistically graphics take most part. That is unless you allocate abnormally large dynamic arrays in script.

If you are worried about memory consumption, then calculate it! The amount of memory that variables take may be predicted (with certain level of precision).

Also, the simple way to see how much memory your game requires is to run it and check the process memory in Windows Task Manager.

Snarky

#5
When it comes to repeatedly_execute() it's not a matter of memory, but of processing (i.e. work the computer has to do). Your computer can do only so much work each game cycle: if you ask it to do too much, the game will start to lag (slow down or stutter).

In this case, the amount of work is trivial, so it doesn't make a significant difference, but in general it's something it's good to be aware of, and to learn how to optimize the code to reduce the amount of work performed (usually by structuring it differently). Khris's version is an optimization because it does less work each game cycle, only checking the list of spells when it's actually necessary to update (once per second).

Another possible optimization for something like this would be to keep a temporary list of the spells that are currently on cooldown, and only update those rather than checking the whole list; but since the list is very short (only 10 entries) and the work of checking the other spells very light, and since adding this "optimization" would require more code (and another array, which takes up memory), it doesn't make sense in this case. But if you had 10,000 different spells, and checking if each was on cooldown took a lot of time/effort, that would be something worth doing. (A middle-ground would be to just keep a counter of how many spells are currently on cooldown; that way you don't have to do anything if the counter is 0.)

But it's good that you're asking. As a general rule of thumb, using repeatedly_execute() is usually the "wrong" solution to most problems when you're starting out. By which I mean that inexperienced AGS coders very often jump to "forcing" things in repeatedly_execute() because they can't think of a better solution. This results in code that is inefficient, inelegant, and sometimes liable to create further problems—when not outright wrong.

lafouine88

Ok thanks a lot to you guys for all the precisions :cheesy:  :cheesy:  :cheesy:
It s much clearer now

SMF spam blocked by CleanTalk