# QB CULT MAGAZINEVol. 2 Iss. 1 - March 2001

• The classic one is to use DEFINT A-Z. This forces you to use as many integer variables as possible.
• Use integer variables to index FOR loops. This may require substitution and algebraic simplification.
Before:
FOR i!=0 to 0.3 STEP 0.01
p!=i!*3
NEXT

After:
FOR i%=0 to 30
p!=i%*0.03
NEXT

• Use SELECT CASE instead of a bunch of ELSEIFs.
Before:
IF i=1 THEN
CALL DrawSprite
ELSEIF i=6 THEN
CALL PlaySound
ELSEIF i>9 AND i<16 THEN
CALL Calculate(i)
ELSE
PRINT "."
ENDIF

After:
SELECT CASE i
CASE 1
CALL DrawSprite
CASE 6
CALL PlaySound
CASE 10 TO 15
CALL Calculate(i)
CASE ELSE
PRINT "."
END SELECT

• If your code has a lot of floating point calculations that need high accuracy, compile with QB 4.0. (e.g. a raytracer)
• If your code has a lot of floating point calculations that don't need more than 8 bits of accuracy, then definitely convert it to fixed point. Even if it needs up to 16 bits of accuracy, it might be worth converting to fixed point, if it is being used in the main loop. (e.g. a rotozoomer or voxel terrain)
• don't use IFs (conditional branches). Some comparison results can be directly be used in a calculation. Note that in QB, a TRUE boolean expression equals -1, and a FALSE one equals 0.
Before:
IF a>4 THEN
b=5
ELSE
b=0
ENDIF

After:
b=-5*(a>4)

• use an assembler keyboard handler or INP(&H60) plus keyboard buffer clearing routines instead of INKEY\$.
• store the results of complicated expressions in look-up tables.
Before:
pi=ATN(1)*4
DO
FOR i=0 to 360
x!=100+COS(i*pi/180!)
y!=100+SIN(i*pi/180!)
PSET(x!,y!),c
NEXT i
LOOP until LEN(INKEY\$)

After:
pi=ATN(1)*4

• Make constants CONST. Unfortunately, you can't use transcendental functions like ATN on the right side anymore.
Before:
pi=ATN(1)*4
piover2=pi/2

After:
CONST pi=3.14159265358979#
CONST piover2=pi/2

• Unroll short loops.
Before:
FOR a=1 to 8
POKE(a,0),a
NEXT

After:
POKE 1,1
POKE 2,2
POKE 3,3
POKE 4,4
POKE 5,5
POKE 6,6
POKE 7,7
POKE 8,8

• Partially unroll long loops.
Before:
FOR x=0 TO 319
POKE x,a
NEXT

After:
' this is a silly example, you should be using
' MMX filling or REP STOSB at least.
FOR x=0 TO 319 STEP 4
POKE x,a
POKE x+1,a
POKE x+2,a
POKE x+3,a
NEXT x

• Move junk outside of the inner loops (code movement).
Before:
FOR y=0 TO 199
FOR x=0 TO 319
a=x*4+COS(t)
b=y*3+SIN(t)
NEXT
NEXT

After:
FOR y=0 TO 199
b=y*3+SIN(t)
FOR x=0 TO 319
a=x*4+COS(t)
NEXT
NEXT

• Use cache sensitive programming. This means, try to access your arrays in a sequential manner if possible. If not, access them in small blocks that are adjacent to eachother. For example, QB arrays are usually stored in a column major order.
• Pass dummy parameters to functions to improve alignment. This only makes a slight difference in speed.
• Prefer array indexing over user defined TYPEs. Warning: This makes code unreadable.
• Avoid multidimensional arrays.
• Use POKE instead of PSET. This is a simple way to get 2x performance in graphics intensive apps.
• PEEKing from video memory is slower than PEEKing from system memory. Therefore, use double buffering when you need to do feedback effects.
• Use DEF SEG sparingly.
• Don't use '\$DYNAMIC. QB arrays in the default segment are accessed at blazing speed, because there is no segment switching. However, '\$DYNAMIC puts them in different segments, which need extra instructions to accessed, slowing them down. This makes a big difference in programs that use large lookup tables in their inner loop.
• Don't put the main loop in the main code-- put it in a SUB.
• Use AND instead of MOD for MODing by a power of 2.
Before:
a=b MOD 64

After:
a=b AND 63

• Simplify compares against zero.
Before:
' assuming a% only decrements by one
IF a%>0 THEN
b%=b%-1
END IF

After:
IF a% THEN 'note >0 is gone
b%=b%-1
END IF