The CASE for less IFs (or maybe NOT!)
simonl
Posts: 866
Not sure if this is widely known, but I thought I'd share it anyway:
There are times when we might want to see (for instance) which command we've received. In such times, I generally opted for:
Nothing wrong with that! However, the Propeller has to do every IF test, even if the command has already been acted upon.
I've now found that it's possible to do:
Notice the case 1 == 1? This allows us to use a "case" structure, which means the Propeller jumps out of the structure as soon as the command has been acted upon (I believe!).
Even if I'm wrong about that, I think it looks "cleaner" anyway!
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Cheers,
Simon
www.norfolkhelicopterclub.com
“Before you criticize someone, you should walk a mile in their shoes. That way when you criticize them, you are a mile away from them and you have their shoes.” - Jack Handey.
Post Edited (simonl) : 5/5/2009 11:44:03 PM GMT
There are times when we might want to see (for instance) which command we've received. In such times, I generally opted for:
if strcomp( string( "cls" ), @cmd[noparse][[/noparse] 0 ] ): doCmdCLS done := 1 if strcomp( string( "hello" ), @cmd[noparse][[/noparse] 0 ] ): doCmdHello done := 1 if strcomp( string( "toggle" ), @cmd[noparse][[/noparse] 0 ] ): doCmdToggle( @cmd[noparse][[/noparse] parStart ] ) done := 1 if strcomp( string( "clear" ), @cmd[noparse][[/noparse] 0 ] ): doCmdClear done := 1 if strcomp( string( "Flash LED" ), @cmd[noparse][[/noparse] 0 ] ): FlashLED done := 1 ' ------------------------------------------------- ' Catch invalid commands. ' ------------------------------------------------- if( done <> 1 ) ser.str( string( "Invalid command!") ) done := 0
Nothing wrong with that! However, the Propeller has to do every IF test, even if the command has already been acted upon.
I've now found that it's possible to do:
case 1 == 1 strcomp( string( "cls" ), @cmd[noparse][[/noparse] 0 ] ): doCmdCLS strcomp( string( "hello" ), @cmd[noparse][[/noparse] 0 ] ): doCmdHello strcomp( string( "toggle" ), @cmd[noparse][[/noparse] 0 ] ): doCmdToggle( @cmd[noparse][[/noparse] parStart ] ) strcomp( string( "clear" ), @cmd[noparse][[/noparse] 0 ] ): doCmdClear strcomp( string( "Flash LED" ), @cmd[noparse][[/noparse] 0 ] ): FlashLED ' ------------------------------------------------- ' Catch invalid commands. ' ------------------------------------------------- other: ser.str( string( "Invalid command!") )
Notice the case 1 == 1? This allows us to use a "case" structure, which means the Propeller jumps out of the structure as soon as the command has been acted upon (I believe!).
Even if I'm wrong about that, I think it looks "cleaner" anyway!
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Cheers,
Simon
www.norfolkhelicopterclub.com
“Before you criticize someone, you should walk a mile in their shoes. That way when you criticize them, you are a mile away from them and you have their shoes.” - Jack Handey.
Post Edited (simonl) : 5/5/2009 11:44:03 PM GMT
Comments
Wow.. Nice find.. Can someone verify this?
OBC
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
New to the Propeller?
Visit the: The Propeller Pages @ Warranty Void.
But you can simply write
case -1
instead of 1==1. Which simply is the same. 1==1 is simply a boolean expresion which will be resolved during runtime (needs runtime of course). True is represented as -1, false as 0. Strcomp also returns a boolean true or false.
But this also ends in a bunch of string-compares if the command is at the end of the list. For many commands I prefere the solution where you create a hash value of the command string and use the case statement to compare numbers. This is much faster and you don't need to store the·text of all the commands. One long/word per command is enough.
Post Edited (MagIO2) : 5/5/2009 9:51:54 PM GMT
I say 60% because (as you'll see if you run my attached codes) IF is slower than CASE some of the time.
The two attached codes are identical except for the CASE and IF structures. They're code for some testing I'm doing with serial comm's, and accept the following commands:
1. cls
2. hello
3. toggle <pinNumber>
4. clear
5. flash
Operation:
* Send to Propeller
* Start PST
* Hit enter
* Type command & hit enter
Here are the counter readings that I got:
Strangley "flash" and "claer" (intentionally wrong!) sometimes give very different readings, and I can't figure-out why!
I'm not sure what to make of this now - LOL.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Cheers,
Simon
www.norfolkhelicopterclub.com
“Before you criticize someone, you should walk a mile in their shoes. That way when you criticize them, you are a mile away from them and you have their shoes.” - Jack Handey.
Post Edited (simonl) : 5/5/2009 10:26:34 PM GMT
I'm not sure I follow what you're saying there! Would you give an example?
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Cheers,
Simon
www.norfolkhelicopterclub.com
“Before you criticize someone, you should walk a mile in their shoes. That way when you criticize them, you are a mile away from them and you have their shoes.” - Jack Handey.
Ultimately, this is a data structures problem that has been studied immensely. Personally, I'd prefer binary trees (O(n log(n)) if I remember right) since they can be expanded easier, and don't take as much memory as hash tables. With this small amount of commands though, neither would be any significant improvement over simply listing them as you have done. It might be worth looking into though if you want lot's of commands (>100 maybe).
You have revealed an interesting aspect of spin. Thanks for that! I never new a case of CASE could be an expression ... of course that means it could be a variable too. CASE implementation is kind of freaky though and actually uses more code bytes than IF ELSEIF ELSE.
Cheers.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
--Steve
Propalyzer: Propeller PC Logic Analyzer
http://forums.parallax.com/showthread.php?p=788230
Just for a laugh (hey, it's 0:23 here in UK!) I re-coded using elseif, and the readings are as follows:
So, again, some bizarre results! And again, CASE is often quicker, but I still don't understand why.
@SLRM: Thanks for the info
@localroger: Interesting findings. I like your use of RETURN - another option I'd not considered. I'm not sure if what I've shown with this CASE trick is so clever though - LOL
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Cheers,
Simon
www.norfolkhelicopterclub.com
“Before you criticize someone, you should walk a mile in their shoes. That way when you criticize them, you are a mile away from them and you have their shoes.” - Jack Handey.
Strange; I looked at program usage and found:
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Cheers,
Simon
www.norfolkhelicopterclub.com
“Before you criticize someone, you should walk a mile in their shoes. That way when you criticize them, you are a mile away from them and you have their shoes.” - Jack Handey.
Post Edited (simonl) : 5/5/2009 11:48:48 PM GMT
Now I really must get some shut-eye - it's 01:16 aaargh!
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Cheers,
Simon
www.norfolkhelicopterclub.com
“Before you criticize someone, you should walk a mile in their shoes. That way when you criticize them, you are a mile away from them and you have their shoes.” - Jack Handey.
Limited to 16? Uuugh.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
--Steve
Propalyzer: Propeller PC Logic Analyzer
http://forums.parallax.com/showthread.php?p=788230
note the addition to Keyboard.spin.
I don't think execution speed is a an issue for keyboard input, I prefer clarity and utility unless I run out of storage.
!!!Not all the pins are the standard ones!
Post Edited (Bill Drummond) : 5/6/2009 2:06:30 AM GMT
John Abshier
I tried some timing as well, but I changed your code to keep every branch consistent and on the same playing field. Using case -1 does shave 320 clocks off of the end result, but something I noticed in my observations, is that the IF/THEN statements are more consistent if you want some kind of determinism, where as the CASE statements seem to progressively increase in clock time the further down in the chain you get. Also the IF/THEN actually produces shorter code by a few longs than the CASE statements.
So in conclusion, if you want timing to be "more consistent" use the IF/THEN ... If slightly more speed is important and you only have 3 or 4 branches, use the CASE function. Anything beyond 5 branches you don't really gain anything in the way of faster execution speed. It actually starts counting against you.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Beau Schwabe
IC Layout Engineer
Parallax, Inc.
Post Edited (Beau Schwabe (Parallax)) : 5/6/2009 8:52:16 PM GMT
This is true. A CASE statement is a serial construct that progressively compares each statement as it works its way down the chain and jumps out as soon as the condition is met (somewhat like a S/A A/D converter staircase). Your series of IF statement run every comparison every time and the only difference is which condition code is run. In that case the first instance (done :=1) is going to be faster than the others as it requires one less byte of spin code than the others. (Pushing 1 on the stack is faster than the others due to the way the compiler/interpreter handles that literal)
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
"VOOM"?!? Mate, this bird wouldn't "voom" if you put four million volts through it! 'E's bleedin' demised!
Is there a modern computer language that doesn't have some form of a case statement?
Who would want one?
If execution speed was an issue, is SPIN a logical choice?
Are there programmers who spend more time debugging their code than writing it?
LONG LIVE CLAIRTY, waste the computers time not mine.
Probably most. Especially if you count testing as debugging, after all you have to find the bugs first.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
For me, the past is not over yet.
Just like to mention a little bug in your code
The assignment is not putting the string into the byte-buffer. It's putting the long sized pointer to string "A" into the cmd buffer. This bug is 'fixed' by another bug:
As you pass the content of cmd to the stringcompare. Usually if you want to compare something with a buffer you'd write
strcomp( string("xy"), @cmd )
@ all
I like the hashing version because it simply saves memory. Of course this comes along with an added runtime for creating the hash value. But I don't see a reason for code that deals with user input to be as fast as possible. And the hashing function could be optimized as well. If you simply·XOR the 2 first with the 2 last characters resulting in a word size hash value of the command-string this might be sufficient to distinguish·all the commands you use. You could build the hash value while you enter the command - that's where you have plenty of time.
Say you have a complex controller with 30 commands with an average of 5 characters. You need 30 * 6 bytes (one additional byte for string terminator) = 180 bytes for storing the commands vs 60 bytes for hash-values. And of course it makes a difference if you use a strcomp in if or case instead of a constant. There you have additional savings.
·
That's kind of strange - if I'm reading your output right - as it looks like CASE is therefore more memory hungry, but that contradicts what I found in the PropTool!
I was in the middle of writing the following summary when I saw your post - I think it still holds?!
So to summarise:
* A "case" structure takes less memory
* Use "case -1" instead of "case 1==1"
* If you're after more deterministic timing, use a series of IF statements
* An ElseIf structure is faster than straight IFs
* The further down the "Case" and "ElseIf" structure that a matching condition is found, the longer the structure takes to process. This isn't the case in an "If" structure.
* The timing differences are extremely small, so your choice of structure really depends on your coding "style"
The up-shot is therefore: none of this really matters (!), but I've had fun exploring it, and learned some stuff along the way
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Cheers,
Simon
www.norfolkhelicopterclub.com
“Before you criticize someone, you should walk a mile in their shoes. That way when you criticize them, you are a mile away from them and you have their shoes.” - Jack Handey.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
lonesock
Piranha are people too.