Super Code from Digital Knife Monkey Productions

Digital Knife Monkeys at Keyboards...Will Eventually Program Everything.

Sample Program 1

'* This is a sample program that works. I have left LOTS of room for optimizations i regularly use which are
'* easy to remember
'* make code easier to read (another type of optimization often overlooked)
'* will make your programs look more expertly written
'* Graphics Optimizations Primer by:
'* CodeGuy
'* DarthWho
'* unseenmachine
'* Cyperium
TYPE Profile
TimeStart AS DOUBLE
TimeEnd AS DOUBLE
ElapsedTime AS DOUBLE
Routine AS INTEGER
Title AS STRING * 128
END TYPE

TYPE PixelRec
Red AS INTEGER
Green AS INTEGER
Blue AS INTEGER
PointValue AS LONG
END TYPE

capscreen& = _SCREENIMAGE
SCREEN capscreen&
wide% = _WIDTH(capscreen&)
high% = _HEIGHT(capscreen&)
DIM Pixel(wide%, high%) AS PixelRec

DIM ProfileCode(127) AS Profile


'* whenever we want to optimize code, we want to do one or two things:
'* optimize for
'* a) time (speed)
'* b) size
'* c) ideally both, which can be a hit-or-miss proposition.

'* one thing you will always want to do is CAREFULLY think about
'* a) what you want your program to do
'* b) how you want to accomplish this
'* a lot of times people get caught up in what i call "demo fever"
'* symptoms of this are:
'* a) the code gets written quickly without regard for how it may be used in the future
'* b) code works but it is structured so badly that it is VERY hard to adapt for other uses
'* c) lots of repetitive code
'* d) sloppy or nonexistent declarations of variable types
'* e) plenty of pasta (spaghetti is food, not a good way to code)
'* f) sometimes unexplained errors
'* g) much too liberal use of literals, which must be changed every instance, rather than just once
'* h) speed trumps readability
'* i) skipping optimization
'* j) using optimizations that actually make a program SLOWER


'* Supreme Programmer syndrome, characterized by:
'* a) use of recursion when iterative code models will work as efficiently if not better
'* b) very sparse/non-existent use of comments, thinking everyone will understand exactly why and how you are doing things
'* c) refusal to ask for answers because you see it as a personal threat to your self-perceived superiority
'* d) unwillingness to experiment (this code ALWAYS works for me, so there is nothing better).
'* e) using os or processor-specific hacks.
'* f) feeling insulted when someone points out possible errors in your programs
'* g) being insulting to other programmers
'* h) too much reliance on external compiled libraries
'* please don't do this. we REALLY want to see how things are done
'* if you write a program consisting of mostly library calls, have you REALLY written a program?
'* i) refusal to acknowledge others for their work or contributions
'* j) egos that inflate to outrageous proportions.

DO
'* some of these sections of code are by no means optimized, but they run.
FOR Routine% = 0 TO 4
ProfileCode(Routine%).TimeStart = TIMER(.001)
IF Routine% = 0 THEN '* get the current screen and load it into Pixel()
ProfileCode(Routine%).Title = "Getting Pixel Values Into Pixel()"
FOR y% = 1 TO high%
FOR x% = 1 TO wide%
PixelInfo Pixel(x%, y%), x%, y%, 0
NEXT
NEXT
ELSEIF Routine% = 1 THEN
'* here is where an optimization could be used. while this code works, it is slowed by multiple calls to POINT, which itself is a "slow" function
ProfileCode(Routine%).Title = "Get values into Pixel() non-optimized"
FOR a% = 1 TO high%
FOR b% = 1 TO wide%
Pixel(b%, a%).Red = _RED(POINT(b%, a%))
Pixel(b%, a%).Green = _GREEN(POINT(b%, a%))
Pixel(b%, a%).Blue = _BLUE(POINT(b%, a%))
Pixel(b%, a%).PointValue = POINT(b%, a%)
NEXT
NEXT
ELSEIF Routine% = 2 THEN

'* so now we clean it up a bit, changing the multiple POINT calls into a single one at the beginning and calculating
'* _RED, _GREEN and _BLUE values from that instead
ProfileCode(Routine%).Title = "Getting Pixel Values Into Pixel() using Pixel(b%, a%).PointValue = POINT(b%, a%)"
FOR a% = 1 TO high%
FOR b% = 1 TO wide%
Pixel(b%, a%).PointValue = POINT(b%, a%)
Pixel(b%, a%).Red = _RED(Pixel(b%, a%).PointValue)
Pixel(b%, a%).Green = _GREEN(Pixel(b%, a%).PointValue)
Pixel(b%, a%).Blue = _BLUE(Pixel(b%, a%).PointValue)
NEXT
NEXT
ELSEIF Routine% = 3 THEN
'* TYPE variable access is "slow" because of pointer offset calculations, so replace Pixel(b%, a%).PointValue with PointTemp&=Pixel(b%, a%).PointValue
'* This code SHOULD run the most quickly.
ProfileCode(Routine%).Title = "Getting Pixel Values Into Pixel() using PointTemp& = Pixel(b%, a%).PointValue"
FOR a% = 1 TO high%
FOR b% = 1 TO wide%
PointTemp& = Pixel(b%, a%).PointValue
Pixel(b%, a%).PointValue = PointTemp&
Pixel(b%, a%).Red = _RED(PointTemp&)
Pixel(b%, a%).Green = _GREEN(PointTemp&)
Pixel(b%, a%).Blue = _BLUE(PointTemp&)
NEXT
NEXT
ELSE '* in case we want to try other optimizations
END IF
ProfileCode(Routine%).Routine = Routine%
ProfileCode(Routine%).TimeEnd = TIMER(.001)
ProfileCode(Routine%).ElapsedTime = ProfileCode(Routine%).TimeEnd - ProfileCode(Routine%).TimeStart
NEXT
LOOP UNTIL INKEY$ > ""
SCREEN 0
_FREEIMAGE capscreen&
SYSTEM
SUB PixelInfo (P AS PixelRec, Pixelinfox%, PixelInfoy%, Operation%)
SELECT CASE Operation%
CASE 0 '*Get the pixel Value
P.Red = _RED(POINT(Pixelinfox%, PixelInfoy%))
P.Green = _GREEN(POINT(Pixelinfox%, PixelInfoy%))
P.Blue = _RED(POINT(Pixelinfox%, PixelInfoy%))
P.PointValue = POINT(Pixelinfox%, PixelInfoy%)
CASE 1 '* set the pixel value
PSET (Pixelinfox%, PixelInfoy%), P.PointValue
CASE ELSE
END SELECT
END SUB

'* This is a sample program that works. I have left LOTS of room for optimizations i regularly use which are
'*          easy to remember
'*          make code easier to read (another type of optimization often overlooked)
'*          will make your programs look more expertly written

