2

I'm using GameMaker Studio and you can think of it as a giant loop.

I use a counter variable step to keep track of what frame it is.

I'd like to run some code only every Xth step for efficiency.

if step mod 60 {

}

Would run that block every 60 steps (or 1 second at 60 fps).

My understanding is modulus is a heavy operation though and with thousands of steps I imagine the computation can get out of hand. Is there a more efficient way to do this?

Perhaps involving bitwise operator?

I know this can work for every other frame:

// Declare
counter = 0

// Step
counter = (counter + 1) & 1

if counter {

}

Or is the performance impact of modulus negligible at 60FPS even with large numbers?

veta
  • 716
  • 9
  • 22
  • 3
    Generally, my attitude is to not worry about performance until you have to. Especially at this low level. Modulus is heavy compared to addition, but probably pretty light compared to String comparison or something. If my thinking is correct, modulus is simply 3 arithmetic operations, so effectively nil time. – Joe Essey Oct 02 '15 at 19:39
  • why don't you run a separate thread that triggers events every so often. – ergonaut Oct 02 '15 at 19:39
  • I'm not sure if that's applicable because a lot of this stuff is done runtime. E.g. A user can change the FPS to 30 and then all the subsequent frame skips change too. And of course the step variable is never constant. – veta Oct 02 '15 at 20:10

2 Answers2

3

In essence:

i := 0
WHILE i < n/4
  do rest of stuff × 4
  do stuff that you want to do one time in four
  Increment i

Do rest of stuff i%4 times

The variant of this that takes the modulus and switches based on that is called Duff’s Device. Which is faster will depend on your architecture: on many RISC chips, mod executes in a single clock cycle, but on other CPUs, integer division might not even be a native instruction.

If you don’t have a loop counter per se, because it’s an event loop for example, you can always make one and reset it every four times in the if block where you execute your code:

i := 1
WHILE you loop
  do other stuff
  if i == 4
    do stuff
    i := 1
  else
    i := i + 1

Here’s an example of doing some stuff one time in two and stuff one time in three:

WHILE looping
  do stuff
  do stuff a second time
  do stuff B
  do stuff a third time
  do stuff C
  do stuff a fourth time
  do stuff B
  do stuff a fifth time
  do stuff a sixth time
  do stuff B
  do stiff C

Note that the stuff you do can include calling an event loop once.

Since this can get unwieldy, you can use template metaprogramming to write these loops for you in C++, something like:

constexpr unsigned a = 5, b = 7, LCM_A_B = 35;

template<unsigned N>
  inline void do_stuff(void)
{
  do_stuff_always();
  if (N%a)
    do_stuff_a(); // Since N is a compile-time constant, the compiler does not have to check this at runtime!

  if (N%b)
    do_stuff_b();

  do_stuff<N-1>();
}

template<>
  inline void do_stuff<1U>(void)
{
  do_stuff_always();
}

while (sentinel)
  do_stuff<LCM_A_B>(); 

In general, though, if you want to know whether your optimizations are helping, profile.

Davislor
  • 14,674
  • 2
  • 34
  • 49
  • Interesting, so would it just be faster to use a separate variable for each skip length - or is the cost of modulus more or less the same even with large (100k+ numbers)? – veta Oct 02 '15 at 20:12
  • If you add multiple skip lengths, let’s say 3 and 4, updating two counters would take twice as long. You could keep one counter that runs from 1 to 12 and run extra code when it hits 3, 6, 9 and 12, and also 4, 8 and 12, but then you either have a mod or a bunch of extra tests. I suspect the overhead of one mod instruction per loop is smaller on your CPU. – Davislor Oct 02 '15 at 20:22
  • @beta Added an example of how to do it efficiently without mod, but it could get unwieldy for large numbers. (But there’s always template metaprogramming to do it for you!) – Davislor Oct 02 '15 at 20:32
  • @veta Whoops, got auto-"corrected" on your name there. Anyway, added a template example. – Davislor Oct 02 '15 at 20:48
  • Yeah this makes sense thanks Lorehead. I'll need to evaluate if this is really the best way to get savings. I know I'm at these kinds of arithmetic optimizations though. – veta Oct 02 '15 at 21:01
2

The most important part of the answer: that test probably takes so little time, in context, that it isn't worth the ions moving around your brain to think about it. If it only costs 1% it's almost certain there are bigger speedups you should be thinking about.

However, if the loop is fast, you could put in something like this:

if (--count < 0){
  count = 59;
  // do your thing
}

In some hardware, that test comes down to a single instruction decrement-and-branch-if-negative.

Mike Dunlavey
  • 40,059
  • 14
  • 91
  • 135
  • Yeah I looked into and some architectures it's 1 single operation, some it's 3. You don't really know which to expect on Android, so I decided to factor it out and use loops/bools like you describe. – veta Oct 04 '15 at 13:47