Difference between revisions of "LSL Script Efficiency"

From Second Life Wiki
Jump to navigation Jump to search
m
m (<lsl> tag to <source>)
 
(26 intermediate revisions by 14 users not shown)
Line 1: Line 1:
{{LSL Header}}
{{LSL Header|ml=*}}
<div style="float:right;">__TOC__</div>


<div id="box">
<div id="box">
Line 5: Line 6:
<div style="padding: 0.5em;">
<div style="padding: 0.5em;">


Efficiency is how long it takes to run a script.  
Efficiency is how much resource a particular script uses to accomplish a goal.  This often goes hand-in-hand with speed of execution, but not always.


There are many ways to speed up scripts, such as using ++a instead of a++.
Things to watch out for, in rough order of importance:
 
However most of those micro optimizations might not hold true in the future. But there are some common guidelines to avoid:


* Using a lot of scripts to accomplish something.  See Efficient Design.
* Short frequency timers (<5 sec)
* Short frequency timers (<5 sec)
* Listen - especially to the 0 channel
* Listen - especially to the 0 channel
* Changing textures on attachments (Others have to download them)
* Changing textures often (Others have to download them)
* Use of server/physical rotations instead of omega
* Inappropriate use of server/physical rotations instead of omega
* Event handlers which are not needed (touch, collision, ...)
* Event handlers which are not needed (touch, collision, ...)
* Large amount of email or IM
* Large amount of email or IM
* Inefficient algorithms (like linear search)
* Inefficient algorithms (like linear search)
* Busy (polling) loops
* Busy ([[HTTP_Polling|polling]]) loops


</div></div>
</div></div>
<div id="box">
<div id="box">


== Rules for posting ==
== Efficient Design ==
<div style="padding: 0.5em;">
<div style="padding: 0.5em;">


1. Don't add theories.<br>
Each idle script in an object adds 0.001 to 0.003 milliseconds per frame of script time usage. Fewer scripts is better.
2. Don't write "x is faster than y", write, "x takes X millis, y takes Y milis".


* If you need to have a bunch of "buttons", don't put a script in each prim.  Use [[llDetectedLinkNumber]] to detect which button was touched from your main script instead. 
* If you need to change color, alpha, or texture of child prims, do not put a script in each prim.  Instead use [[llSetLinkAlpha]], [[llSetLinkColor]], [[llSetLinkTexture]], [[llSetLinkPrimitiveParams]] or [[llSetLinkPrimitiveParamsFast]].
* '''Never''' put a script in each prim (of a large linkset) that listens on channel 0 (or any other channel).  This is probably the worst thing you can do for efficiency.  Unfortunately it is all too common.
* Consider using [[XyzzyText]] instead of [[XyText]].
* While sleeping scripts have 0.0 script time, while even idling averages 1.9 microseconds. Consider if your script could be more efficient with added sleeps.
The bottom line is, if you find yourself tempted to put a script in each prim, stop and think about how you could do it differently with fewer scripts.  There is almost always an alternative.  It is up to you to decide whether the alternative is a viable one for your application.
</div></div>
<div id="box">
== Micro-optimization ==
<div style="padding: 0.5em;">
There are many ways to speed up scripts, such as using ++a instead of a++, however, most of these micro optimizations do not hold true for Mono. See [[LSL_Hacks]] for more specific information.
</div>
== How Fast Does That Code Run ==
<div style="padding: 0.5em;">


The following code will get the time for each function call in millis.
The following code will get the time for each function call in millis.


Please do not modify the script below, as any small change could change efficiency. Thanks to Xaviar Czervik for the original code, and Strife Onizuka for optimizing the code.
Please first propose changes to the [[Talk:LSL_Script_Efficiency|discussion]] tab. Please do not change the code here without discussion, as any small change could reduce the accuracy of measurements. Please note this code and its comments should exactly match the code and comments presented by the [[Efficiency Tester]] page.
 
Thanks to {{User|Xaviar Czervik}} for the original code, thanks to {{User|Strife Onizuka}} for tuning the code to produce more accurate measurements, and thanks to the other contributors listed in the history of this article.


<pre>
<source lang="lsl2">
//IMPORTANT: Only perform tests in an empty region.
//IMPORTANT: Only perform tests in an empty region.
// To reduce contamination and be sure to wearing no attachments.
// To reduce contamination and be sure to wearing no attachments.
Line 41: Line 61:
// There is a margin of error so run the tests multiple times to determine it.
// There is a margin of error so run the tests multiple times to determine it.


