IIF functionality made simple

Share your advanced PureBasic knowledge/code with the community.
User avatar
Kaeru Gaman
Addict
Addict
Posts: 4826
Joined: Sun Mar 19, 2006 1:57 pm
Location: Germany

Re: IIF functionality made simple

Post by Kaeru Gaman »

mesozorn wrote:Zero is actually a real result, because it means the process took less than a single millisecond.
no it doesn't.
zero means, that the process took less than one timeslice, that means less than 13ms.

so, comparing two processes and getting results of zero, can mean one needs 2 and the other needs 6 ms, wich would mean the second needs threefold the time of the first.

thus I said, zero is no result, because it tells you nothing.
screw it higher to 1000 - 60000 ms, to get something that is worth being called a result.

and again - run it without debugger, you would need a MsgBox to show the results then.
oh... and have a nice day.
mesozorn
Enthusiast
Enthusiast
Posts: 171
Joined: Fri Feb 20, 2009 2:23 am

Re: IIF functionality made simple

Post by mesozorn »

Kaeru Gaman wrote:no it doesn't.
zero means, that the process took less than one timeslice, that means less than 13ms.
What is your backup for this statement? In the documentation for ElapsedMilliseconds(), it says nothing about 13ms "timeslices". And if it does work this way, why is it not mentioned in the documentation?

In any case, for me the following:

Code: Select all

e=ElapsedMilliseconds()
For x=1 To 100000000
z=Val(Str(5.5>5.6))
Next x
MessageRequester("Test result",Str(ElapsedMilliseconds()-e))
... without the debugger produces a result of 3250 milliseconds after one hundred million repetitions. Which gives a result of 0.0000325 milliseconds per cycle, and which means that my original test of ten thousand cycles would still only take 0.325 milliseconds, which is still less than one single millisecond as originally asserted. Thus the Val(Str()) usage can hardly be said to consume much resource.
User avatar
Kaeru Gaman
Addict
Addict
Posts: 4826
Joined: Sun Mar 19, 2006 1:57 pm
Location: Germany

Re: IIF functionality made simple

Post by Kaeru Gaman »

> why is it not mentioned in the documentation?
because it's an OS specific problem, not PB specific.
ElapsedMilliseconds is a wrapper of the API-Function to return the milliseconds.
you can adjust the precision to a single millisecond by some API calls, there is code in the forums.

> What is your backup for this statement?
knowledge, experience, dozens of discussions in the forums about that issue..
but ok, also something definitely and printed:
http://www.geisswerks.com/ryan/FAQS/timing.html
have fun
oh... and have a nice day.
mesozorn
Enthusiast
Enthusiast
Posts: 171
Joined: Fri Feb 20, 2009 2:23 am

Re: IIF functionality made simple

Post by mesozorn »

Kaeru Gaman wrote:you can adjust the precision to a single millisecond by some API calls, there is code in the forums.
I am definitely going to need to do this for all future programs then, as I can't stand imprecision on any scale. In any case, if you check the second part of my post above (which may have been missed because I edited it in after further testing) you can see that in this specific case, even higher volume testing reveals Val(Str()) not to be a significant issue in terms of time consumption, even when larger cycles are used.
SFSxOI
Addict
Addict
Posts: 2970
Joined: Sat Dec 31, 2005 5:24 pm
Location: Where ya would never look.....

Re: IIF functionality made simple

Post by SFSxOI »

mesozorn wrote:
Kaeru Gaman wrote:no it doesn't.
zero means, that the process took less than one timeslice, that means less than 13ms.
What is your backup for this statement? In the documentation for ElapsedMilliseconds(), it says nothing about 13ms "timeslices". And if it does work this way, why is it not mentioned in the documentation?

In any case, for me the following:

Code: Select all

e=ElapsedMilliseconds()
For x=1 To 100000000
z=Val(Str(5.5>5.6))
Next x
MessageRequester("Test result",Str(ElapsedMilliseconds()-e))
... without the debugger produces a result of 3250 milliseconds after one hundred million repetitions. Which gives a result of 0.0000325 milliseconds per cycle, and which means that my original test of ten thousand cycles would still only take 0.325 milliseconds, which is still less than one single millisecond as originally asserted. Thus the Val(Str()) usage can hardly be said to consume much resource.

That code gives me 10265


this code gives me 8331

Code: Select all

e=ElapsedMilliseconds()
For x=1 To 100000000
z=5.5>5.6
Next x
MessageRequester("Test result",Str(ElapsedMilliseconds()-e))
which is correct (or as near almost correct for the discussion)?
The advantage of a 64 bit operating system over a 32 bit operating system comes down to only being twice the headache.
User avatar
idle
Always Here
Always Here
Posts: 5844
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: IIF functionality made simple

Post by idle »

I'm not saying your solution is bad but it's not suitable to use in all instances if it's used in GUI window loop in response to a button click then yes go for your life but if it's used in a loop controlling the flow then no it's almost 10x slower