'* Graphics Optimizations Primer by:
'*          CodeGuy
'*          DarthWho
'*          unseenmachine
'*          Cyperium

TYPE Profile
    TimeStart AS DOUBLE
    TimeEnd AS DOUBLE
    ElapsedTime AS DOUBLE
    Routine AS INTEGER
    Title AS STRING * 128
END TYPE

TYPE PixelRec
    Red AS INTEGER
    Green AS INTEGER
    Blue AS INTEGER
    PointValue AS LONG
END TYPE

capscreen& = _SCREENIMAGE
SCREEN capscreen&
wide% = _WIDTH(capscreen&)
high% = _HEIGHT(capscreen&)
DIM Pixel(wide%, high%) AS PixelRec

DIM ProfileCode(127) AS Profile


'* whenever we want to optimize code, we want to do one or two things:
'* optimize for
'*      a) time (speed)
'*      b) size
'*      c) ideally both, which can be a hit-or-miss proposition.

'* one thing you will always want to do is CAREFULLY think about
'*      a) what you want your program to do
'*      b) how you want to accomplish this

'* a lot of times people get caught up in what i call "demo fever"
'* symptoms of this are:
'*      a) the code gets written quickly without regard for how it may be used in the future
'*      b) code works but it is structured so badly that it is VERY hard to adapt for other uses
'*      c) lots of repetitive code
'*      d) sloppy or nonexistent declarations of variable types
'*      e) plenty of pasta (spaghetti is food, not a good way to code)
'*      f) sometimes unexplained errors
'*      g) much too liberal use of literals, which must be changed every instance, rather than just once
'*      h) speed trumps readability
'*      i) skipping optimization
'*      j) using optimizations that actually make a program SLOWER


'* Supreme Programmer syndrome, characterized by:
'*      a) use of recursion when iterative code models will work as efficiently if not better
'*      b) very sparse/non-existent use of comments, thinking everyone will understand exactly why and how you are doing things
'*      c) refusal to ask for answers because you see it as a personal threat to your self-perceived superiority
'*      d) unwillingness to experiment (this code ALWAYS works for me, so there is nothing better).
'*      e) using os or processor-specific hacks.
'*      f) feeling insulted when someone points out possible errors in your programs
'*      g) being insulting to other programmers
'*      h) too much reliance on external compiled libraries
'*               please don't do this. we REALLY want to see how things are done
'*               if you write a program consisting of mostly library calls, have you REALLY written a program?
'*      i) refusal to acknowledge others for their work or contributions
'*      j) egos that inflate to outrageous proportions.

DO
    '* some of these sections of code are by no means optimized, but they run.
    FOR Routine% = 0 TO 4
        ProfileCode(Routine%).TimeStart = TIMER(.001)
        IF Routine% = 0 THEN '* get the current screen and load it into Pixel()
            ProfileCode(Routine%).Title = "Getting Pixel Values Into Pixel()"
            FOR y% = 1 TO high%
                FOR x% = 1 TO wide%
                    PixelInfo Pixel(x%, y%), x%, y%, 0
                NEXT
            NEXT
        ELSEIF Routine% = 1 THEN

            '* here is where an optimization could be used. while this code works, it is slowed by multiple calls to POINT, which itself is a "slow" function
            ProfileCode(Routine%).Title = "Get values into Pixel() non-optimized"

            FOR a% = 1 TO high%
                FOR b% = 1 TO wide%
                    Pixel(b%, a%).Red = _RED(POINT(b%, a%))
                    Pixel(b%, a%).Green = _GREEN(POINT(b%, a%))
                    Pixel(b%, a%).Blue = _BLUE(POINT(b%, a%))
                    Pixel(b%, a%).PointValue = POINT(b%, a%)
                NEXT
            NEXT
        ELSEIF Routine% = 2 THEN

            '* so now we clean it up a bit, changing the multiple POINT calls into a single one at the beginning and calculating
            '* _RED, _GREEN and _BLUE values from that instead

            ProfileCode(Routine%).Title = "Getting Pixel Values Into Pixel() using Pixel(b%, a%).PointValue = POINT(b%, a%)"
            FOR a% = 1 TO high%
                FOR b% = 1 TO wide%
                    Pixel(b%, a%).PointValue = POINT(b%, a%)
                    Pixel(b%, a%).Red = _RED(Pixel(b%, a%).PointValue)
                    Pixel(b%, a%).Green = _GREEN(Pixel(b%, a%).PointValue)
                    Pixel(b%, a%).Blue = _BLUE(Pixel(b%, a%).PointValue)
                NEXT
            NEXT
        ELSEIF Routine% = 3 THEN

            '* TYPE variable access is "slow" because of pointer offset calculations, so replace Pixel(b%, a%).PointValue with PointTemp&=Pixel(b%, a%).PointValue
            '* This code SHOULD run the most quickly.

            ProfileCode(Routine%).Title = "Getting Pixel Values Into Pixel() using PointTemp& = Pixel(b%, a%).PointValue"
            FOR a% = 1 TO high%
                FOR b% = 1 TO wide%
                    PointTemp& = Pixel(b%, a%).PointValue
                    Pixel(b%, a%).PointValue = PointTemp&
                    Pixel(b%, a%).Red = _RED(PointTemp&)
                    Pixel(b%, a%).Green = _GREEN(PointTemp&)
                    Pixel(b%, a%).Blue = _BLUE(PointTemp&)
                NEXT
            NEXT
        ELSE '* in case we want to try other optimizations
        END IF
        ProfileCode(Routine%).Routine = Routine%
        ProfileCode(Routine%).TimeEnd = TIMER(.001)
        ProfileCode(Routine%).ElapsedTime = ProfileCode(Routine%).TimeEnd - ProfileCode(Routine%).TimeStart
    NEXT
LOOP UNTIL INKEY$ > ""
SCREEN 0

_FREEIMAGE capscreen&
SYSTEM

SUB PixelInfo (P AS PixelRec, Pixelinfox%, PixelInfoy%, Operation%)
SELECT CASE Operation%
    CASE 0 '*Get the pixel Value
        P.Red = _RED(POINT(Pixelinfox%, PixelInfoy%))
        P.Green = _GREEN(POINT(Pixelinfox%, PixelInfoy%))
        P.Blue = _RED(POINT(Pixelinfox%, PixelInfoy%))
        P.PointValue = POINT(Pixelinfox%, PixelInfoy%)
    CASE 1 '* set the pixel value
        PSET (Pixelinfox%, PixelInfoy%), P.PointValue
    CASE ELSE
END SELECT
END SUB

 

Sample Program 2

'* This is a sample program that works. I have left LOTS of room for optimizations i regularly use which are
'*          easy to remember
'*          make code easier to read (another type of optimization often overlooked)
'*          will make your programs look more expertly written

