Optimize your code. (Tips and techniques)

245

Comments

  • Spire_JeffSpire_Jeff Formerly Caffeinated Programmer Posts: 1,917
    Joe Hebert wrote: »
    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.

    Joe Hebert wrote: »
    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.
  • REBUILD_EVENTREBUILD_EVENT Junior Member Posts: 127
    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.
  • Spire_JeffSpire_Jeff Formerly Caffeinated Programmer Posts: 1,917
    Here is a new one. Does anyone know if NetLinx does short circuit conditional evaluation? The reason I am asking is the following:
    for(x=500;x;x--){
       if(uSomething[x].isActive and uSomething[x].nSequence == nCurrentSequence)
           fnDoSomething();
    }
    

    compared to :
    for(x=500;x;x--){
       if(uSomething[x].isActive)
           if(uSomething[x].nSequence == nCurrentSequence)
               fnDoSomething();
    }
    

    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
  • viningvining X Member Posts: 4,353
    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?
  • Spire_JeffSpire_Jeff Formerly Caffeinated Programmer Posts: 1,917
    vining wrote: »
    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)::  *********************************************************
    
    define_function integer stepFalse(integer nID){
    	send_string 0,"'False Evaluated at position: ',itoa(nID)";
    	return FALSE;
    }
    define_function integer stepTrue(integer nID){
    	send_string 0,"'True Evaluated at position: ',itoa(nID)";
    	return TRUE;
    }
    define_function shortCircuitTest(){
    	send_string 0,"'Testing if(position1true and position2true)'";
    	if(stepTrue(1) and stepTrue(2))
    		send_string 0,"'Evaluated True'";
    	else
    		send_string 0,"'Evaluated False'";
    	send_string 0,"'*********************************************************'";
    
    
    	send_string 0,"'Testing if(position1false and position2true)'";
    	if(stepFalse(1) and stepTrue(2))
    		send_string 0,"'Evaluated True'";
    	else
    		send_string 0,"'Evaluated False'";
    	send_string 0,"'*********************************************************'";
    
    
    	send_string 0,"'Testing if(position1true and position2false)'";
    	if(stepTrue(1) and stepFalse(2))
    		send_string 0,"'Evaluated True'";
    	else
    		send_string 0,"'Evaluated False'";
    	send_string 0,"'*********************************************************'";
    
    
    	send_string 0,"'Testing if(position1false and position2false)'";
    	if(stepFalse(1) and stepFalse(2))
    		send_string 0,"'Evaluated True'";
    	else
    		send_string 0,"'Evaluated False'";
    	send_string 0,"'*********************************************************'";
    
    
    	send_string 0,"'*********************************************************'";
    	
    	send_string 0,"'Testing if(position1true or position2true)'";
    	if(stepTrue(1) or stepTrue(2))
    		send_string 0,"'Evaluated True'";
    	else
    		send_string 0,"'Evaluated False'";
    	send_string 0,"'*********************************************************'";
    
    
    	send_string 0,"'Testing if(position1false or position2true)'";
    	if(stepFalse(1) or stepTrue(2))
    		send_string 0,"'Evaluated True'";
    	else
    		send_string 0,"'Evaluated False'";
    	send_string 0,"'*********************************************************'";
    
    
    	send_string 0,"'Testing if(position1true or position2false)'";
    	if(stepTrue(1) or stepFalse(2))
    		send_string 0,"'Evaluated True'";
    	else
    		send_string 0,"'Evaluated False'";
    	send_string 0,"'*********************************************************'";
    
    
    	send_string 0,"'Testing if(position1false or position2false)'";
    	if(stepFalse(1) or stepFalse(2))
    		send_string 0,"'Evaluated True'";
    	else
    		send_string 0,"'Evaluated False'";
    	send_string 0,"'*********************************************************'";
    	
    	send_string 0,"'*********************************************************'";
    	send_string 0,"'Testing if(position1true && position2true)'";
    	if(stepTrue(1) && stepTrue(2))
    		send_string 0,"'Evaluated True'";
    	else
    		send_string 0,"'Evaluated False'";
    	send_string 0,"'*********************************************************'";
    
    
    	send_string 0,"'Testing if(position1false && position2true)'";
    	if(stepFalse(1) && stepTrue(2))
    		send_string 0,"'Evaluated True'";
    	else
    		send_string 0,"'Evaluated False'";
    	send_string 0,"'*********************************************************'";
    
    
    	send_string 0,"'Testing if(position1true && position2false)'";
    	if(stepTrue(1) && stepFalse(2))
    		send_string 0,"'Evaluated True'";
    	else
    		send_string 0,"'Evaluated False'";
    	send_string 0,"'*********************************************************'";
    
    
    	send_string 0,"'Testing if(position1false && position2false)'";
    	if(stepFalse(1) && stepFalse(2))
    		send_string 0,"'Evaluated True'";
    	else
    		send_string 0,"'Evaluated False'";
    	send_string 0,"'*********************************************************'";
    	
    }
    

    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)::  *********************************************************
    
  • viningvining X Member Posts: 4,353
    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?
  • Spire_JeffSpire_Jeff Formerly Caffeinated Programmer Posts: 1,917
    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.

    Jeff
  • viningvining X Member Posts: 4,353
    Spire_Jeff wrote:
    How does it require running through the loop again?
    Exactly! What the heck was I thinkin' / sayin', it would just nested under the first condiiton, duh.
  • Spire_JeffSpire_Jeff Formerly Caffeinated Programmer Posts: 1,917
    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).

    Jeff
  • mushmush I programme in the dark! Posts: 285
    G'day guys,

    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
  • PhreaKPhreaK Senior Member Posts: 966
    mush wrote: »
    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.
  • viningvining X Member Posts: 4,353
    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. --

    Current Timeline Loopcnt = 10
  • DHawthorneDHawthorne Junior Member Posts: 4,584
    mush wrote: »
    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.
  • mushmush I programme in the dark! Posts: 285
    DHawthorne wrote: »
    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.

    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
  • a_riot42a_riot42 AMX Wizard Posts: 1,619
    DHawthorne wrote: »
    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:

    define_program

    if (bCheckBounces)
    reboot(0)


    Paul
  • DHawthorneDHawthorne Junior Member Posts: 4,584
    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.
  • Define_Program section

    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.
  • mushmush I programme in the dark! Posts: 285
    B_Clements wrote: »

    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.
  • PhreaKPhreaK Senior Member Posts: 966
    Maybe it should be renamed:
    define_schrödingers_box
    
  • mushmush I programme in the dark! Posts: 285
    PhreaK wrote: »
    Maybe it should be renamed:
    define_schrödingers_box
    


    ROFL

    Mate, you crack me up!
  • ColzieColzie Senior Member Posts: 470
    PhreaK wrote: »
    Maybe it should be renamed:
    define_schrödingers_box
    

    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.
  • Where do they actually teach this stuff?
    PhreaK wrote: »
    Maybe it should be renamed:
    define_schrödingers_box
    
    If a tree falls in the forest and no one is around to hear it, does it make a noise?
  • mush wrote: »
    Yes it will run, but only because you put something in there. If you hadn't it wouldn't.
    The point I was making is that, just like an empty Timeline or a Wait with no code behind it, Define_Program gets called whether you like it or not.

    I suppose you could leave Define_Program out of your code completely. This old school programmer never even considered doing so...good idea.
  • mushmush I programme in the dark! Posts: 285
    G'day Brian,
    B_Clements wrote: »
    The point I was making is that, just like an empty Timeline or a Wait with no code behind it, Define_Program gets called whether you like it or not.
    Actually DEFINE_PROGRAM, according to the AMX documentation, does NOT get called if it has nothing in it!

    B_Clements wrote: »
    I suppose you could leave Define_Program out of your code completely. This old school programmer never even considered doing so...good idea.

    Do you mean leave out the DEFINE_PROGRAM declaration all together? You can't do that, the compiler doesn't like it.

    Cheers
  • mushmush I programme in the dark! Posts: 285
    B_Clements wrote: »
    If a tree falls in the forest and no one is around to hear it, does it make a noise?

    That would depend on your definition of sound and one.
  • PhreaKPhreaK Senior Member Posts: 966
    mush wrote: »
    Do you mean leave out the DEFINE_PROGRAM declaration all together? You can't do that, the compiler doesn't like it.
    I do it all the time. Never caused any issues here.
  • mushmush I programme in the dark! Posts: 285
    PhreaK wrote: »
    I do it all the time. Never caused any issues here.

    Hah!

    We'll I'll be buggered! I stand corrected.

    Thanks Phreak!
  • 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.
  • a_riot42a_riot42 AMX Wizard Posts: 1,619
    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
  • The data needs to be evaluated somewhere, right?
    a_riot42 wrote: »
    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.
Sign In or Register to comment.