integer time() {
integer time() { // count milliseconds since the day began
     string stamp = llGetTimestamp();
     string stamp = llGetTimestamp(); // "YYYY-MM-DDThh:mm:ss.ff..fZ"
     return (integer) llGetSubString(stamp, 11, 12) * 3600000 +  
     return (integer) llGetSubString(stamp, 11, 12) * 3600000 + // hh
           (integer) llGetSubString(stamp, 14, 15) * 60000 +  
           (integer) llGetSubString(stamp, 14, 15) * 60000 + // mm
           llRound((float)llGetSubString(stamp, 17, -2) * 1000000.0)/1000;
           llRound((float)llGetSubString(stamp, 17, -2) * 1000000.0)/1000; // ss.ff..f
//llInsertString(llDeleteSubString(stamp, 19, 19) + "000000", 23, ".")
}
}


default {
default {
   state_entry() {
   state_entry() {
    llOwnerSay((string) llGetFreeMemory());
     //test variables
     //test variables
     float counter;
     float counter;
Line 57: Line 78:
     float i = 0;
     float i = 0;
     float j = 0;
     float j = 0;
     float max = 10000;
     float max = 10000; // 2ms of work takes 20 seconds to repeat 10,000 times, plus overhead
     float start = time();
 
     float t0 = time();
     do {
     do {
       //test
       //test
       counter += 1;
       counter += 1;
        
        
     }while (++i < max);
     }while (++i < max);
     float delta = time();
     float t1 = time();
     do ; while (++j < max);
     do ; while (++j < max);
     float end = time();//remove the time required by the framework
     float t2 = time();//remove the time required by the framework
     float t = ((delta - start) - (end - delta))/max;
     float elapsed = ((t1 - t0) - (t2 - t1))/max;
     llOwnerSay("The function in the loop took a total of " + (string)t + " milliseconds.");
     llOwnerSay("The function in the loop took a total of " + (string)elapsed + " milliseconds.");
   }
   }
}
}
</pre>
</source>


</div></div>
</div></div>
Line 79: Line 102:
<div style="padding: 0.5em;">
<div style="padding: 0.5em;">


While the following data is correct, the function above was not used. A slightly less optimized version written by Xaviar Czervik was used. If someone could please repeat the tests with the function above.
The following data was collected using the above function in a recently deployed empty (though not private) sim, with only that one script running in a HUD attachment. 20 tests of each were run and averaged.
  ++a:    0.173780 ms    std. dev.:  0.003393 ms
  a += 1:  0.181720 ms    std. dev.:  0.013267 ms
  a++:    0.243500 ms    std. dev.:  0.013816 ms
Thus a++ takes 40% longer to execute than ++a (rough estimate).
 
While the following data is correct, the function above was not used. A slightly less optimized version written by {{User|Xaviar Czervik}} was used. If someone could please repeat the tests with the function above.


In theory, ++a and a += 1 are equal in speed, however this is not true:
   ++a:    0.364700 millis
   ++a:    0.364700 millis
   a += 1:  0.346900 millis
   a += 1:  0.346900 millis
Line 91: Line 119:
   a++:    0.424600 millis
   a++:    0.424600 millis


llOwnerSay v. llSay v. llShout v. llWhisper (Channel 0 where applies):
[[llOwnerSay]] v. [[llSay]] v. [[llShout]] v. [[llWhisper]] (Channel 0 where applies):
   llOwnerSay(): 4.359000 millis
   llOwnerSay(): 4.359000 millis
   llWhisper():  5.201000 millis
   llWhisper():  5.201000 millis
Line 97: Line 125:
   llShout():  14.877000 millis
   llShout():  14.877000 millis


Different Channels (llSay() Used for all):
[[llOwnerSay]] v. [[llSay]] v. [[llShout]] v. [[llWhisper]] (Re-tested under Mono as of 04/02/09. Channel 1 for regionsay, listener set up nearby):
 
  llOwnerSay():  3.607200 millis
  llWhisper():    4.871200 millis
  llSay():        3.975200 millis
  llShout():    10.931200 millis
  llRegionSay():  0.312000 millis
 
Different Channels ([[llSay]]() Used for all):
   -100000000: 1.226400 millis
   -100000000: 1.226400 millis
   -100000:    1.254300 millis
   -100000:    1.254300 millis
Line 108: Line 144:
   100000000:  1.228700 millis
   100000000:  1.228700 millis


Amount of text (llSay() and Channel 1 used for all):
Amount of text ([[llSay]]() and Channel 1 used for all):
<pre>
<pre>
   1 Character:    1.242300 millis
   1 Character:    1.242300 millis

Latest revision as of 12:32, 25 January 2015

What is Efficiency

Efficiency is how much resource a particular script uses to accomplish a goal. This often goes hand-in-hand with speed of execution, but not always.

Things to watch out for, in rough order of importance:

  • Using a lot of scripts to accomplish something. See Efficient Design.
  • Short frequency timers (<5 sec)
  • Listen - especially to the 0 channel
  • Changing textures often (Others have to download them)
  • Inappropriate use of server/physical rotations instead of omega
  • Event handlers which are not needed (touch, collision, ...)
  • Large amount of email or IM
  • Inefficient algorithms (like linear search)
  • Busy (polling) loops

Efficient Design