'* Graphics Optimizations Primer by:
'*          CodeGuy
'*          DarthWho
'*          unseenmachine
'*          Cyperium

First, a little background on the program we want to write. It is a very simple graphics program that copies the graphic contents of your desktop, loads the pixels into an array big enough to contain the picture, uses various methods to get the values of a pixel for each one on the screen and then uses different methods to accomplish this task. This is the first step to structural optimization, to define clearly what you want the program to do and how you want to accomplish it. There would be little purpose in writing a program with no clearly defined goal.


'* whenever we want to optimize code, we want to do one or two things:
'* optimize for
'*      a) time (speed)
'*      b) size
'*      c) ideally both, which can be a hit-or-miss proposition.

'* one thing you will always want to do is CAREFULLY think about
'*      a) what you want your program to do
'*      b) how you want to accomplish this

'* a lot of times people get caught up in what i call "demo fever"
'* symptoms of this are:
'*      a) written quickly without regard for how it may be used in the future
'*      b) works but structured so badly that it is VERY hard to adapt for other uses
'*      c) lots of repetitive code
'*      d) sloppy or nonexistent declarations of variable types
'*      e) plenty of pasta (spaghetti is food, not a good way to code)
'*      f) sometimes unexplained errors
'*      g) memory leaks
'*      g) much too liberal use of literals, which must be changed every instance, rather than just once if declared as a variable or constant

'*      h) speed trumps readability
'*      i) skipping optimization
'*      j) using optimizations that actually make a program SLOWER


'* Supreme Programmer syndrome, characterized by:
'*      a) use of recursion when iterative code models will work as efficiently if not better. One of the prime examples I will mention of poor use of recursion is a recursive factorial SUB or FUNCTION. There are plenty of others but this one stands out as one of the most egregiously hideous examples of an otherwise fine programming tool.

'*      b) very sparse/non-existent use of comments, thinking everyone will understand exactly why and how you are doing things
'*      c) refusal to ask for answers because you see it as a personal threat to your self-perceived superiority
'*      d) unwillingness to experiment (this code ALWAYS works for me, so there is nothing better).
'*      e) using operating system or processor-specific hacks.
'*      f) feeling insulted when someone points out possible errors in your programs
'*      g) being insulting to other programmers
'*      h) too much reliance on external compiled libraries
'*               please don't do this. we REALLY want to see how things are done
'*               if you write a program consisting of mostly library calls, have you REALLY written a program?
'*      i) refusal to acknowledge others for their work or contributions
'*      j) egos that inflate to outrageous proportions.

'* Sometimes a program is adopted without even bothering to optimize it, relying on ever faster processors to decrease running time. Fast processors are good, but slow ones will help you see the value of optimizing code. To simulate a slow CPU, I either run a program in VirtualBox from SUN or use a CPU slowing program I got from here (check for compatibility with your OS) which slows my CPU. Sometimes it is simple change that can have the greatest effect. My prime example is on QB64, where I brought the running time of a program to within a very small margin of FreeBasic's highly optimized code using QB64. it was indeed some very small changes that had the greatest effect. The first place to look for places to cut down bottlenecks is where the program spends the most time. I have seen programmers write code that simply returned the sum of 2 numbers, when leaving that code inline would have been far more efficient. One case in point that I read was about a supercomputing facility that ran a program for years and depended on ever faster processors to run that part more quickly.

One day one of the programmers decided to take a look at the code and within a short time, cut the running time by a phenomenal amount, just as I did with this. It did not take much time to find where the bottleneck was, nor did it take much time to decide how I wanted to go about making it faster. And nothing is better than a testimonial from someone using your code that says what a huge improvement you made. In my case, it was an 800% increase in efficiency. Even with a modest 20% increase in efficiency, your programs run 25% faster. This is very important in graphics-heavy presentations like games and demos. Your aim should be not only to demonstrate it can be done, but done in a way that someone whose computer lacks what I call horsepower can also run it because not everyone packs a 4+ GHz CPU.

One of the first things you will want to do when starting the optimization process is to insert what I call profiling code. These are like markers that mark the starts and ends of procedures, loops and such. For this example, I created a typed variable array, ProfileCode(), that tracks the execution times of various segments. It does not keep track of total running times as we are only concerned at this point with what would be a single run.

This is a testimonial about a program I optimized

Comment About Program Improvement

This is a sample program in various stages of optimization as indicated by routine.

********************** start of program **********************

TYPE Pixelrec
    red AS INTEGER
    green AS INTEGER
    blue AS INTEGER
    PointValue AS LONG
END TYPE
TYPE Profile
    Title AS STRING * 80
    TimeStart AS SINGLE
    Routine AS INTEGER
    TimeEnd AS SINGLE
    ElapsedTime AS SINGLE
    AccumulatedTime AS SINGLE
    NumberOfRuns AS LONG
END TYPE

capscreen& = _SCREENIMAGE
high% = _HEIGHT(capscreen&)
wide% = _WIDTH(capscreen&)

SCREEN capscreen&
DIM ProfileCode(0 TO 4) AS Profile
DIM Pixel(wide%, high%) AS Pixelrec

