Catching every Nth FOR/NEXT iteration

Hi, I’m a little rusty and embarrassed to ask, but here we go:
In this code what condition expression in the IF statement will be true for every 4th value of X ?
Note:
all numbers are Double (so no Mod() ), and the FOR/NEXT loop steps in fractional numbers.

  //---------SCALE GRADUATION LINES------
  LayerAll.graphics.ForeColor = RGB(254, 254, 254)
  LayerAll.graphics.PenWidth = 1  
  FOR X = 0 to 101 step ( 25000 / 20000)'*270'(100/MaxRPM*250)/10  '4 fine lines pr. coarse line
    Vector = 90 - (135 + (X * 2.7))
    Vector = (3.14159 / 180) * Vector
    //-----
    LayerAll.graphics.PenWidth = 1
    LineStartX = (Size / 2) + sin(Vector) * LimitSectorRi 
    LineEndX   = (Size / 2) + sin(Vector) * LimitSectorRo
    LineStartY = (Size / 2) + cos(Vector) * LimitSectorRi
    LineEndY   = (Size / 2) + cos(Vector) * LimitSectorRo
    LayerAll.graphics.DrawLine LineStartX, LineStartY, LineEndX, LineEndY
    //-----
    
    
    If X =  ?????????? Then 'catch every 4th iteration of X
      Vector = 270 - (X * 2.7 ) + 45
      Vector = (3.14159 / 180) * Vector 'convert to radians
      LayerAll.graphics.PenWidth = 3
      LineStartX = (Size / 2) + sin(Vector) * LimitSectorRi * 0.8
      LineEndX = (Size / 2) + (Sin(Vector)) * LimitSectorRo
      LineStartY = (Size / 2) + cos(Vector) * LimitSectorRi * 0.8
      LineEndY = (Size / 2) + (Cos(Vector)) * LimitSectorRo
      LayerAll.graphics.DrawLine LineStartX, LineStartY, LineEndX, LineEndY
    End if
    
  next X
  //-------------------------------

The easiest way I’ve found:

var count as integer

for x ...
  // your code

  count = count + 1
  if count = 4 then
    count = 0
    // your iteration code
  end if
next
1 Like

Doubles represent floating point numbers that can not always be precisely represented so maybe using x to compare could lead to missing steps.

Use a separate value as Kem says

Yupp, works great. Thanks.
Its for the scale graduations:
Image2456

Yes I had problems with missing “matches”

for x = startNumber to endNumber

    if x mod 4 = 0 then
        //do stuff
     end

next

Not with the loop he’s using though.

Ah, good point. Still, I wonder if this might be faster than your IF, since there is no assignment:

for x = startNumber to endNumber

    counter = counter + 1

    if counter mod 4 = 0 then
        //do stuff
     end

next

I suppose there’s a theoretical hazard that counter would overflow and go bonkers since it isn’t being reset to 0. But that doesn’t seem possible, at least not in this case.

Is a mod calculation faster than a static assignment? I’d guess no, but that’s easy enough to test.

In practice, I doubt it would be a significant difference either way.

Also I would have to check but I don’t think the rollover would matter either since we’re calculating against a power of two.

Good points, all.

I checked and tested. First, as I thought, the rollover, even if it could get that high, is irrelevant, mod will still work as desired.

But color me shocked, mod is about twice as fast as resetting the variable.

My test code:

#if not DebugBuild
  #pragma BackgroundTasks False
  #pragma NilObjectChecking False
  #pragma StackOverflowChecking False
  #pragma BoundsChecking False
#endif

dim msg as string
dim sw as new Stopwatch_MTC
sw.Start

var count as integer
var total as integer

count = 0
total = 0

for i as integer = 0 to 10000000
  count = count + 1
  if count = 4 then
    total = total + count
    count = 0
  end if
next

sw.Stop
AddToResult total

msg = format( sw.ElapsedMicroseconds, "#," ) + " microsecs"
AddToResult msg

sw.Reset
sw.Start

count = 0
total = 0

for i as integer = 0 to 10000000
  count = count + 1
  if ( count mod 4 ) = 0 then
    total = total + count
  end if
next

sw.Stop
AddToResult total

msg = format( sw.ElapsedMicroseconds, "#," ) + " microsecs"
AddToResult msg

That the results of the two calculations don’t match is unimportant. The only reason total is there at all is to keep the compiler from doing away with the whole loop.

A typical result:

10000000
15,026 microsecs
12500005000000
6,983 microsecs

Tested in a compiled app using Aggressive.

2 Likes

WTF? It is a fixed lenght datatype, It should just overwrite in the same memory addres and be ultra fast.

If this is a bottleneck every time a variable change its value in xojo, they need to look at this ASAP

What OS? What xojo release?

I tested on windows with xojo 2019r2.1 and resetting the variable is about twice as fast. Added another zero to the loop and it is the same.

Since I dont have a licence for most recent releases, I tested in debug mode, it was 19 seconds for resetting the variable and 18 for mod.

BUT, same code in xojo 2023r3 it was 42 seconds for resetting the variable and 40 for mod.

Looks like xojo really messed up the debug performance in half after 2019 :man_facepalming:t2:

Fascinating.

Try changing the modulus to a non-power-of-2 like 5 or 7. I wonder if there’s some amazing optimization that kicks in with powers of 2.

Also - try something like:

dim zero as Integer = 0

...

count = zero

…because there’s a chance that the compiler is representing that 0 as a double and doing a conversion every time you assign it.

That makes sense, it might just be shifting bits.

I did try using constants, but that made no difference.