Search code examples
performancetimemodulusprocessing-efficiency

Efficient way to skip code every X iterations?


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?


Solution

  • 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.