Visual Designers and SELECT CASE AS LONG

Started by Theo Gottwald, March 01, 2008, 09:27:41 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Charles Pegge

Hello Bob,

Thanks for your comment on SELECT .. AS CONST.

I ran some further tests to check the jump table sizes, and it is definitely 6 bytes * max number. With a span limit of 3000 entries as far as I can tell. Assuming you are using indirect jumps (4 bytes) , I think that the discrepancy is due to the fixup tables that EXEs use for resolving addresses at run time.

Bob Zale

Hi Charles...

Your statement was:  " seems to be 6 bytes*max case number for the table..."

Fortunately, fixup entries in a portable executable (PE) file are not a part of the memory image, nor are they part of the SELECT CASE jump table, as you stated.  The table is four (4) bytes per CASE number from smallest to largest, not per max case number.

That makes an astounding difference in many situations.

Best regards,

Bob Zale
PowerBASIC Inc.

Charles Pegge

Yes Bob, I was just looking at the file size rather than considering the memory image.

One interesting result we found is that for small numbers of cases, (say 10)  SELECT AS LONG has a very similar timing to SELECT AS CONST. This is probably due to the CPU's branch prediction abilities.
They do some amazing tricks these days and one cannot be  certain how long a sequence of ops will take.

Charles Pegge

An Idea:

For very large constant spans, it is possible to set up a virtual jump table. This example shows how this can be done. With a span of 100,000, which would not be tolerated by the compiler, and would, in any case add 600k to the size of the file, this code will achieve similar performance to SELCT CASE AS CONST .

' Creating a virtual jump table for extreme constants



    LOCAL i,j,vtable,which  AS LONG
    DIM t(1e5) AS LONG

    j=CODEPTR(xit) ' default location
    FOR i=0 TO 1e5:t(i)=j:NEXT
    t(01024)=CODEPTR(aa) ' case constant
    t(42935)=CODEPTR(bb) ' case constant
    t(99900)=CODEPTR(cc) ' case constant

    ' select case as const
    which=42935 'example

    ! mov eax,which
    ! shl eax,2
    ! add eax,vtable
    ! jmp dword ptr [eax]

    aa: 'case
        MSGBOX "AA": GOTO xit
    bb: 'case
        MSGBOX "BB": GOTO xit
        MSGBOX "CC": GOTO xit

    xit: ' end select


Theo Gottwald

@Charles, you are right, and that exactly is what PB does with SELECT CASE as CONST.
Let me add that in the case of an comparisson of "IF THEN ELSE" and SELECT CASE AS LONG" speed differences are so small that they can be even different on other manufacturer CPU.
Therefore I assume both as equal. While for good reasosn, the "normal" SELECT CASE is slower, because for more general use.
We see this often, that a tool that is less specific for a case, (= more general) is a bit slower.

I already posted on this case, here:
SELECT CASE Comparison

But - at least PB 8.03 seems to had an limitation of a maximum of 3057 choices for SELECT CASE AS CONST before getting the Error 402.

Now you wrote:
QuoteA structure with 20,000 CASE's executes in the same elapsed time as one with 2 CASE's

Was there a table-increase in PB 8.04 or do you talk from something we all do not know if it exists, but hope it will be available soon (PB 9)?
Let me state that I have never needed 3000 cases, but just wondered that you give this number 20000.

I personally do not need such an big jump table, and therefore I do not wait for this, really - but I can imagine bussiness programms who would like to get that much big.

There is one limit I do not really understand and this Limit is not 3000 but 6. Its the "include/Macro nesting Limit".
People making small programms and utilities upto 200, 300 kb may not even know that this exists.
But then on bigger projects, this limit may show its teeth.

When I load an Include file, this one loads an include file, that loads an includefile and that file calls a MACRO that calls a MACRO that calls a MACRO.

This limit is not 8, its not 32 or 256 its 6. I do not understand how it comes just to this number 6.

But anyway, I can say, that I hope, that this limit will fall one day in the future, as this would make some things a bit easier.
Even if it would be - for example just 38 (38 = 32 + 6 :-)  I'd be Ok.

Charles Pegge

Theo, I also get the same limitation of ~3000 CASE number span on PB version 8.04. The virtual table solution is a way out of this, and minimises the file size. Perhaps a future release of PB could adopt this approach, who knows :)

Charles Pegge

Well the combination of inline assembler and Basic makes just about any programming technique possible. The language is virtually future-proof  but you don't get the symbolic notation to go with new ideas or paradigms.

Theo Gottwald