DO

    FOR Routine% = 0 TO 4
        ProfileCode(Routine%).TimeStart = TIMER(.001)
        IF Routine% = 0 THEN '* get the current screen and load it into Pixel()
            ProfileCode(Routine%).Title = "Getting Pixel Values Into Pixel()"
            FOR y% = 1 TO high%
                FOR x% = 1 TO wide%
                    PixelInfo Pixel(x%, y%), x%, y%, 0
                NEXT
            NEXT
        ELSEIF Routine% = 1 THEN

            '* here is where an optimization could be used. while this code works, it is slowed by multiple calls to POINT, which itself is a "slow" function
            ProfileCode(Routine%).Title = "Get values into Pixel() non-optimized"

            FOR a% = 1 TO high%
                FOR b% = 1 TO wide%
                    Pixel(b%, a%).red = _RED(POINT(b%, a%))
                    Pixel(b%, a%).green = _GREEN(POINT(b%, a%))
                    Pixel(b%, a%).blue = _BLUE(POINT(b%, a%))
                    Pixel(b%, a%).PointValue = POINT(b%, a%)
                NEXT
            NEXT
        ELSEIF Routine% = 2 THEN

            '* so now we clean it up a bit, changing the multiple POINT calls into a single one at the beginning and calculating
            '* _RED, _GREEN and _BLUE values from that instead

            ProfileCode(Routine%).Title = "Getting Pixel Values Into Pixel() using Pixel(b%, a%).PointValue = POINT(b%, a%)"
            FOR a% = 1 TO high%
                FOR b% = 1 TO wide%
                    Pixel(b%, a%).PointValue = POINT(b%, a%)
                    Pixel(b%, a%).red = _RED(Pixel(b%, a%).PointValue)
                    Pixel(b%, a%).green = _GREEN(Pixel(b%, a%).PointValue)
                    Pixel(b%, a%).blue = _BLUE(Pixel(b%, a%).PointValue)
                NEXT
            NEXT
        ELSEIF Routine% = 3 THEN

            '* TYPE variable access is "slow" because of pointer offset calculations, so replace Pixel(b%, a%).PointValue with PointTemp&=Pixel(b%, a%).PointValue
            '* This code SHOULD run the most quickly.

            ProfileCode(Routine%).Title = "Getting Pixel Values Into Pixel() using PointTemp& = Pixel(b%, a%).PointValue"
            FOR a% = 1 TO high%
                FOR b% = 1 TO wide%
                    PointTemp& = Pixel(b%, a%).PointValue
                    Pixel(b%, a%).PointValue = PointTemp&
                    Pixel(b%, a%).red = _RED(PointTemp&)
                    Pixel(b%, a%).green = _GREEN(PointTemp&)
                    Pixel(b%, a%).blue = _BLUE(PointTemp&)
                NEXT
            NEXT
        ELSEIF Routine% = 4 THEN

            '* in case we want to try other optimizations
            '* now we will try doing smaller dimension in the inner loop
            ProfileCode(Routine%).Title = "Smaller Dimension Inner Loop"
            IF wide% < high% THEN
                FOR a% = 1 TO high%
                    FOR b% = 1 TO wide%
                        PointTemp& = Pixel(b%, a%).PointValue
                        Pixel(b%, a%).PointValue = PointTemp&
                        Pixel(b%, a%).red = _RED(PointTemp&)
                        Pixel(b%, a%).green = _GREEN(PointTemp&)
                        Pixel(b%, a%).blue = _BLUE(PointTemp&)
                    NEXT
                NEXT
            ELSE
                FOR b% = 1 TO wide%
                    FOR a% = 1 TO high%
                        PointTemp& = Pixel(b%, a%).PointValue
                        Pixel(b%, a%).PointValue = PointTemp&
                        Pixel(b%, a%).red = _RED(PointTemp&)
                        Pixel(b%, a%).green = _GREEN(PointTemp&)
                        Pixel(b%, a%).blue = _BLUE(PointTemp&)
                    NEXT
                NEXT
            END IF
        END IF
        ProfileCode(Routine%).Routine = Routine%
        ProfileCode(Routine%).TimeEnd = TIMER(.001)
        ProfileCode(Routine%).ElapsedTime = ProfileCode(Routine%).TimeEnd - ProfileCode(Routine%).TimeStart
        ProfileCode(Routine%).AccumulatedTime = ProfileCode(Routine%).AccumulatedTime + ProfileCode(Routine%).ElapsedTime
        ProfileCode(Routine%).NumberOfRuns = ProfileCode(Routine%).NumberOfRuns + 1
    NEXT
LOOP UNTIL INKEY$ > ""
SCREEN 0
_FREEIMAGE capscreen&

'* you will want to display the results of running this program here
'* we will also later want a way to sort the results using the metric
'* to measure the performance factor of each method. higher performance
'* numbers mean better performance
'*
'*               ProfileCode(DispTimes%).AccumulatedTime
'* performance = ---------------------------------------
'*                ProfileCode(DispTimes%).NumberOfRuns
'*

FOR DispTimes% = 0 TO 4
    IF ProfileCode(DispTimes%).NumberOfRuns > 0 THEN
        PRINT ProfileCode(DispTimes%).Title
        PRINT ProfileCode(DispTimes%).ElapsedTime
        PRINT ProfileCode(DispTimes%).AccumulatedTime
        PRINT ProfileCode(DispTimes%).NumberOfRuns
    END IF
NEXT

DO
LOOP UNTIL INKEY$ > ""

SYSTEM

SUB PixelInfo (P AS Pixelrec, Pixelinfox%, PixelInfoy%, Operation%)
SELECT CASE Operation%
    CASE 0 '*Get the pixel Value
        P.red = _RED(POINT(Pixelinfox%, PixelInfoy%))
        P.green = _GREEN(POINT(Pixelinfox%, PixelInfoy%))
        P.blue = _RED(POINT(Pixelinfox%, PixelInfoy%))
        P.PointValue = POINT(Pixelinfox%, PixelInfoy%)
    CASE 1 '* set the pixel value
        PSET (Pixelinfox%, PixelInfoy%), P.PointValue
    CASE ELSE
END SELECT
END SUB

'********************* end of program **********************

I have added some new features and will use a performance metric to help decide which method is the most advantageous. One thing to watch out for is doing too few runs of a set of algorithms. Due to varying computer workloads, what is fastest in a small set of runs may not actually be the fastest overall algorithm to use. Especially with very fast running code, you want to run it many times and compare it against other variations. In some cases because CPUs are so much more powerful, this may require runs numbering in the thousands or millions. Sections of code that run faster than the timing resolution of the built-in timer definitely need to be run enough times to exceed the lower limit of timing resolution (IE: "ticks," or in the case of QB64, milliseconds). So this necessitates adding a few more things. Namely a time accumulator, a comparison function and a way to organize the results according to relative performance. So I have added the following routines and changes to Profile TYPE

TYPE Profile
    Title AS STRING * 80
    TimeStart AS SINGLE
    Routine AS INTEGER
    TimeEnd AS SINGLE
    ElapsedTime AS SINGLE

    '**** these have been added to help assess relative performance ****
    AccumulatedTime AS SINGLE '* new variable added to accumulate run time
    NumberOfRuns AS LONG '* track number of times a routine has been run
END TYPE

CONST MinRuns% = 16
'* you will want to display the results of running this program here

'* we will also later want a way to sort the results using the metric
'* to measure the performance factor of each method. higher performance
'* numbers mean better performance
'*
'*               ProfileCode(DispTimes%).AccumulatedTime
'* performance = ---------------------------------------
'*                ProfileCode(DispTimes%).NumberOfRuns
'*
'* Sort by Performance - use selectionsort, which is slow, but for few items, it is fine
'* avoid making decisions about which algorithm is best with too few runs

NRoutines% = 4
FOR Perf% = 0 TO NRoutines%
- 1
   FOR Perf2% = Perf% + 1 TO NRoutines%
        IF Performance!(ProfileCode(Perf%)) > Performance!(ProfileCode(Perf2%)) THEN
            SWAP ProfileCode(Perf%), ProfileCode(Perf2%)
        END IF
    NEXT
NEXT

'* show routines sorted by order of speed, from best performing to worst.

trialruns& = 0
FOR DispTimes% = 0 TO NRoutines%
    IF ProfileCode(DispTimes%).NumberOfRuns > 0 THEN
        PRINT ProfileCode(DispTimes%).Title
        PRINT ProfileCode(DispTimes%).ElapsedTime
        PRINT ProfileCode(DispTimes%).AccumulatedTime
        PRINT ProfileCode(DispTimes%).NumberOfRuns
        trialruns& = trialruns& + ProfileCode(DispTimes%).NumberOfRuns
    END IF
