I don?t feel loops that only last milliseconds collect anywhere near enough data to draw solid conclusions. The FOR loops I tested with were 1 million times round trip When tests last for 20 seconds plus, measuring to the tenth of a second or to a thousand of a second doesn?t make much of a difference. If my math is correct that?s about ? of 1 percent possible margin of difference or at most ? of 1 percent.
If your test methods still support that 1/0 is slower than TRUE/FALSE, I just can?t buy it. I also believe the simpler and least obstructive testing method works best. We?ll simply have to agree to disagree.
I just ran the tests again with 1 million iterations of the loop. The results are more inline with what you are describing. Here are the results:
Line 161 (13:02:28):: *********************************************************
Line 162 (13:02:28):: * TEST 8 REPORT: nInfo[nLoop]=TRUE .. nInfo[nLoop]=FALSE
Line 163 (13:02:28):: * Most recent 5 runs:
Line 164 (13:02:28):: * 1: 23.3sec
Line 165 (13:02:28):: * 2: 23.3sec
Line 166 (13:02:28):: * 3: 23.3sec
Line 167 (13:02:28):: * 4: 23.3sec
Line 168 (13:02:28):: * 5: 23.3sec
Line 169 (13:02:28):: *----------------------------------------------------------
Line 170 (13:02:28):: * Average run time: 233ms - over 10 tests
Line 171 (13:02:28):: *********************************************************
Line 172 (13:02:28):: *********************************************************
Line 173 (13:02:28):: * TEST 9 REPORT: nInfo[nLoop]=1 .. nInfo[nLoop]=0
Line 174 (13:02:28):: * Most recent 5 runs:
Line 175 (13:02:28):: * 1: 23.3sec
Line 176 (13:02:28):: * 2: 23.2sec
Line 177 (13:02:28):: * 3: 23.3sec
Line 178 (13:02:28):: * 4: 23.3sec
Line 179 (13:02:28):: * 5: 23.3sec
Line 180 (13:02:28):: *----------------------------------------------------------
Line 181 (13:02:28):: * Average run time: 232ms - over 10 tests
Line 182 (13:02:28):: *********************************************************
Line 183 (13:02:28):: *********************************************************
Line 184 (13:02:28):: * TEST 11 REPORT: nInfo[nLoop]=TRUE .. nInfo[nLoop]=FALSE
Line 185 (13:02:28):: * Most recent 5 runs:
Line 186 (13:02:28):: * 1: 23282ms
Line 187 (13:02:28):: * 2: 23286ms
Line 188 (13:02:28):: * 3: 23280ms
Line 189 (13:02:28):: * 4: 23282ms
Line 190 (13:02:28):: * 5: 23282ms
Line 191 (13:02:28):: *----------------------------------------------------------
Line 192 (13:02:28):: * Average run time: 23280ms - over 10 tests
Line 193 (13:02:28):: *********************************************************
Line 194 (13:02:28):: *********************************************************
Line 195 (13:02:28):: * TEST 12 REPORT: nInfo[nLoop]=1 .. nInfo[nLoop]=0
Line 196 (13:02:28):: * Most recent 5 runs:
Line 197 (13:02:28):: * 1: 23271ms
Line 198 (13:02:28):: * 2: 23271ms
Line 199 (13:02:28):: * 3: 23273ms
Line 200 (13:02:28):: * 4: 23270ms
Line 201 (13:02:28):: * 5: 23273ms
Line 202 (13:02:28):: *----------------------------------------------------------
Line 203 (13:02:28):: * Average run time: 23269ms - over 10 tests
Line 204 (13:02:28):: *********************************************************
Both tests are showing the same time of roughly 23.3 seconds. I am not sure what else is going on in the processor to cause the variations with only 70,000 iterations, but at 1 million, I get the same results.
Whatever the case, I?m not going to be afraid to use ON/OFF, TRUE/FALSE, or 1/0 because all we?re really doing in the big scheme of things is contesting (discussing) at most a drop of water in the ocean with this one.
I agree, I just enjoy the hunt. I really would like to figure out why the discrepancy with lower loop counts. There is a little difference at 1million iterations, but it is less of a difference than at 70,000 iterations. The problem I have with the test results from the 70,000 iteration tests are repeatable and consistent. I was expecting everything to run the same, or at least 1 vs TRUE to be the same, but I would really like to know why my test results are different.
If it was a problem with my test method, it should remain constant no matter how many iterations are involved, correct?
Jeff
P.S.
I have not really been arguing that TRUE is faster than 1, I have been trying to figure out why the tests show it to be. With my understanding of constants, I agree that there should not be any difference, but that does not change the results.
Although I have been Programming AMX for more than two years, I didtn't know GET_TIMER nor TRUE. Its always nice to learn such things.
What I saw a few weeks ago is that the handling of the event table is being stopped, when the what we call Benchmark is too low (about one execution per second). DEFINE_PROGRAM is then executed more times than the event table.
As soon as the Benchmark recovers, the event table is executed several more times than DEFINE_PROGRAM.
I am wondering which will be more efficient? Assume that 250 of the uSomethings will not be active. Only one of the nSequences will match the nCurrentSequence. If NetLinx short circuits the evaluation, I would think that the first way will be better. If it doesn't, I'm wondering if the conditional statement is more processor intensive than the evaluation?
Any thoughts? I would test this out, but I am back in San Diego and not sure that I will have time to start running benchmarks this trip.
By short circuit do you mean if the processor tests the first condition of the && and the frist conditoin doesn't evaluate as true does it immediately exit and not check the second condition. I would think so but often wondered if that assumption was correct too. Also which condition is tested first? I'd think left to right but that's cuz I'm a human (most of the time), how does the machine think? Right to left, inside to outside?
By short circuit do you mean if the processor tests the first condition of the && and the frist conditoin doesn't evaluate as true does it immediately exit and not check the second condition. I would think so but often wondered if that assumption was correct too. Also which condition is tested first? I'd think left to right but that's cuz I'm a human (most of the time), how does the machine think? Right to left, inside to outside?
Well, you know what they say when you assume....
Here are the test results and code for a quick test I did (mind you I am fairly tired, so I may have coded this incorrectly)
Line 1 (01:43:36.879):: Testing if(position1true and position2true)
Line 2 (01:43:36.879):: True Evaluated at position: 1
Line 3 (01:43:36.879):: True Evaluated at position: 2
Line 4 (01:43:36.895):: Evaluated True
Line 5 (01:43:36.895):: *********************************************************
Line 6 (01:43:36.895):: Testing if(position1false and position2true)
Line 7 (01:43:36.895):: False Evaluated at position: 1
Line 8 (01:43:36.895):: True Evaluated at position: 2
Line 9 (01:43:36.895):: Evaluated False
Line 10 (01:43:36.895):: *********************************************************
Line 11 (01:43:36.895):: Testing if(position1true and position2false)
Line 12 (01:43:36.895):: True Evaluated at position: 1
Line 13 (01:43:36.895):: False Evaluated at position: 2
Line 14 (01:43:36.895):: Evaluated False
Line 15 (01:43:36.895):: *********************************************************
Line 16 (01:43:36.895):: Testing if(position1false and position2false)
Line 17 (01:43:36.895):: False Evaluated at position: 1
Line 18 (01:43:36.895):: False Evaluated at position: 2
Line 19 (01:43:36.895):: Evaluated False
Line 20 (01:43:36.895):: *********************************************************
Line 21 (01:43:36.895):: *********************************************************
Line 22 (01:43:36.895):: Testing if(position1true or position2true)
Line 23 (01:43:36.895):: True Evaluated at position: 1
Line 24 (01:43:36.895):: True Evaluated at position: 2
Line 25 (01:43:36.895):: Evaluated True
Line 26 (01:43:36.895):: *********************************************************
Line 27 (01:43:36.895):: Testing if(position1false or position2true)
Line 28 (01:43:36.895):: False Evaluated at position: 1
Line 29 (01:43:36.911):: True Evaluated at position: 2
Line 30 (01:43:36.911):: Evaluated True
Line 31 (01:43:36.911):: *********************************************************
Line 32 (01:43:36.911):: Testing if(position1true or position2false)
Line 33 (01:43:36.911):: True Evaluated at position: 1
Line 34 (01:43:36.911):: False Evaluated at position: 2
Line 35 (01:43:36.911):: Evaluated True
Line 36 (01:43:36.911):: *********************************************************
Line 37 (01:43:36.911):: Testing if(position1false or position2false)
Line 38 (01:43:36.911):: False Evaluated at position: 1
Line 39 (01:43:36.911):: False Evaluated at position: 2
Line 40 (01:43:36.911):: Evaluated False
Line 41 (01:43:36.911):: *********************************************************
Line 42 (01:43:36.911):: *********************************************************
Line 43 (01:43:36.911):: Testing if(position1true && position2true)
Line 44 (01:43:36.911):: True Evaluated at position: 1
Line 45 (01:43:36.911):: True Evaluated at position: 2
Line 46 (01:43:36.911):: Evaluated True
Line 47 (01:43:36.911):: *********************************************************
Line 48 (01:43:36.911):: Testing if(position1false && position2true)
Line 49 (01:43:36.911):: False Evaluated at position: 1
Line 50 (01:43:36.911):: True Evaluated at position: 2
Line 51 (01:43:36.911):: Evaluated False
Line 52 (01:43:36.911):: *********************************************************
Line 53 (01:43:36.911):: Testing if(position1true && position2false)
Line 54 (01:43:36.926):: True Evaluated at position: 1
Line 55 (01:43:36.926):: False Evaluated at position: 2
Line 56 (01:43:36.926):: Evaluated False
Line 57 (01:43:36.926):: *********************************************************
Line 58 (01:43:36.926):: Testing if(position1false && position2false)
Line 59 (01:43:36.926):: False Evaluated at position: 1
Line 60 (01:43:36.926):: False Evaluated at position: 2
Line 61 (01:43:36.926):: Evaluated False
Line 62 (01:43:36.926):: *********************************************************
hope this helps... running the benchmark on the original thought in a few.... need to find what I used tired fuzzy logic on
Jeff
Here are the preliminary reports:
Using nested IF() statements seemed to be better on the data set I tested. Here are the result on a 10,000 record array:
Line 1 (02:09:48.270):: *********************************************************
Line 2 (02:09:48.270):: * TEST 1 REPORT: if(x and y)
Line 3 (02:09:48.270):: * Most recent 5 runs:
Line 4 (02:09:48.270):: * 1: 223ms
Line 5 (02:09:48.270):: * 2: 221ms
Line 6 (02:09:48.270):: * 3: 221ms
Line 7 (02:09:48.270):: * 4: 221ms
Line 8 (02:09:48.270):: * 5: 221ms
Line 9 (02:09:48.270):: *----------------------------------------------------------
Line 10 (02:09:48.270):: * Average run time: 220ms - over 20 tests
Line 11 (02:09:48.270):: *********************************************************
Line 12 (02:09:48.270):: *********************************************************
Line 13 (02:09:48.286):: * TEST 2 REPORT: if(x){ if(y)}
Line 14 (02:09:48.286):: * Most recent 5 runs:
Line 15 (02:09:48.286):: * 1: 175ms
Line 16 (02:09:48.286):: * 2: 178ms
Line 17 (02:09:48.286):: * 3: 176ms
Line 18 (02:09:48.286):: * 4: 178ms
Line 19 (02:09:48.286):: * 5: 175ms
Line 20 (02:09:48.286):: *----------------------------------------------------------
Line 21 (02:09:48.286):: * Average run time: 175ms - over 20 tests
Line 22 (02:09:48.286):: *********************************************************
I thought of doing the same test with similar functions last night but by then I was already in bed and wasn't getting up to scratch that itch.
So I guess nesting if's in situations where your testing conditions in large loops would be prudent as a pre-qaulification for further evalutaions to avoid unessary testing. On the down side of that it requires running through the loop again so which is the lesser of the two evils?
How does it require running through the loop again? It does involve creating a second conditional evaluation, but in the testing I did, about half of the time the first test would fail. I am going to test all true and all false to see what the differences are.
STOP THE PRESSES!!! (and releases too! )
I just realized that my testing was flawed. When I was running the tests, I was doing an extra operation when the test was true. In further thought, I'm not sure that this will skew the results greatly as both test are using the same data set, but I am fixing the problem and running the tests again.
I am also going to run the test 1,000 times on a loop that does 10,000 iterations just to get to the millionish point and see if there is fluctuation.
Jeff
Ok, here are some results:
With the modified test, it did close the gap a little, but it is still better to split the IFs:
Line 1 (02:53:59.801):: *********************************************************
Line 2 (02:53:59.801):: * TEST 1 REPORT: if(x and y)
Line 3 (02:53:59.801):: * Most recent 5 runs:
Line 4 (02:53:59.801):: * 1: 274ms
Line 5 (02:53:59.801):: * 2: 274ms
Line 6 (02:53:59.801):: * 3: 274ms
Line 7 (02:53:59.801):: * 4: 275ms
Line 8 (02:53:59.801):: * 5: 275ms
Line 9 (02:53:59.801):: *----------------------------------------------------------
Line 10 (02:53:59.801):: * Average run time: 274ms - over 5 tests
Line 11 (02:53:59.817):: *********************************************************
Line 12 (02:53:59.817):: *********************************************************
Line 13 (02:53:59.817):: * TEST 2 REPORT: if(x){ if(y)}
Line 14 (02:53:59.817):: * Most recent 5 runs:
Line 15 (02:53:59.817):: * 1: 227ms
Line 16 (02:53:59.817):: * 2: 226ms
Line 17 (02:53:59.817):: * 3: 227ms
Line 18 (02:53:59.817):: * 4: 226ms
Line 19 (02:53:59.817):: * 5: 229ms
Line 20 (02:53:59.817):: *----------------------------------------------------------
Line 21 (02:53:59.817):: * Average run time: 226ms - over 5 tests
Line 22 (02:53:59.817):: *********************************************************
if the first element is always false, here are the results:
Line 1 (02:56:35.004):: *********************************************************
Line 2 (02:56:35.004):: * TEST 1 REPORT: if(x and y)
Line 3 (02:56:35.004):: * Most recent 5 runs:
Line 4 (02:56:35.004):: * 1: 275ms
Line 5 (02:56:35.004):: * 2: 275ms
Line 6 (02:56:35.004):: * 3: 275ms
Line 7 (02:56:35.004):: * 4: 274ms
Line 8 (02:56:35.004):: * 5: 275ms
Line 9 (02:56:35.004):: *----------------------------------------------------------
Line 10 (02:56:35.004):: * Average run time: 274ms - over 5 tests
Line 11 (02:56:35.004):: *********************************************************
Line 12 (02:56:35.020):: *********************************************************
Line 13 (02:56:35.020):: * TEST 2 REPORT: if(x){ if(y)}
Line 14 (02:56:35.020):: * Most recent 5 runs:
Line 15 (02:56:35.020):: * 1: 179ms
Line 16 (02:56:35.020):: * 2: 180ms
Line 17 (02:56:35.020):: * 3: 179ms
Line 18 (02:56:35.020):: * 4: 179ms
Line 19 (02:56:35.020):: * 5: 180ms
Line 20 (02:56:35.020):: *----------------------------------------------------------
Line 21 (02:56:35.020):: * Average run time: 179ms - over 5 tests
Line 22 (02:56:35.020):: *********************************************************
Result if everything is true:
Line 1 (03:02:19.473):: *********************************************************
Line 2 (03:02:19.473):: * TEST 1 REPORT: if(x and y)
Line 3 (03:02:19.489):: * Most recent 5 runs:
Line 4 (03:02:19.489):: * 1: 276ms
Line 5 (03:02:19.489):: * 2: 276ms
Line 6 (03:02:19.489):: * 3: 274ms
Line 7 (03:02:19.489):: * 4: 276ms
Line 8 (03:02:19.489):: * 5: 274ms
Line 9 (03:02:19.489):: *----------------------------------------------------------
Line 10 (03:02:19.489):: * Average run time: 274ms - over 5 tests
Line 11 (03:02:19.489):: *********************************************************
Line 12 (03:02:19.489):: *********************************************************
Line 13 (03:02:19.489):: * TEST 2 REPORT: if(x){ if(y)}
Line 14 (03:02:19.489):: * Most recent 5 runs:
Line 15 (03:02:19.489):: * 1: 273ms
Line 16 (03:02:19.489):: * 2: 272ms
Line 17 (03:02:19.489):: * 3: 272ms
Line 18 (03:02:19.489):: * 4: 272ms
Line 19 (03:02:19.489):: * 5: 273ms
Line 20 (03:02:19.489):: *----------------------------------------------------------
Line 21 (03:02:19.489):: * Average run time: 272ms - over 5 tests
Line 22 (03:02:19.489):: *********************************************************
I am going to run the really long test to make sure there are no anomalies affecting the results, but ti seems that splitting into 2 IFs is always better. I am guessing that two conditionals is one cycle less than evaluating two statements and ANDing the result?
Jeff
Over 1000 iterations of both methods (each method looping 10,000 times per iteration), average results where within 100ms of each other. There was some fluctuation between multiple tests of each method that was greater than the difference between the two methods. Because of the other things going on in the processor and the fluctuation between tests of the same method, I am inclined to think that the shorter tests are more accurate as they might be focused more on the code being tested and less on other processor operations being performed at the time of the test.
Ok, here are the long test results for all FALSE x values:
Line 1 (05:23:08.176):: *********************************************************
Line 2 (05:23:08.176):: * TEST 1 REPORT: if(x and y)
Line 3 (05:23:08.176):: * Most recent 5 runs:
Line 4 (05:23:08.176):: * 1: 274877ms
Line 5 (05:23:08.176):: * 2: 274812ms
Line 6 (05:23:08.176):: * 3: 274816ms
Line 7 (05:23:08.176):: * 4: 274806ms
Line 8 (05:23:08.192):: * 5: 274807ms
Line 9 (05:23:08.192):: *----------------------------------------------------------
Line 10 (05:23:08.192):: * Average run time: 274823ms - over 5 tests
Line 11 (05:23:08.192):: *********************************************************
Line 12 (05:23:08.192):: *********************************************************
Line 13 (05:23:08.192):: * TEST 2 REPORT: if(x){ if(y)}
Line 14 (05:23:08.192):: * Most recent 5 runs:
Line 15 (05:23:08.192):: * 1: 182362ms
Line 16 (05:23:08.192):: * 2: 182297ms
Line 17 (05:23:08.192):: * 3: 182294ms
Line 18 (05:23:08.192):: * 4: 182299ms
Line 19 (05:23:08.192):: * 5: 182301ms
Line 20 (05:23:08.192):: *----------------------------------------------------------
Line 21 (05:23:08.192):: * Average run time: 182309ms - over 5 tests
Line 22 (05:23:08.192):: *********************************************************
Basically, in the worst case scenario, nested IF statements are the same speed as one IF with AND. In most situations, the nested statements will be faster.
Also, I am inclined to think that because the processor is single threaded and there are other operations being performed, it is better to use higher resolution on a shorter test. This allows you to focus on the code being tested and not the other functions the processor is handling. You can use multiple runs of a shorter tests to verify that you didn't catch the processor in the middle of some other operation (like garbage collection).
Throwing my 2 cents worth in (although we don't have that denomination in our currency any more).
I think that using GET_TIMER is probably an inaccurate way of measuring how quickly things happen in NetLinx.
The reason I think this is because GET_TIMER is a legacy Axcess key word and therefore most likely runs only during mainline.
Since mainline is now practically an idle time process in NetLinx (and really should not be used at all) and is only processed after all EVENTS have been processed, any usage of GET_TIMER to 'measure' would, to my mind, be inaccurate.
Since Timelines are event based they will be processed before any mainline code. So if you have events continuously coming in and those events are in the event handler then main line will never be actioned. Although that would have to be a lot of events!
Please feel free to shoot me down in flames or throw in your 2 cents worth.
Cheers
Mush
Hmmm...
I've just been playing around with your testing code Jeff and I've had some interesting results.
I decided to run a timeline as fast as it could go (once every millisecond) and take note of how many times it had run every time GET_TIMER fired.
The average count was around 1618 (for 200 GET_TIMER iterations). This was with the system fully booted and in an otherwise idle state and NO code in DEFINE_PROGRAM
If you start pounding the master with other tasks such as multiple FOR loops with IF statements etc, this number drops, down to around 1400.
If I but some code in DEFINE_PROGRAM the number drops even further down to about 900.
So which timer is varying, GET_TIMER or the TimeLine?
Any thoughts?
So which timer is varying, GET_TIMER or the TimeLine?
Hmmm interesting. You could run a test againt values obtained from the time command as this won't be subject to drift under load. To get anywhere near an accurate resolution it'd have to be run for a day or three.
If anything else is going on in the system it may impact the timeline operations so unless there are no events in the system during the test other than timeline events it may vary ever so slightly.
>set timeline loopcnt
-- This will set the frequency for which continuous timeline activity will --
-- be interrupted to service incoming events. The loopcnt is the number --
-- of consecutive timeline events that will be processed before checking --
-- for incoming NetLinx events. --
-- This value should only need adjustment for systems with heavy timeline --
-- usage. --
Since mainline is now practically an idle time process in NetLinx (and really should not be used at all) and is only processed after all EVENTS have been processed, any usage of GET_TIMER to 'measure' would, to my mind, be inaccurate.
Though I agree with your primary assertion about GET_TIMER, I have to pick a nit on this statement. First off, "mainline" is a necessary part of any event-driven program architecture. Everything happens during mainline, including your events. What we are generally referring to in these forums when we speak of mainline is DEFINE_PROGRAM, which is simply a point at which we can forcibly insert code into the mainline loop. But the mainline loop itself always runs, and everything that happens happens as part of that loop. And I think you handicap your programming if you work too hard to leave everything out of DEFINE_PROGRAM. There are simply times when it is more efficient and makes better sense. Yes, it can be horribly abused, and yes it can totally screw your program up if you use it badly, but used properly it's fine.
Though I agree with your primary assertion about GET_TIMER, I have to pick a nit on this statement. First off, "mainline" is a necessary part of any event-driven program architecture. Everything happens during mainline, including your events. What we are generally referring to in these forums when we speak of mainline is DEFINE_PROGRAM, which is simply a point at which we can forcibly insert code into the mainline loop. But the mainline loop itself always runs, and everything that happens happens as part of that loop. And I think you handicap your programming if you work too hard to leave everything out of DEFINE_PROGRAM. There are simply times when it is more efficient and makes better sense. Yes, it can be horribly abused, and yes it can totally screw your program up if you use it badly, but used properly it's fine.
The following is taken from the current AMX NetLinx Language Reference Guide (page 2)
Axcess communication updates occur only between passes through mainline (or after each iteration through
LONG_WHILE loops). This places timing constraints on mainline processing in order for the system to operate
properly. NetLinx avoids these constraints by processing network activity through a separate thread of
execution. Bus activity is serviced concurrently with event processing and mainline execution.
The event processing that previously could occur only through mainline code can now be handled through
code in the DEFINE_EVENT section. This provides a more efficient mechanism for processing events;
mainline does not have to be traversed to process a single I/O request.
A handler can be defined for processing device-specific events, as well as providing feedback for the device
initiating the event notification. If a handler is present, mainline will not be called to process the event; the
handler is called instead. Once the handler completes its execution, the system is ready to process the next
input message. When no more messages are pending, mainline runs.
[highlight]In effect, mainline in NetLinx is an idle time process.
With the addition of the DEFINE_EVENT section for processing events, the role of mainline in a NetLinx
program becomes greatly diminished if not totally eliminated. Programs can still be written using the
traditional technique of processing events and providing feedback through mainline code. However, programs
written using the event table structure provided in the NetLinx system will run faster and be easier to
maintain.[/highlight]
FIG.1 illustrates message and mainline processing as it appears in the NetLinx system. Note that bus servicing
is taken care of by a separate process thread (Connection Manager & Message Dispatcher) and, therefore, is
not a task that must follow mainline.
I've also found this to be true in the real world. The job I am currently working on has many thousands of lines of code and was starting to bog down at certain times. I found my CPU usage was constantly >90%. By simply moving ALL mainline code into an appropriate timeline (all of which were already running anyway) my CPU usage has dropped to ≈40%.
All told there was only about 40 lines of code moved. I don't know about you but to me this says a lot about mainline! (or possibly my coding techniques ).
But the mainline loop itself always runs, and everything that happens happens as part of that loop. And I think you handicap your programming if you work too hard to leave everything out of DEFINE_PROGRAM. There are simply times when it is more efficient and makes better sense. Yes, it can be horribly abused, and yes it can totally screw your program up if you use it badly, but used properly it's fine.
I think you are wrong about mainline always running. If nothing changes, it isn't run unnecessarily I think also that using define_program handicaps your system, and I have seen this in the real world many times. There are a few times when I will need it but they are so rare its negligible. Can you think of a situation where it makes more sense to use define_program than an event? I'm curious what kind of things people put in there. The only thing I can think of is maybe:
Read between the lines on that AMX document quoted. It says it's effectively and idle time process. In the core of the OS, there is a continuous loop running at all times. That is what is referred to as mainline in most programming languages that use it. Part of the execution of that loop includes hardware interrupts and event handling, which get priority. I suppose we are getting into a matter of terminology, and AMX doesn't refer to it that way ... but things like WAITs become part of even what AMX calls mainline. The code in a wait is dumped into that loop, and every pass of the loop it has to check if the conditions of that WAIT are met before running it. Likewise, it also checks if there is anything in DEFINE_PROGRAM, and runs that as well. So, no matter what they want to call it, it's the same processor loop, and it's running all the time.
Greetings, I still read the posts even though I don't program much these days.
Try this test...build a simple counter in the Define_Program section.
DEFINE_VARIABLE
nRunTest
nCounter
DEFINE_PROGRAM
Wait 600 // 60 seconds wait...time for system to settle down after rebooting
On[nRunTest]
If (nRunTest)
{
nCounter++
Wait 10 // About 1 second
{
Off[nRunTest]
Send_String 0," 'End of Test...nCounter = ',ITOA(nCounter) "
}
}
As an option, set the value of nRunTest in any Button_Event in your program.
Check the value of nRunTest in Diagnostics. I think you may find that even in your largest program, the value of nRunTest exceeds 100 and in small programs exceeds 300.
Conclusion...the Define_Program section is going to execute a minimum of 100 times a second no matter what.
I was teasing my wife recently that our house is like Schrodinger's Box. We have an 18 year old cat that looks like an 18 year old cat. When I come home I'm never sure if I will find it dead or alive.
The only section you really need to compile a program is Define_Device. I use the following program in masters that need no running code in M2M systems.
// Start of program
DEFINE_DEVICE
dvLocalMaster = 0:1:0
// End of program
From the NetLinx manaul:
Not all of the sections listed above are required to create a complete program. In an Axcess system, only
DEFINE_PROGRAM is required. In a NetLinx system, either DEFINE_PROGRAM or DEFINE_EVENT is
required. Other sections are required only to support code in one of these two sections, although the compiler
might require more.
From the diagram you posted it looks like define_program is only run if a message isn't handled or there are no more messages. Otherwise processing occurs in the event handler, then waits are serviced and the next message processed. So the only time define_program is run is if a message occurred for which there is no event, or if the system is idle. So if you write code such that all messages are evented, define_program is an idle process like the System Idle Process in XP. It runs whenever there is nothing else to do, getting the least CPU priority.
Paul
From the diagram you posted it looks like define_program is only run if a message isn't handled or there are no more messages...
Looks that way. Consider all the messages from panels and other devices that have no Event assigned to them. Define_Program is the final section to evaluate and process before the message is discarded. It does appear that eliminating that section could potentially speed up the system. Or perhaps the process is run whether the section is implicitly defined or not as a way to evaluate undefined message events.
I suspect the process runs whether defined or not and this is the point I was trying to make previously. Based on your programing style, you can take advantage of a built-in process that loops >100-300+ times/sec.
Comments
I just ran the tests again with 1 million iterations of the loop. The results are more inline with what you are describing. Here are the results:
Both tests are showing the same time of roughly 23.3 seconds. I am not sure what else is going on in the processor to cause the variations with only 70,000 iterations, but at 1 million, I get the same results.
I agree, I just enjoy the hunt. I really would like to figure out why the discrepancy with lower loop counts. There is a little difference at 1million iterations, but it is less of a difference than at 70,000 iterations. The problem I have with the test results from the 70,000 iteration tests are repeatable and consistent. I was expecting everything to run the same, or at least 1 vs TRUE to be the same, but I would really like to know why my test results are different.
If it was a problem with my test method, it should remain constant no matter how many iterations are involved, correct?
Jeff
P.S.
I have not really been arguing that TRUE is faster than 1, I have been trying to figure out why the tests show it to be. With my understanding of constants, I agree that there should not be any difference, but that does not change the results.
What I saw a few weeks ago is that the handling of the event table is being stopped, when the what we call Benchmark is too low (about one execution per second). DEFINE_PROGRAM is then executed more times than the event table.
As soon as the Benchmark recovers, the event table is executed several more times than DEFINE_PROGRAM.
compared to :
I am wondering which will be more efficient? Assume that 250 of the uSomethings will not be active. Only one of the nSequences will match the nCurrentSequence. If NetLinx short circuits the evaluation, I would think that the first way will be better. If it doesn't, I'm wondering if the conditional statement is more processor intensive than the evaluation?
Any thoughts? I would test this out, but I am back in San Diego and not sure that I will have time to start running benchmarks this trip.
Thanks,
Jeff
Well, you know what they say when you assume....
Here are the test results and code for a quick test I did (mind you I am fairly tired, so I may have coded this incorrectly)
hope this helps... running the benchmark on the original thought in a few.... need to find what I used tired fuzzy logic on
Jeff
Here are the preliminary reports:
Using nested IF() statements seemed to be better on the data set I tested. Here are the result on a 10,000 record array:
So I guess nesting if's in situations where your testing conditions in large loops would be prudent as a pre-qaulification for further evalutaions to avoid unessary testing. On the down side of that it requires running through the loop again so which is the lesser of the two evils?
STOP THE PRESSES!!! (and releases too! )
I just realized that my testing was flawed. When I was running the tests, I was doing an extra operation when the test was true. In further thought, I'm not sure that this will skew the results greatly as both test are using the same data set, but I am fixing the problem and running the tests again.
I am also going to run the test 1,000 times on a loop that does 10,000 iterations just to get to the millionish point and see if there is fluctuation.
Jeff
Ok, here are some results:
With the modified test, it did close the gap a little, but it is still better to split the IFs:
if the first element is always false, here are the results:
Result if everything is true:
I am going to run the really long test to make sure there are no anomalies affecting the results, but ti seems that splitting into 2 IFs is always better. I am guessing that two conditionals is one cycle less than evaluating two statements and ANDing the result?
Jeff
Over 1000 iterations of both methods (each method looping 10,000 times per iteration), average results where within 100ms of each other. There was some fluctuation between multiple tests of each method that was greater than the difference between the two methods. Because of the other things going on in the processor and the fluctuation between tests of the same method, I am inclined to think that the shorter tests are more accurate as they might be focused more on the code being tested and less on other processor operations being performed at the time of the test.
Jeff
Basically, in the worst case scenario, nested IF statements are the same speed as one IF with AND. In most situations, the nested statements will be faster.
Also, I am inclined to think that because the processor is single threaded and there are other operations being performed, it is better to use higher resolution on a shorter test. This allows you to focus on the code being tested and not the other functions the processor is handling. You can use multiple runs of a shorter tests to verify that you didn't catch the processor in the middle of some other operation (like garbage collection).
Jeff
Throwing my 2 cents worth in (although we don't have that denomination in our currency any more).
I think that using GET_TIMER is probably an inaccurate way of measuring how quickly things happen in NetLinx.
The reason I think this is because GET_TIMER is a legacy Axcess key word and therefore most likely runs only during mainline.
Since mainline is now practically an idle time process in NetLinx (and really should not be used at all) and is only processed after all EVENTS have been processed, any usage of GET_TIMER to 'measure' would, to my mind, be inaccurate.
Since Timelines are event based they will be processed before any mainline code. So if you have events continuously coming in and those events are in the event handler then main line will never be actioned. Although that would have to be a lot of events!
Please feel free to shoot me down in flames or throw in your 2 cents worth.
Cheers
Mush
Hmmm...
I've just been playing around with your testing code Jeff and I've had some interesting results.
I decided to run a timeline as fast as it could go (once every millisecond) and take note of how many times it had run every time GET_TIMER fired.
The average count was around 1618 (for 200 GET_TIMER iterations). This was with the system fully booted and in an otherwise idle state and NO code in DEFINE_PROGRAM
If you start pounding the master with other tasks such as multiple FOR loops with IF statements etc, this number drops, down to around 1400.
If I but some code in DEFINE_PROGRAM the number drops even further down to about 900.
So which timer is varying, GET_TIMER or the TimeLine?
Any thoughts?
Cheers
I found that
Hmmm interesting. You could run a test againt values obtained from the time command as this won't be subject to drift under load. To get anywhere near an accurate resolution it'd have to be run for a day or three.
Though I agree with your primary assertion about GET_TIMER, I have to pick a nit on this statement. First off, "mainline" is a necessary part of any event-driven program architecture. Everything happens during mainline, including your events. What we are generally referring to in these forums when we speak of mainline is DEFINE_PROGRAM, which is simply a point at which we can forcibly insert code into the mainline loop. But the mainline loop itself always runs, and everything that happens happens as part of that loop. And I think you handicap your programming if you work too hard to leave everything out of DEFINE_PROGRAM. There are simply times when it is more efficient and makes better sense. Yes, it can be horribly abused, and yes it can totally screw your program up if you use it badly, but used properly it's fine.
The following is taken from the current AMX NetLinx Language Reference Guide (page 2)
Please also see this post
http://www.amxforums.com/showthread.php?5887-Cpu-Usage&p=39203#post39203
I've also found this to be true in the real world. The job I am currently working on has many thousands of lines of code and was starting to bog down at certain times. I found my CPU usage was constantly >90%. By simply moving ALL mainline code into an appropriate timeline (all of which were already running anyway) my CPU usage has dropped to ≈40%.
All told there was only about 40 lines of code moved. I don't know about you but to me this says a lot about mainline! (or possibly my coding techniques ).
Cheers
Mush
I think you are wrong about mainline always running. If nothing changes, it isn't run unnecessarily I think also that using define_program handicaps your system, and I have seen this in the real world many times. There are a few times when I will need it but they are so rare its negligible. Can you think of a situation where it makes more sense to use define_program than an event? I'm curious what kind of things people put in there. The only thing I can think of is maybe:
define_program
if (bCheckBounces)
reboot(0)
Paul
Greetings, I still read the posts even though I don't program much these days.
Try this test...build a simple counter in the Define_Program section. As an option, set the value of nRunTest in any Button_Event in your program.
Check the value of nRunTest in Diagnostics. I think you may find that even in your largest program, the value of nRunTest exceeds 100 and in small programs exceeds 300.
Conclusion...the Define_Program section is going to execute a minimum of 100 times a second no matter what.
Yes it will run, but only because you put something in there. If you hadn't it wouldn't.
ROFL
Mate, you crack me up!
Very good!
I was teasing my wife recently that our house is like Schrodinger's Box. We have an 18 year old cat that looks like an 18 year old cat. When I come home I'm never sure if I will find it dead or alive.
If a tree falls in the forest and no one is around to hear it, does it make a noise?
I suppose you could leave Define_Program out of your code completely. This old school programmer never even considered doing so...good idea.
Actually DEFINE_PROGRAM, according to the AMX documentation, does NOT get called if it has nothing in it!
Do you mean leave out the DEFINE_PROGRAM declaration all together? You can't do that, the compiler doesn't like it.
Cheers
That would depend on your definition of sound and one.
Hah!
We'll I'll be buggered! I stand corrected.
Thanks Phreak!
From the NetLinx manaul:
Paul
Looks that way. Consider all the messages from panels and other devices that have no Event assigned to them. Define_Program is the final section to evaluate and process before the message is discarded. It does appear that eliminating that section could potentially speed up the system. Or perhaps the process is run whether the section is implicitly defined or not as a way to evaluate undefined message events.
I suspect the process runs whether defined or not and this is the point I was trying to make previously. Based on your programing style, you can take advantage of a built-in process that loops >100-300+ times/sec.