Well, if you's ask me, this is one of the fields I would be most happy of improvements.
At least PB has a very reliable inline assembler already and the possibility to make inline Macros.
But of course there is more thinkable.

Donald Darden

I think the original question as less to do about speed than it does about understanding what the code does.  You ask, "Why not use SELECT CASE AS LONG rather than just SELECT CASE if it produces faster code?"  And someone else will reply:  "It does?  I didn;t know that.  I guess I will use SELECT CASE AS LONG from now own, because I want my code to be faster".

Here is another question:  Is it better to use FUNCTION PBMAIN, FUNCTION PBMAIN(), FUNCTION PBMAIN AS LONG, FUNCTION MAIN, or one of the declaratives for FUNCTION WINMAIN for your main body of code?  Some people, mostly those with PB/CC, would just use FUNCTION PBMAIN, whereas those with PB/Win probably prefer FUNCTION WINMAIN which requires that you sepecify the parameters.  Why?  Does it really make a difference most times?  Do you gain anything if you include the AS LONG?  Probably not, so why do some people do it?

I think it comes down to this:  In the SELECT CASE situation, you are aware of the benefit of including AS LONG because you explored the subject, and this is your way of showing people that this might be a good thing to do.  Fine.  But the argument that followed about whether it really makes a difference in the perception of performance is equally valid, because it is very difficult to quantify such things.  In the final analysis, all that really counts is whether the written code does what it is suppose to do, and is the performance level adequate.  If not, the solutions is often to just get a faster PC, not to go back and tweak existing code in an effort to milk a bit more performance speed out of it.

Steve Rossell

Quote from: Theo Gottwald on March 05, 2008, 09:25:41 PM
Bob, if you read this,  maybe you can clarify one thing for me, that noone else can.
In my tests, it showed - exctly as you said - that SELECT CASE AS CONST is good, for a large number of choices, as it does not need to go through all those needless comparison but jumps directly where it should.

Hello Theo, Charles, et al

We love to hear of your continued interest in PowerBASIC. But I am sure you understand that PowerBASIC representatives cannot possibly monitor 10 to 20 boards to provide PowerBASIC technical support. We must centralize our support in one location: visit soon or bring these questions and we will be very happy to discuss it with you.

Steve Hutchesson

Its interesting to read a topic of this age as it has some useful information init that still applies to the later compiler versions. With the original question, if you are using integers there is no reason not to use the Select Case as LONG notation apart from the slight increase in typing.

he CONST option in select case is in fact very useful when writing high level code. In MASM if you want a jump table, you place an aligned table with the labels as the table members then make indirect jumps based off the table start address + indexed label locations in the table but for design reasons PowerBASIC excludes module level scope for labels so that technique is possible but very messy to set up to work.

One of my toys, the tiny MASM editor called TheGun uses a jump table in the WndProc and it yielded a drop in assembled size and the technique tests up very fast but even with the rate that messages are being fed through the WndProc, the data throughput is far slower than the technique will handle so while its cute, it does not justify the effort to buid it.

Now where the Select case as CONST came into its own was with a scripting engine that I wrote a few years ago where you are parsing lines of script on a criterion of leading command or alternatively the word after return value and "=" symbol. I did a direct jump back to the start label after each branch to the case location to reduce the overhead and after gutting the complete instruction fetching loop it was testing up last time I played with it at a bit over 3 million instruction fetches a second. Drop the CONST and the fetch performance drops very badly so in this case the technique fully justifies the minor size increase from using the jump table code in Select Case.

Unfortunately the type of design was vulnerable to the virus idiot fringe sneaking malicious scripts into it that would write binary to disk and run it so I stopped development of the engine as a stand alone app and later used it as the scripting engine in my current MASM editor as a DLL. This blocked malicious scripts from being able to be run without the user knowing it as a script must be started in the editor. With the fetch loop running in more or less constant time for all commands and functions, scripts written and run by the script engine run at about the same speed as a normal binary file.

Edwin Knoppert

Since i write me a visual designer i feel somewhat spoken to, the TS mentions SELECT CASE AS LONG in respect to a messagepump.
Imo it's pretty nonsense to take this particular part of a program to speed up, you'll gain nothing, and i mean nothing towards the enduser.
There is program code and there is interface code.
Interface code will always be a slower part of a program, this is normal Windows behaviour.
There is so much overhead windows got to do anyway that using a SELECT CASE AS LONG won't help you at all.
Also, how is it possible one decides to use a large number of jump parts in a messagepump?
I have a select case in SDK mode yes, but it handles only 2 jumps, i never use the MP for alternative catching, i never had a need to.

The messagepump will only work when no program code is executed and the computer is somewhat idle (or given time).