Each idle script in an object adds 0.001 to 0.003 milliseconds per frame of script time usage. Fewer scripts is better.

  • If you need to have a bunch of "buttons", don't put a script in each prim. Use llDetectedLinkNumber to detect which button was touched from your main script instead.
  • If you need to change color, alpha, or texture of child prims, do not put a script in each prim. Instead use llSetLinkAlpha, llSetLinkColor, llSetLinkTexture, llSetLinkPrimitiveParams or llSetLinkPrimitiveParamsFast.
  • Never put a script in each prim (of a large linkset) that listens on channel 0 (or any other channel). This is probably the worst thing you can do for efficiency. Unfortunately it is all too common.
  • Consider using XyzzyText instead of XyText.
  • While sleeping scripts have 0.0 script time, while even idling averages 1.9 microseconds. Consider if your script could be more efficient with added sleeps.

The bottom line is, if you find yourself tempted to put a script in each prim, stop and think about how you could do it differently with fewer scripts. There is almost always an alternative. It is up to you to decide whether the alternative is a viable one for your application.

Micro-optimization

There are many ways to speed up scripts, such as using ++a instead of a++, however, most of these micro optimizations do not hold true for Mono. See LSL_Hacks for more specific information.

How Fast Does That Code Run

The following code will get the time for each function call in millis.

Please first propose changes to the discussion tab. Please do not change the code here without discussion, as any small change could reduce the accuracy of measurements. Please note this code and its comments should exactly match the code and comments presented by the Efficiency Tester page.

Thanks to Xaviar Czervik for the original code, thanks to Strife Onizuka for tuning the code to produce more accurate measurements, and thanks to the other contributors listed in the history of this article.

//IMPORTANT: Only perform tests in an empty region.
// To reduce contamination and be sure to wearing no attachments.
// Preferably do tests in a private sim with one on it.
// Don't move while performing the test.
// There is a margin of error so run the tests multiple times to determine it.

integer time() { // count milliseconds since the day began
    string stamp = llGetTimestamp(); // "YYYY-MM-DDThh:mm:ss.ff..fZ"
    return (integer) llGetSubString(stamp, 11, 12) * 3600000 + // hh
           (integer) llGetSubString(stamp, 14, 15) * 60000 +  // mm
           llRound((float)llGetSubString(stamp, 17, -2) * 1000000.0)/1000; // ss.ff..f
}

default {
  state_entry() {
    llOwnerSay((string) llGetFreeMemory());

    //test variables
    float counter;

    //framework variables
    float i = 0;
    float j = 0;
    float max = 10000; // 2ms of work takes 20 seconds to repeat 10,000 times, plus overhead

    float t0 = time();
    do {

      //test
      counter += 1;
      
    }while (++i < max);
    float t1 = time();
    do ; while (++j < max);
    float t2 = time();//remove the time required by the framework
    float elapsed = ((t1 - t0) - (t2 - t1))/max;
    llOwnerSay("The function in the loop took a total of " + (string)elapsed + " milliseconds.");
  }
}

Efficiency

The following data was collected using the above function in a recently deployed empty (though not private) sim, with only that one script running in a HUD attachment. 20 tests of each were run and averaged.

  ++a:     0.173780 ms    std. dev.:  0.003393 ms
  a += 1:  0.181720 ms    std. dev.:  0.013267 ms
  a++:     0.243500 ms    std. dev.:  0.013816 ms

Thus a++ takes 40% longer to execute than ++a (rough estimate).

While the following data is correct, the function above was not used. A slightly less optimized version written by Xaviar Czervik was used. If someone could please repeat the tests with the function above.

  ++a:     0.364700 millis
  a += 1:  0.346900 millis
  a++:     0.413700 millis

Testing the same function in for loops:

  ++a:     0.358370 millis
  a += 1:  0.351200 millis
  a++:     0.424600 millis

llOwnerSay v. llSay v. llShout v. llWhisper (Channel 0 where applies):

  llOwnerSay(): 4.359000 millis
  llWhisper():  5.201000 millis
  llSay():      5.226000 millis
  llShout():   14.877000 millis

llOwnerSay v. llSay v. llShout v. llWhisper (Re-tested under Mono as of 04/02/09. Channel 1 for regionsay, listener set up nearby):

  llOwnerSay():   3.607200 millis
  llWhisper():    4.871200 millis
  llSay():        3.975200 millis
  llShout():     10.931200 millis
  llRegionSay():  0.312000 millis

Different Channels (llSay() Used for all):

  -100000000: 1.226400 millis
  -100000:    1.254300 millis
  -100:       1.296100 millis
  -1:         1.292400 millis
  0:          5.226000 millis
  1:          1.242300 millis
  100:        1.249100 millis
  100000:     1.219700 millis
  100000000:  1.228700 millis

Amount of text (llSay() and Channel 1 used for all):

   1 Character:    1.242300 millis
   10 Characters:  1.309700 millis
   100 Characters: 1.965600 millis