Code: Select all

Global tt.TIMECAPS
timeGetDevCaps_(@tt, SizeOf(TIMECAPS))
timeBeginPeriod_(tt\wPeriodMin)  

Procedure.s IIFEval(expr,y$,n$)
If expr=1:ProcedureReturn y$
Else:ProcedureReturn n$
EndIf
EndProcedure

Procedure IIFEvaln(expr,y,n)
If expr=1
ProcedureReturn y
Else
ProcedureReturn n
EndIf
EndProcedure


Macro IIFn(expr,y,n)
IIFEvaln(Val(Str(1 And expr)),y,n)
EndMacro

Macro IIF(expr,y,n)
IIFEval(Val(Str(1 And expr)),y,n)
EndMacro

Debug IIF(5.5>5.4,"yes","no")
Debug IIF(5.5>5.6,"yes","no")
Debug IIF(5.5=5.5,"yes","no")
Debug IIFn(5.5>5.7,200,100)
Debug iifn(5.5>5.4 And 5>3,200,100)

lp = 10000000
st = GetTickCount_()
For a = 1 To lp 
   x= IIFn(5>7,200,100)
Next   
et1.f = (GetTickCount_() - st) / lp
st = GetTickCount_()
For a = 1 To lp 
   If 5 > 7 
      x = 200 
   Else 
      x = 100      
   EndIf 
Next     
et2.f = (GetTickCount_() - st) / lp

MessageRequester("test", "iif " + StrF(et1,6) + " if " + StrF(et2,6))
  
mesozorn
Enthusiast
Enthusiast
Posts: 171
Joined: Fri Feb 20, 2009 2:23 am

Re: IIF functionality made simple

Post by mesozorn »

SFSxOI wrote:That code gives me 10265
Turn off the debugger and you'll get a result closer to three seconds. I got around ten as well while the debugger was turned on.

You need to use the Val(Str()) version if you want to evaluate float/decimal number expressions within these custom IIF() macros. I leave it in so that the function is all-purpose and can handle every variation of expression/action.
mesozorn
Enthusiast
Enthusiast
Posts: 171
Joined: Fri Feb 20, 2009 2:23 am

Re: IIF functionality made simple

Post by mesozorn »

idle wrote:I'm not saying your solution is bad but it's not suitable to use in all instances if it's used in GUI window loop in response to a button click then yes go for your life but if it's used in a loop controlling the flow then no it's almost 10x slower
It's simply currently the only one-shot version of an IIF statement that can be used within PB for all evaluation scenarios. Good or bad as it may be, it's all there is that works. If you know for a fact you won't need to evaluate floats, you can change it to a simple one-call procedure that doesn't need the Val(Str()) component. You can have another version that does use it specifically for when you want to use floats, or when you're not sure what the expression will contain. Or you can do a different sort of evaluation that doesn't involve an IIF statement.

But if you want an IIF that can be plugged in as a parameter to another procedure or statement, and can also handle float expressions, this is what's currently available until that new Boolean() function is introduced in 4.5 or whenever.
User avatar
Kaeru Gaman
Addict
Addict
Posts: 4826
Joined: Sun Mar 19, 2006 1:57 pm
Location: Germany

Re: IIF functionality made simple

Post by Kaeru Gaman »

but it is fishy, even more fishy than the Or 0 workaround.

one more thing for duration tests: do not test only a single condition.
in this case, you would need to test all three comparators with all two results, True and False, so you would need six tests within the testloop.
and, since you normally would test variables and not literals, use variables in the test.

... but it's meaningless anyways, it's fish too fishy to talk about reason.

wait for the Boolean() directive, coming in 4.5 or 4.6 ...
Last edited by Kaeru Gaman on Mon Nov 09, 2009 11:47 pm, edited 1 time in total.
oh... and have a nice day.
User avatar
idle
Always Here
Always Here
Posts: 5844
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: IIF functionality made simple

Post by idle »

you get 5/5 for style and I liked your solution, its just a pity it can't be done faster!
mesozorn
Enthusiast
Enthusiast
Posts: 171
Joined: Fri Feb 20, 2009 2:23 am

Re: IIF functionality made simple

Post by mesozorn »

Yes, faster would be better, and Boolean() will no doubt provide that. I invented my version a while back because I needed it then and could not wait for future development. This was many months ago and boolean() wasn't even promised or anticipated at the time, so I had to make do with this hack. I only use it when it's needed as a parameter, never as a stand-alone conditional evaluation.
mesozorn
Enthusiast
Enthusiast
Posts: 171
Joined: Fri Feb 20, 2009 2:23 am

Re: IIF functionality made simple

Post by mesozorn »

I've dramatically improved the speed performance of the IIF macro, and managed to do away with the need for Val(Str()) in the process while preserving the float evaluation integrity. I've pasted the change directly into Idle's time test example below to demonstrate. It's not perfect yet but it's much, much faster than before.

