More Optimizing Techniques

Constraining values within boundaries

Code snippets used to keep x within 0 and 10:

Dual If'sIf X>10:10→X
If X<0:0→X
Dual When'swhen(x>10,10,when(x<0,0,x))→x
If...Then...ElseIf...Then...EndIfIf x>10 Then
10→x
ElseIf X<0 Then
0→x
EndIf
min/maxmin(10,max(0,x))→x

Efficiency for the various code snippets

100 iterationsSize (bytes)Executions per secondSpeed relative to slowestSize relative ti largest
Dual If's3.3811829.610096.7
Dual When's3.2811130.510391
If...Then...ElseIf...
Then...EndIf
3.1812231.4105.9100
min/max2.9510033.9112.782

So what does this all mean?

Well it shows that using min/max is that the smallest and fastest so it should be used whenever possible. To use min/max remember that the syntax is min(upper bound,max(lower bound, value ). When you need more flexibility for I would say when's are good mix between speed (If...Then...ElseIf...Then...EndIf get about one more executions per second) and are the the second smallest for size. Otherwise I'd say to use a If...Then...ElseIf...Then...EndIf structure to get a speed over If's (about 2 executions more a second) and they take only 4 bytes more than using If's.

For loops and better ways

Just for a lark you decide to write a function to calculate factorials for you. Now of course you know there is a factorial function built-in, but that not going to stop you as you want to see how small and fast you can write one (If you'd like to duplicate the factorial function please do so now because otherwise you'll probably see the smallest and quickest possible in TI-BASIC below).

So while throwing together a factorial function you come up with fac1() Unfortunately it is abysmally slow so you endeavor to create a smaller function. While looking in the appendix in the manual or catalog on the calc you find this thing called seq(). You find the syntax is seq(expression, variable, start, stop, [step]) and that seq() returns a list. While browsing under the Math>List ( [2nd] [5] [3] ) you notice this thing called product. You type 'product({3,2,5})' on the home screen and lo behold the answer is 30. Pondering the clue a moment a revelation occurs, make a list numbered for 1 to the size of the factorial and then pass the list to product. Moments later you have fac2() written.

Wow, that is almost 4 times faster than fac1() and much smaller. Let's call it a day... Wait there is more when you looked up product in catalog to see if it had any addition arguments you saw a ½ thing (a capital Greek P or pi). Noticing that it is takes arguments just a seq() does you try writing fac3() Amazingly it works and presto you have a tiny program to calculate factorials only about 3 times slower than using factorial instead of about 10 times slower. You feel vindicated.

Fac1()fac1(n)
Func
Local a,c
1→c
For a,1,n
a*c→c
EndFor
EndFunc
Fac2()fac2(n)
Func
product(seq(a,a,1,n))
EndFunc
Fac3()fac3(n)
Func
½(a,a,1,n)
EndFunc

Efficiency for the functions

Size in bytesTime to execute
50 times in seconds
Executions/secondSpeed relative
to slowest
Speed relative
to largest
10!1.3836.2188
fac1(10)5811.54.3100100
fac2(10)273.5814168.946.6
fac3(10)263.2815.2171.544.8

What's to be learned?

Well it means to avoid using looping structures whenever possible. Seq() is extremely powerful and flexible for example, try creating a list that contains the ASCII value (use ord() ) for each character in a string without a predetermined length. If you need to get a sum while using seq() don't use sum(seq()), instead use …() ( … is the Greek S or sigma, which can be typed by [2nd] [G] [SHIFT] [S] or [2nd] [4] ) and above all experiment to find new smaller and quicker ways to do things, who would guess that 'expr("5:stop")' in a program would display '5:stop' on the home screen (Thanks to the TI-89/92+ discussion group at TI and if anyone knows who found this out please let me know).

Thins you may not know about toenization

So you have done virtually everything to make you program smaller by changing the way to solve problem by the quickest smallest way and now you just can't seem to make it any smaller. Fear not for there may be a way to cut size of the program even more.

TI-BASIC before it can be run needs to be tokenized. By tokenizing the size of a program often decreases and it runs faster than if when running the calc tokenizes each line every time it the line is run. By knowing the size of tokens (the code that says If, Then, ClrDraw, etc.) one can determine what code will be smallest and possible even quickest.

Numbers

 Decimal integers are stored as binary with 0 taking 2 bytes 1 to 255 taking 3, 256 to 65535 taking 4 bytes and so on.

 Numbers enters as hexadecimal or binary (using 0h or 0b) takes two bytes plus what the number would take if it were represented as a decimal integer.

 Real numbers (meaning the number has a decimal point, ie 10. is a real) always take 10 bytes to store and can contain up 14 digits.

Operations

 A negative sign take 1 byte, so -10 takes 4 bytes to store.

 Implied multiplication does not save space. In fact the calc puts the multiplies in for you if you happen to leave them out.

 [STO] (→ or store) takes 1 byte.

Strings

 Strings take 3 bytes to create plus however many characters in the string so "123 Hi" takes up 9 bytes.

Variables

 Variables 'a' thru 'z' only take a single byte, while Ø (Theta) takes three bytes.

 Variable names with multiple characters take 2 bytes plus the number of characters in the name, so 'myvar' would be 6 bytes.

 Local variables take 1 extra byte tacked on to whatever size it would otherwise have been, so 'a' would be 2 bytes if local.

 The Local token itself is 3 bytes and Delvar itself is 3 bytes as well, however since Local make every variable used 1 byte larger use Delvar whenever possible to save space.

Quitting

 Stop is 2 bytes, and return is 3 bytes

So what does this all mean?

Well first of all, always use integers as decimals and avoid using reals when possible. Use the commutative properties of addition and subtract numbers instead of adding a negative number. Use variable names 'a' to 'z' first to save space, because at worst they take only 2 bytes when used as locals. Use Stop instead of Return only when space is a must as Stop terminates execution, not a good thing if a program called your program and expects to finish running (a shell is an example of this) or better yet don't use a Stop or Return and save 4-5 bytes (2 bytes for a colon or return) letting the program terminate with EndPrgm.

Last, but not least, how to make "5→a" smaller

Notice how numbers other than zero take at least 3 bytesm and variables 'a' thru 'z' take only 2 bytes? Well, now you have and that means there is a way to save space. Use constants to save space.

tes1()
Prgm
Local a
5->a
5->a
5->a
5->a
5->a
5->a
5->a
5->a
5->a
5->a
EndPrgm
test1()
Prgm
Local a,b
5->b
b->a
b->a
b->a
b->a
b->a
b->a
b->a
b->a
b->a
b->a
EndPrgm
100 bytes99 bytes

This just goes to show it only takes 10 replacements of 5 by 'b' to payoff (9 replacements and they are even at 92 bytes) If you used Delvar to cleanup afterwards then the payoff would be even quicker, try it and see. However constants come at a price, legibility of a program will be reduced.

Copyright 1998 Michael Van Der Kolk