NEXT


'* give a warning if too few runs have been made to give accurate results

IF trialruns& < MinRuns% * (NRoutines% + 1) THEN
    PRINT "This may not reflect accurately on actual best-case results per algorithm."
END IF

FUNCTION Performance! (P AS Profile)
Performance! = P.AccumulatedTime / P.NumberOfRuns
END FUNCTION

'* so now the new listing looks like this:
'* Replaced Literals 4 with NRoutines%
'*                  16 with MinRuns%
TYPE Pixelrec
    red AS INTEGER
    green AS INTEGER
    blue AS INTEGER
    PointValue AS LONG
END TYPE
TYPE Profile
    Title AS STRING * 80
    TimeStart AS SINGLE
    Routine AS INTEGER
    TimeEnd AS SINGLE
    ElapsedTime AS SINGLE
    AccumulatedTime AS SINGLE
    NumberOfRuns AS LONG
END TYPE

capscreen& = _SCREENIMAGE
high% = _HEIGHT(capscreen&)
wide% = _WIDTH(capscreen&)
'* MinRuns% is arbitrary. set high enough or you may get inaccurate results
CONST MinRuns% = 16
NRoutines% = 4
SCREEN capscreen&
DIM ProfileCode(0 TO NRoutines%) AS Profile
DIM Pixel(wide%, high%) AS Pixelrec

DO

    FOR Routine% = 0 TO NRoutines%
       ProfileCode(Routine%).TimeStart = TIMER(.001)
        IF Routine% = 0 THEN
           '* get the current screen and load it into Pixel()
            ProfileCode(Routine%).Title = "Getting Pixel Values Into Pixel()"
            FOR y% = 1 TO high%
                FOR x% = 1 TO wide%
                    PixelInfo Pixel(x%, y%), x%, y%, 0
                NEXT
            NEXT
        ELSEIF Routine% = 1 THEN

            '* here is where an optimization could be used. while this code works, it is slowed by multiple calls to POINT, which itself is a "slow" function
            ProfileCode(Routine%).Title = "Get values into Pixel() non-optimized"

            FOR a% = 1 TO high%
                FOR b% = 1 TO wide%
                    Pixel(b%, a%).red = _RED(POINT(b%, a%))
                    Pixel(b%, a%).green = _GREEN(POINT(b%, a%))
                    Pixel(b%, a%).blue = _BLUE(POINT(b%, a%))
                    Pixel(b%, a%).PointValue = POINT(b%, a%)
                NEXT
            NEXT
        ELSEIF Routine% = 2 THEN

            '* so now we clean it up a bit, changing the multiple POINT calls into a single one at the beginning and calculating
            '* _RED, _GREEN and _BLUE values from that instead

            ProfileCode(Routine%).Title = "Getting Pixel Values Into Pixel() using Pixel(b%, a%).PointValue = POINT(b%, a%)"
            FOR a% = 1 TO high%
                FOR b% = 1 TO wide%
                    Pixel(b%, a%).PointValue = POINT(b%, a%)
                    Pixel(b%, a%).red = _RED(Pixel(b%, a%).PointValue)
                    Pixel(b%, a%).green = _GREEN(Pixel(b%, a%).PointValue)
                    Pixel(b%, a%).blue = _BLUE(Pixel(b%, a%).PointValue)
                NEXT
            NEXT
        ELSEIF Routine% = 3 THEN

            '* TYPE variable access is "slow" because of pointer offset calculations, so replace Pixel(b%, a%).PointValue with PointTemp&=Pixel(b%, a%).PointValue
            '* This code SHOULD run the most quickly.

            ProfileCode(Routine%).Title = "Getting Pixel Values Into Pixel() using PointTemp& = Pixel(b%, a%).PointValue"
            FOR a% = 1 TO high%
                FOR b% = 1 TO wide%
                    PointTemp& = Pixel(b%, a%).PointValue
                    Pixel(b%, a%).PointValue = PointTemp&
                    Pixel(b%, a%).red = _RED(PointTemp&)
                    Pixel(b%, a%).green = _GREEN(PointTemp&)
                    Pixel(b%, a%).blue = _BLUE(PointTemp&)
                NEXT
            NEXT
        ELSEIF Routine% = 4 THEN

            '* in case we want to try other optimizations
            '* now we will try doing smaller dimension in the inner loop
            ProfileCode(Routine%).Title = "Smaller Dimension Inner Loop"
            IF wide% < high% THEN
                FOR a% = 1 TO high%
                    FOR b% = 1 TO wide%
                        PointTemp& = Pixel(b%, a%).PointValue
                        Pixel(b%, a%).PointValue = PointTemp&
                        Pixel(b%, a%).red = _RED(PointTemp&)
                        Pixel(b%, a%).green = _GREEN(PointTemp&)
                        Pixel(b%, a%).blue = _BLUE(PointTemp&)
                    NEXT
                NEXT
            ELSE
                FOR b% = 1 TO wide%
                    FOR a% = 1 TO high%
                        PointTemp& = Pixel(b%, a%).PointValue
                        Pixel(b%, a%).PointValue = PointTemp&
                        Pixel(b%, a%).red = _RED(PointTemp&)
                        Pixel(b%, a%).green = _GREEN(PointTemp&)
                        Pixel(b%, a%).blue = _BLUE(PointTemp&)
                    NEXT
                NEXT
            END IF
        END IF
        ProfileCode(Routine%).Routine = Routine%
        ProfileCode(Routine%).TimeEnd = TIMER(.001)
        ProfileCode(Routine%).ElapsedTime = ProfileCode(Routine%).TimeEnd - ProfileCode(Routine%).TimeStart
        ProfileCode(Routine%).AccumulatedTime = ProfileCode(Routine%).AccumulatedTime + ProfileCode(Routine%).ElapsedTime
        ProfileCode(Routine%).NumberOfRuns = ProfileCode(Routine%).NumberOfRuns + 1
    NEXT
LOOP UNTIL INKEY$ > ""
SCREEN 0
_FREEIMAGE capscreen&

'* you will want to display the results of running this program here
'* we will also later want a way to sort the results using the metric
'* to measure the performance factor of each method. higher performance
'* numbers mean better performance
'*
'*               ProfileCode(DispTimes%).AccumulatedTime
'* performance = ---------------------------------------
'*                ProfileCode(DispTimes%).NumberOfRuns
'*
'* Sort by Performance -- uses selectionsort, which is slow, but for very few items, it is fine
'* you want to avoid making decisions about which algorithm is the best with too few runs
'*