Code: Select all

Global tt.TIMECAPS
timeGetDevCaps_(@tt, SizeOf(TIMECAPS))
timeBeginPeriod_(tt\wPeriodMin) 

Procedure.s IIFEval(expr,y$,n$)
If expr=1:ProcedureReturn y$
Else:ProcedureReturn n$
EndIf
EndProcedure

Procedure IIFEvaln(expr,y,n)
If expr=1
ProcedureReturn y
Else
ProcedureReturn n
EndIf
EndProcedure


Macro IIFn(expr,y,n)
IIFEvaln(1-(1 And Not(expr)),y,n)
EndMacro

Macro IIF(expr,y,n)
IIFEval(1-(1 And Not(expr)),y,n)
EndMacro

Debug IIF(5.5>5.4,"yes","no")
Debug IIF(5.5>5.6,"yes","no")
Debug IIF(5.5=5.5,"yes","no")
Debug IIFn(5.5>5.7,200,100)
Debug iifn(5.5>5.4 And 5>3,200,100)

lp = 10000000
st = GetTickCount_()
For a = 1 To lp
   x= IIFn(5>7,200,100)
Next   
et1.f = (GetTickCount_() - st) / lp
st = GetTickCount_()
For a = 1 To lp
   If 5 > 7
      x = 200
   Else
      x = 100     
   EndIf
Next     
et2.f = (GetTickCount_() - st) / lp

MessageRequester("test", "iif " + StrF(et1,6) + " if " + StrF(et2,6))
Also my original crude time test:

Code: Select all

e=ElapsedMilliseconds()
For x=1 To 100000000
z=1-(1 And Not(5.5>5.3))
Next x
MessageRequester("Test result",Str(ElapsedMilliseconds()-e))
...now for me returns just under 300 milliseconds for one hundred million cycles, as opposed to 3250 milliseconds before. About a 10x improvement in this (admittedly crude and incomplete) test example.
User avatar
idle
Always Here
Always Here
Posts: 5844
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: IIF functionality made simple

Post by idle »

That's heaps better, I think the additional time in the numeric version is mostly due to the procedure call, so nothing much else you could do.
Not sure about the string version though, still looking

see what comes from all the bitching and bickering. :mrgreen:

well done it's a good improvement.

couldn't you just use this though?

Code: Select all

Macro IIFn(expr,y,n)
IIFEvaln((Not(expr)),n,y)
EndMacro

Macro IIF(expr,y,n)
IIFEval((Not(expr)),n,y)
EndMacro

or this and is probably quicker than a not

Code: Select all

Macro IIFn(expr,y,n)
IIFEvaln((1 And(expr)),y,n)
EndMacro

Macro IIF(expr,y,n)
IIFEval((1 And(expr)),y,n)
EndMacro
Last edited by idle on Tue Nov 10, 2009 2:51 am, edited 1 time in total.
User avatar
Kaeru Gaman
Addict
Addict
Posts: 4826
Joined: Sun Mar 19, 2006 1:57 pm
Location: Germany

Re: IIF functionality made simple

Post by Kaeru Gaman »

why 1- and Not, when you have the And..?

and as I already said, use all variations and variables...

Code: Select all

e=ElapsedMilliseconds()
a.f = 5.5
b.f = 5.3
c.f = 5.3
For x=1 To 100000000
z=(1 And ( a > b ))
z=(1 And ( b > a ))
z=(1 And ( a < b ))
z=(1 And ( b < a ))
z=(1 And ( a = b ))
z=(1 And ( c = b ))
Next x
MessageRequester("Test result",Str(ElapsedMilliseconds()-e))
oh... and have a nice day.
mesozorn
Enthusiast
Enthusiast
Posts: 171
Joined: Fri Feb 20, 2009 2:23 am

Re: IIF functionality made simple

Post by mesozorn »

idle wrote:see what comes from all the bitching and bickering. :mrgreen:
well done it's a good improvement.
Thanks, evolution comes mostly from struggle right? 8)
idle wrote:couldn't you just use this though?

Code: Select all

Macro IIFn(expr,y,n)
IIFEvaln((Not(expr)),n,y)
EndMacro

Macro IIF(expr,y,n)
IIFEval((Not(expr)),n,y)
EndMacro

Yep that seems to work too and is cleaner-looking, nice one. The reason I didn't think that would work is because for some reason on its own, this:

Code: Select all

z=(1-(1 And Not(5.5>5.6)))
Debug z
z=(1-(1 And Not(5.5>5.3)))
Debug z
...works correctly, whereas (for me) using "NOT" by itself:

Code: Select all

z=(Not(5.5>5.6))
Debug z
z=(Not(5.5>5.3))
Debug z
...does not work, oddly. So I don't know why it works in the macro but not by itself, but that's why it didn't seem likely to work. I'm probably just missing something from being too tired...
Post Reply