Small Basic: Optimization
This article explains how to optimize, improve, the performance of a Small Basic program. Before beginning, the reader should be aware that the terms " speed " and " performance " are interchangeable in programming. Also, " main loop " and " interactive loop " are viewed as identical. The meaning of " performance critical loop " is a broader term to the former terms (loops) and include, but not limited to, loops that aren't the center of the program and are time critical such as, event driven loops. It's important to remind the reader that Small Basic isn't meant for performance. Nonetheless, here are some general tips that are important to practice early so it can have more of an impact if the reader chooses to progress to a more complex language. With the simplicity of Small Basic, it offers an ideal opportunity to begin learning the basics of this topic.
Keep Arrays Small
Prevent unnecessary build up of a specific array because the greater the size the slower a program can manipulate or read it. For instance, in the game 2048 (by Nonki Takahashi), instead of adding another value to the array, board[], which encompasses other values such as, occupied tiles, a new array, space[], is created to search for empty spaces exclusively in the board and, therefore, is faster to read.
Know When To Use Multi-Dimensional Arrays
Know when to use multi-dimensional array (cell[1][1] = 1) vs. simple variables (x = 1). Simple variables are read faster, but multi-dimensional arrays are useful for organizing data.
In the key stroke recorder (THS130)*, using the following multi-dimensional array is more efficient than using several variables for storing time and text.
Multi-dimensional array:
record[nextRecord]["time"]
record[nextRecord]["text"]
Deleting data with a multi-dimensional array:
record = ""
Even though, calling a multi dimensional array is slower as oppose to using simple variables, It is faster deleting data as oppose to deleting several variables, which would have required a large scale loop. For example, if the user records 500 words and decides to delete the recorded data, instead of looping 500 times, which would have hinder the performance of the program, the program simply deletes with record = "". The reader is encouraged to read the full code of the key stroke recorder in context to this example.
Now, let's swap perspectives and look at the first response in the following thread: Shuffling a deck of cards. This thread demonstrates the unnecessary usage of an multiple dimensional array and increasing the performance of a program by using simple variables instead.
*Special thanks to Yan Grenier for helping me polish and improve the key stroke recorder.
Remove Non-Usable Data
Excessive shapes or sprites can impact the performance of a program. One common problem in programs that feature graphics are shapes that aren't properly removed. Be sure to keep track of all sprites or shapes that are created and clear or remove any that aren't used. Simply hiding shapes/sprites will continue hinder the speed of a program. Refer to this thread (last response): Duck Shoot bug. Also, read Small Basic: Sprite Arrays.
Here is the final product of Jibba jabba's Eora Duck Hunt from the previous thread. This is a prime example of displaying the creative utilization of shapes while maintaining the overall performance of the program by efficiently removing and using shapes accordingly.
Code Placement in Performance Critical Loops/Code Blocks
- Limit the use of check points (If, Else statements), small basic commands, or calculations that are unrelated in performance critical loops /code blocks such as, recording time or mouse interaction.
- When reasonable, initialize variables, graphics, or any calculations before the main loop. This way, data is retrieved as oppose to being created at the moment.
- The Following program displays all the techniques mentioned above as well as additional techniques mentioned below in context to it: TLP645
- Calculations moving a falling block are done separately from initiating the actual movement of a block. Ex) Calculating the next position for a falling block is done in updatePiece() while performing actual action is processed in createBoard().
- Multi-dimensional arrays are organized according to each major aspect of the program. Ex) Data related to a falling block is categorized under " dropped ". Data related to a block's attributes (size, color, etc.) is named " piece ".
Nested Loops
For j = 1 To 10
' Do something (outer loop)
For i = 1 To 10
' Do something (inner loop - need to optimize first in many case)
EndFor
EndFor
In the code above, inner loop is called 100 times and outer loop is called 10 times. To increase speed, keep calculations, variables, and small basic commands that are unrelated limited in the inner loop.
Nested loops should not do the same thing twice. Instead, it can reduce the use of small basic commands and variables. With the nested loop technique, Shapes.AddRectangle() is used only once and creates a grid of blocks. Click on this link for an example: TLP645.
Reverse Loops
Use a reverse loop when order isn't important. The following link provides an example and more details: Programming Tips - some general programming tips.
Investigating Which Part of a Program is Slow
When investigating where a program's performance is lacking , it is generally done by testing it. Usually, the feature that begins to slow considerably upon utilization is an indication and, in turn, prompts further investigation of the code related to it. Conversely, when the problem requires minute observations, use the Clock.ElapsedMilliseconds property in performance critical parts of a program.
start = Clock.ElapsedMilliseconds 'STARTING POINT
'CODE BLOCK HERE
elapsed = Clock.ElapsedMilliseconds - start 'RECORD ELAPSED MILLISECONDS
TextWindow.WriteLine(elapsed + " milliseconds elapsed") 'PRINT ELAPSED MILLISECONDS
start = Clock.ElapsedMilliseconds 'RESTART STARTING POINT IF THERE IS A LOOP
A brief word about code block above. The variable " start " stores the number of seconds that have elapsed when the program starts. The program reads the code. Then the current number of seconds that have passed, clock.ElapsedMilliseconds, is subtracted by the starting point, " start ", which is the number of milliseconds elapsed. Another start is placed below in the event that you're recording in a loop and prevents " elapsed " from building up.
This thread provide more code samples on how to use the Clock.ElapsedMillisecond property to track the performance of a program: Programming Tips - some general programming tips. Another related thread: Optimization.
Depending on the set up of the code, the elapsed milliseconds displayed may vary each time the program is ran or looped. It's important to test the same block multiple times to gain an average of elapsed milliseconds it takes to perform a specific code block.
Approach Complex Problems with Simple Solutions
Simple solutions to a complex operation can improve the performance of a program.
The following example filters any input, except numbers, so if I were to input " Ezra94 " then the program will return " 94 ", for readability sakes, let's call it the " letter filter ":
Here's an inefficient approach: SDD265
Now, here's an efficient approach: VNR260
Both examples do the same task. However, the second performs faster since it doesn't have to loop and read every string/number. The original post can be viewed here.
Here's an in-depth discussion about approaching complex problems with simple solutions. The program discussed in the aforementioned thread is the " key stroke recorder ". Be particularly mindful of the difference in code structure (of each feature) between the first and last version of the key stroke recorder.
A word about the letter filter and the key stroke recorder. The efficient versions of both programs display a fine example of simple solutions to a complex operation, however, the nature of their problems are different. To make the letter filtering system efficient, it was a matter of knowing when to use the proper tools, as oppose to the key stroke recorder, it was a matter of building a time recording system from scratch.
Here are some tips to writing a simple solution:
- Don't write two pieces of code that does the same thing (This is not to be mistaken with using a subroutine more than once)
- Be mindful of code placement
Set the Size of Images Before Using Them in a Program
Make downloadable images the same size as they will appear when a program is running. If an image is 200 x 200 pixels but, in the program, it's displayed as 100 x 100 pixels then it'll take user time to resize the image. Instead, manipulate the size (using image manipulation software such as, GIMP)* of selected images according to their use.
To load an image use:
ImageList.LoadImage()
To display use:
GraphicsWindow.DrawResizedImage()
Or
Shapes.AddImage()
Refer to documentation on proper syntax for commands above.
*Note: here is a Video or two on image manipulation using GIMP. Also, refer to this blog for more info on using software like GIMP.
Balancing Animation and Performance
Manipulating a static image may not be any different than shapes. However, with a program that displays an animation by cycling frames, it's critical that each frame is removed properly as well as taking account of the rate of the cycle and the number of frames. Here is a Graphics With Frame Animation Prototype by litdiv that will guide reader the through process of balancing all the previously mentioned variables and the performance of a program. Note: this is only a prototype so the images need to be provided and implemented. For more info refer to Small Basic: Sprite Arrays.
Keep Event Handlers Simple
Avoid calling small basic commands, computing calculations, or declaring variables in code blocks that handle events (read Small Basic: Dynamic Graphics by litdev for more information and examples).
Balancing Optimization with Other Factors
It's important to note that optimization shouldn't be the main goal when writing a program. Often times, when creating a feature, ask yourself, "Does this need to be optimized ?". Certain areas or features may require more optimization than others. When creating a prototype of a feature it should be avoided until it's a complete feature and set to be implemented in the main program itself. Consider balancing these other factors when writing a program:
- Security--Protection from malware and hackers. Here is a link about secure programming with Unix to get started, which provides some tips applicable to any language.
- Price--Most projects have at least one of the following factors to balance: cost of development tools, the number of developers involved, amount of development time to create and implement features. In any case, developers have to prioritize these conditions accordingly when creating, adding, or optimizing a certain feature because it can cost valuable development time that can be focused elsewhere.
- Code Maintainability--Readability of code. Can changes be easily implemented?
- Logic of Code --The logic and structure of the code of each feature. Read paragraph in this article titled "Approach Complex Problems with Simple Solutions".
- Productivity--Writing a program is already time consuming task and using these practices can take even more time. For this reason, don't obsess over these factors and apply it to every detail of a program. You may want to ask yourself, "I am reaching my goals in a timely fashion? What have I accomplished so far?." The key is to apply one or two of these optimization practices when needed. In some cases, optimization may not be necessary and other factors may need to be polished instead such as, maintainability.
As a beginner, many of the points previously mentioned should be approached slowly and individually.
Have additional tips or questions?
Post here.
Additional Resources
- Optimizing (original forum thread of this article)
- Programmers At Work > Bill Gates - 1986 (has story about performance trade-offs)
- Can I optimize this more? (forum thread)
- Duck shoot exe only runs well when the SB ide is open (forum thread)
- Optimizing Loops (General information on optimization)