FOR Perf% = 0 TO NRoutines% - 1
   FOR Perf2% = Perf% + 1 TO NRoutines%
       IF Performance!(ProfileCode(Perf%)) > Performance!(ProfileCode(Perf2%)) THEN
            SWAP ProfileCode(Perf%), ProfileCode(Perf2%)
        END IF
    NEXT
NEXT

'* now show the routines sorted by order of performance, going from best performing to worst.
'* show a warning if not enough trial runs have been made
trialruns& = 0
FOR DispTimes% = 0 TO NRoutines%
   IF ProfileCode(DispTimes%).NumberOfRuns > 0 THEN
        PRINT ProfileCode(DispTimes%).Title
        PRINT ProfileCode(DispTimes%).ElapsedTime
        PRINT ProfileCode(DispTimes%).AccumulatedTime
        PRINT ProfileCode(DispTimes%).NumberOfRuns
        trialruns& = trialruns& + ProfileCode(DispTimes%).NumberOfRuns
    END IF
NEXT

IF trialruns& < MinRuns% * (NRoutines% + 1) THEN
    PRINT "This may not reflect accurately on the actual best-case results per algorithm."
END IF
DO
LOOP UNTIL INKEY$ > ""

SYSTEM

SUB PixelInfo (P AS Pixelrec, Pixelinfox%, PixelInfoy%, Operation%)
SELECT CASE Operation%
    CASE 0 '*Get the pixel Value
        P.red = _RED(POINT(Pixelinfox%, PixelInfoy%))
        P.green = _GREEN(POINT(Pixelinfox%, PixelInfoy%))
        P.blue = _RED(POINT(Pixelinfox%, PixelInfoy%))
        P.PointValue = POINT(Pixelinfox%, PixelInfoy%)
    CASE 1 '* set the pixel value
        PSET (Pixelinfox%, PixelInfoy%), P.PointValue
    CASE ELSE
END SELECT
END SUB

FUNCTION Performance! (P AS Profile)
Performance! = P.AccumulatedTime / P.NumberOfRuns
END FUNCTION


I have left a few optimizations out so you can experiment with this code. One suggested improvement is to use SELECT CASE whenever there are more than 2 IF/THEN/ELSE statements in a block. Optimize your code first for maximum readability and this will make your life as a programmer far easier, as well as anyone else who needs to maintain or modify your code. In this version of the program I added a routine to check ifdoing the shorter dimension in the inside loop had any effect. One would think that since there are the same number of elements processed (pixels, in this case), it would make no difference. But indeed it makes all the difference in the world. One of the reasons is that stuff on the execution stack must be moved more frequently when the inner (in this case shorter) loop finishes. Your best bet is to always do the dimension that is larger or largest in the inner loop. so the shortest dimension should be the outer parts of a nested loop.


Try this code and see how it runs when compiled with QB64.
START! = TIMER(.001)
S& = 0
FOR X% = 0 TO 9999
    FOR Y% = 0 TO 999
        FOR Z% = 0 TO 99
            S& = S& + 1
        NEXT
    NEXT
NEXT
FINISH! = TIMER(.001)
PRINT FINISH! - START!, S&


now try it the other way:

START! = TIMER(.001)
S& = 0
FOR X% = 0 TO 99
    FOR Y% = 0 TO 999
        FOR Z% = 0 TO 9999
            S& = S& + 1
        NEXT
    NEXT
NEXT
FINISH! = TIMER(.001)
PRINT FINISH! - START!, S&

This one finishes around 6% faster. You might think, well, it's only 6%. but 6% on a slow computer makes a LOT of difference.

Another structural mistake I sometimes see is located here. This is another what I call egregious abuse of an otherwise quite useful programming construct. What I must ask is why this is taught at collegiate level. It is useful when used in things like my recursive merge program, traversing directory trees, getting data from optical or magnetic media or when used in quicksort, which by the way while I am on the topic, I will present.

SUB QuickSort (a(), Start&, finish&)
SELECT CASE finish& - Start&
    CASE 1
        IF a(Start&) > a(finish&) THEN
            SWAP a(Start&), a(finish&)
        END IF
    CASE IS > 1
        i& = Start&
        j& = finish&
        m = a(Start& + RND * (finish& - Start&))
        DO
            WHILE a(i&) < m
                i& = i& + 1
            WEND
            WHILE a(j&) > m
                j& = j& - 1
            WEND
            IF i& <= j& THEN
                SWAP a(i&), a(j&)
                i& = i& + 1
                j& = j& - 1
            END IF
        LOOP UNTIL i& > j&
        QuickSort a(), i&, finish&
        QuickSort a(), Start&, j&
END SELECT
END SUB


There are a couple problems with this. One is major, the other pretty minor. First, the bad news. This sort performs in polynomial (n^2) fashion when the wrong pivot is consistently chosen. The method used in this example for choosing the pivot is far less likely to encounter such horrendous computational behavior, whereas the other method of always choosing the middle array element between start& and finish& ie: a(start& +(finish& - start&) \ 2)can be defeated using "median of 3-killer" sequences." But it is far less than likely in practice to encounter this. One solution is to resort to mergesort past a certain level of recursion (typically log(n)/(2 Log 2)). This will guarantee a worst-case logarithmic running time, where without this addition and unfortunate pivot choices or the aforementioned "median of 3-killer" sequence, this sort can degenerate to quadratic (n^2) time, like bubblesort.

Now for the minor bad news which with a bit of corrective code can be avoided. QuickSort is unstable, meaning equal keys can switch relative order. This is a minor problem indeed and happens only with sets where there are equal keys. So how does this have to do with optimization? Choosing the wrong element for the pivot can have disastrous consequences on QuickSort's performance. So now you have another weapon against poorly performing code.

and now for the good news, which is the best news of all. the conditions i described here are rarely met in practice and this version of quicksort is not affected by the median of 3-killer sequence because of its random choice of pivot. you can expect good performance from this routine on a wide variety of datasets.

so not we add just a little more structure to the program by the effective use of SELECT CASE. as an example of how this structural tool can make your programs even more efficient.

I have found that SELECT CASE is about 20% slower than using block IF/THEN/ELSEIF. But that does not mean SELECT CASE is not valuable. I also have an interesting result that results from assigning iteration variables as the _BYTE type. this results in almost a 50% increase in speed. Try this code:

DIM LoopI AS _BYTE
FOR routine% = 0 TO 1
    exect& = 0
    timer! = TIMER(.001)
    DO
        IF routine% = 0 THEN
            FOR LoopI = 0 TO 9
                IF LoopI = 0 THEN
                ELSEIF LoopI = 1 THEN
                ELSEIF LoopI = 2 THEN
                ELSEIF LoopI = 3 THEN
                ELSEIF LoopI = 4 THEN
                ELSEIF LoopI = 5 THEN
                ELSEIF LoopI = 6 THEN
                ELSEIF LoopI = 7 THEN
                ELSEIF LoopI = 8 THEN
                ELSEIF LoopI = 9 THEN
                END IF
            NEXT
        ELSE
            FOR LoopI = 0 TO 9
                SELECT CASE LoopI
                    CASE 0
                    CASE 1
                    CASE 2
                    CASE 3
                    CASE 4
                    CASE 5
                    CASE 6
                    CASE 7
                    CASE 8
                    CASE 9
                END SELECT
            NEXT
        END IF
        exect& = exect& + 1
    LOOP UNTIL ABS(TIMER(.001) - timer!) >= 10
    PRINT exect&
NEXT

so now you have another weapon in your arsenal. If you know the range of numbers will be from 0 to 255 or (-128 to 127) and are integers, _BYTE is definitely the type to use!

so now we have another weapon to use to make this program even faster: we save on 2 fronts: lower storage requirements and faster execution.

TYPE Pixelrec
    red AS _BYTE
    green AS _BYTE
    blue AS _BYTE
    PointValue AS LONG
END TYPE

TYPE Profile
    Title AS STRING * 80
    TimeStart AS SINGLE
    Routine AS INTEGER
    TimeEnd AS SINGLE
    ElapsedTime AS SINGLE
    AccumulatedTime AS SINGLE
    NumberOfRuns AS LONG
END TYPE

capscreen& = _SCREENIMAGE
high% = _HEIGHT(capscreen&)
wide% = _WIDTH(capscreen&)
'* MinRuns% is arbitrary. set high enough or you may get inaccurate results
CONST MinRuns% = 16
NRoutines% = 4
SCREEN capscreen&
DIM ProfileCode(0 TO NRoutines%) AS Profile
DIM Pixel(wide%, high%) AS Pixelrec

DO

    FOR Routine% = 0 TO NRoutines%
       ProfileCode(Routine%).TimeStart = TIMER(.001)
        IF Routine% = 0 THEN
           '* get the current screen and load it into Pixel()
            ProfileCode(Routine%).Title = "Getting Pixel Values Into Pixel()"
            FOR y% = 1 TO high%
                FOR x% = 1 TO wide%
                    PixelInfo Pixel(x%, y%), x%, y%, 0
                NEXT
            NEXT
        ELSEIF Routine% = 1 THEN

            '* here is where an optimization could be used. while this code works, it is slowed by multiple calls to POINT, which itself is a "slow" function
            ProfileCode(Routine%).Title = "Get values into Pixel() non-optimized"

            FOR a% = 1 TO high%
                FOR b% = 1 TO wide%
                    Pixel(b%, a%).red = _RED(POINT(b%, a%))
                    Pixel(b%, a%).green = _GREEN(POINT(b%, a%))
                    Pixel(b%, a%).blue = _BLUE(POINT(b%, a%))
                    Pixel(b%, a%).PointValue = POINT(b%, a%)
                NEXT
            NEXT
        ELSEIF Routine% = 2 THEN

            '* so now we clean it up a bit, changing the multiple POINT calls into a single one at the beginning and calculating
            '* _RED, _GREEN and _BLUE values from that instead

            ProfileCode(Routine%).Title = "Getting Pixel Values Into Pixel() using Pixel(b%, a%).PointValue = POINT(b%, a%)"
            FOR a% = 1 TO high%
                FOR b% = 1 TO wide%
                    Pixel(b%, a%).PointValue = POINT(b%, a%)
                    Pixel(b%, a%).red = _RED(Pixel(b%, a%).PointValue)
                    Pixel(b%, a%).green = _GREEN(Pixel(b%, a%).PointValue)
                    Pixel(b%, a%).blue = _BLUE(Pixel(b%, a%).PointValue)
                NEXT
            NEXT
        ELSEIF Routine% = 3 THEN

            '* TYPE variable access is "slow" because of pointer offset calculations, so replace Pixel(b%, a%).PointValue with PointTemp&=Pixel(b%, a%).PointValue
            '* This code SHOULD run the most quickly.

            ProfileCode(Routine%).Title = "Getting Pixel Values Into Pixel() using PointTemp& = Pixel(b%, a%).PointValue"
            FOR a% = 1 TO high%
                FOR b% = 1 TO wide%
                    PointTemp& = Pixel(b%, a%).PointValue
                    Pixel(b%, a%).PointValue = PointTemp&
                    Pixel(b%, a%).red = _RED(PointTemp&)
                    Pixel(b%, a%).green = _GREEN(PointTemp&)
                    Pixel(b%, a%).blue = _BLUE(PointTemp&)
                NEXT
            NEXT
        ELSEIF Routine% = 4 THEN

            '* in case we want to try other optimizations
            '* now we will try doing smaller dimension in the inner loop
            ProfileCode(Routine%).Title = "Smaller Dimension Inner Loop"
            IF wide% < high% THEN
                FOR a% = 1 TO high%
                    FOR b% = 1 TO wide%
                        PointTemp& = Pixel(b%, a%).PointValue
                        Pixel(b%, a%).PointValue = PointTemp&
                        Pixel(b%, a%).red = _RED(PointTemp&)
                        Pixel(b%, a%).green = _GREEN(PointTemp&)
                        Pixel(b%, a%).blue = _BLUE(PointTemp&)
                    NEXT
                NEXT
            ELSE
                FOR b% = 1 TO wide%
                    FOR a% = 1 TO high%
                        PointTemp& = Pixel(b%, a%).PointValue
                        Pixel(b%, a%).PointValue = PointTemp&
                        Pixel(b%, a%).red = _RED(PointTemp&)
                        Pixel(b%, a%).green = _GREEN(PointTemp&)
                        Pixel(b%, a%).blue = _BLUE(PointTemp&)
                    NEXT
                NEXT
            END IF
        ELSEIF Routine% = 5 THEN
Get (1,1)-(High%,Wide%), StoreScreen%()

        END IF
        ProfileCode(Routine%).Routine = Routine%
        ProfileCode(Routine%).TimeEnd = TIMER(.001)
        ProfileCode(Routine%).ElapsedTime = ProfileCode(Routine%).TimeEnd - ProfileCode(Routine%).TimeStart
        ProfileCode(Routine%).AccumulatedTime = ProfileCode(Routine%).AccumulatedTime + ProfileCode(Routine%).ElapsedTime
        ProfileCode(Routine%).NumberOfRuns = ProfileCode(Routine%).NumberOfRuns + 1
    NEXT
LOOP UNTIL INKEY$ > ""
SCREEN 0
_FREEIMAGE capscreen&

'* you will want to display the results of running this program here
'* we will also later want a way to sort the results using the metric
'* to measure the performance factor of each method. higher performance
'* numbers mean better performance
'*
'*               ProfileCode(DispTimes%).AccumulatedTime
'* performance = ---------------------------------------
'*                ProfileCode(DispTimes%).NumberOfRuns
'*
'* Sort by Performance -- uses selectionsort, which is slow, but for very few items, it is fine
'* you want to avoid making decisions about which algorithm is the best with too few runs
'*

FOR Perf% = 0 TO NRoutines% - 1
   FOR Perf2% = Perf% + 1 TO NRoutines%
       IF Performance!(ProfileCode(Perf%)) > Performance!(ProfileCode(Perf2%)) THEN
            SWAP ProfileCode(Perf%), ProfileCode(Perf2%)
        END IF
    NEXT
NEXT

'* now show the routines sorted by order of performance, going from best performing to worst.
'* show a warning if not enough trial runs have been made
trialruns& = 0
FOR DispTimes% = 0 TO NRoutines%
   IF ProfileCode(DispTimes%).NumberOfRuns > 0 THEN
        PRINT ProfileCode(DispTimes%).Title
        PRINT ProfileCode(DispTimes%).ElapsedTime
        PRINT ProfileCode(DispTimes%).AccumulatedTime
        PRINT ProfileCode(DispTimes%).NumberOfRuns
        trialruns& = trialruns& + ProfileCode(DispTimes%).NumberOfRuns
    END IF
NEXT

IF trialruns& < MinRuns% * (NRoutines% + 1) THEN
    PRINT "This may not reflect accurately on the actual best-case results per algorithm."
END IF
DO
LOOP UNTIL INKEY$ > ""

SYSTEM

SUB PixelInfo (P AS Pixelrec, Pixelinfox%, PixelInfoy%, Operation%)
SELECT CASE Operation%
    CASE 0 '*Get the pixel Value
        P.red = _RED(POINT(Pixelinfox%, PixelInfoy%))
        P.green = _GREEN(POINT(Pixelinfox%, PixelInfoy%))
        P.blue = _RED(POINT(Pixelinfox%, PixelInfoy%))
        P.PointValue = POINT(Pixelinfox%, PixelInfoy%)
    CASE 1 '* set the pixel value
        PSET (Pixelinfox%, PixelInfoy%), P.PointValue
    CASE ELSE
END SELECT
END SUB

FUNCTION Performance! (P AS Profile)
Performance! = P.AccumulatedTime / P.NumberOfRuns
END FUNCTION

the fastest now averages (9/64)s 141ms to complete a single run of loading the screen into an array. surprisingly now doing the smaller dimension on the inner loop is now faster than the other way around. it clocks in at a respectable 144ms anf the slowest clocks in at 332ms, more than twice as slow as the 2 fastest algorithms. So now you have it. I have taken a simple Program and made it about as fast as it can possibly be.

Another kind of optimization is the elimination of repetitive code as I have done in this example.

TYPE minmax
    min AS INTEGER
    max AS INTEGER
END TYPE

TYPE mystuff
    a AS INTEGER
    increment AS INTEGER
    mm AS minmax
END TYPE
TYPE MyPoint
    p1 AS mystuff
    p2 AS mystuff
    p3 AS mystuff
END TYPE
DIM d(255) AS MyPoint

FOR x = 0 TO 255
    IF d(x).p1.a + d(x).p1.increment < d(x).p1.mm.min THEN
    ELSEIF d(x).p1.a + d(x).p1.increment > d(x).p1.mm.max THEN
    END IF
    IF d(x).p2.a + d(x).p2.increment < d(x).p2.mm.min THEN
    ELSEIF d(x).p2.a + d(x).p2.increment > d(x).p2.mm.max THEN
    END IF
    IF d(x).p3.a + d(x).p3.increment < d(x).p3.mm.min THEN
    ELSEIF d(x).p3.a + d(x).p3.increment > d(x).p3.mm.max THEN
    END IF
NEXT

FOR x = 0 TO 255
    AddPointStuff d(x).p1
    AddPointStuff d(x).p2
    AddPointStuff d(x).p3
NEXT

SUB AddPointStuff (PointRec AS mystuff)
AddPointStuffTemp =
PointRec.a + PointRec.increment
IF
AddPointStuffTemp < P.mm.min THEN
ELSE
    IF
AddPointStuffTemp > P.mm.max THEN
    END IF
END IF
END SUB


In the second example, the code is more gneralized and is indeed far easier to read. while it may not be faster, more times than not, neatness also counts. For example, you can put PointRec.a + PointRec.increment into a temporary storage variable and use this number instead for the comparisons. Whether it will be faster is anyone's guess, but my guess is that it will indeed be faster.

 

TYPE variables:

TYPE variables are very useful structures, however in ANY language, not just QB64, they are slower than scalar variables, an example of this being the variable thisvariable% as opposed to this.variable as in TYPE variables. My recommendation when using these, especially if they will be used a lot is to store the TYPE variables in scalar variables using similar names. Say you have a TYPE variable declared as such

TYPE Monkey
height as integer
weight as integer
likesbananas as _BIT
age as integer
END TYPE

dim DKM(0 TO 131071) as Monkey

if you're going to use any of these frequently, say you will use DKM().height a bunch of times, because offsets have to be calculated every time you use any type variable, whether it is part of an array or not, maybe store DKM().height in a variable called DKMHeight. Say you want to find all the monkeys in a particular height range and find their average height versus age and height versus weight and height versus age. Easy enough then.

for x=lbound(dkm) to ubound(dkm)
if dkm(x).height>=63 and dkm(x).height<=86 then
if dkm(x).weight/dkm(x).height>=.25 and dkm(x).weight/dkm(x).height<=.35 then
if dkm(x).height/dkm(x).age>=4 and dkm(x).height/dkm(x).age<=6 then

end if
end if
end if
next

Yes, this works, but nt optimally. this is an extreme case of using a single variable many times. Optimizing compilers will see that dkm(x).height is used a bunch of times and will replace each occurrence where it is not altered with some scalar variable. so you could make this replacement yourself if you do not have an optimizing compiler. The next step in this process is to replace dkm(x).height with something like dkmxheight and make sure it is the same kind of variable as dkm(x).height, which in this case is an integer. no problem then. now we do what is called the "plug and chug."

dkmxheight% = dkm(x).height
FOR x = LBOUND(dkm) to UBOUND(dkm)
   IF dkmxheight >= 63 THEN
      IF dkmxheight <= 86 THEN
         IF dkm(x).weight / dkmxheight>= .25
            IF dkm(x).weight / dkmxheight <= .35 THEN
               IF dkmheight/dkm(x).age >= 4 THEN
                  IF dkmheight/dkm(x).age <= 6THEN

                  END IF
               END IF
            END IF
         END IF
      END IF
   END IF
NEXT

Please test these code fragments yourself and you will see the second example runs more quickly than the one using dkm(x).height. This is because TYPE variables require calculations of offset addresses because of the way they are stored in memory. For a good example of the way programs can evolve, check this example of recreating the ATAN2 function http://www.qb64.net/forum/index.php?topic=1521.msg15674#msg15674