Don't click here unless you want to be banned.

LSL Wiki : ReadyJack

HomePage :: PageIndex :: RecentChanges :: RecentlyCommented :: UserSettings :: You are

18 Mar 2006


27 Feb 2006

performance tips

As always keep in mind that tweaking for performance should be a last resort reserved for cases where specific actual issues exist. Code maintainablility is much more important than over-performing.

I put together some tests today and verified some ideas and questions I've had regarding LSL performance.

Functions and variables:

Inlining (replacing a user defined function call with the actual code) can be very beneficial.
Local variables (declared within a function) are slightly faster than global variables.


Avoid long lists wherever possible.
If you have a situation where you have two things you want to store in a list format (for example: name and key, use two shorter lists rather than one longer "2D" list. Two shorter lists are much faster.
Don't think pulling out a section using llList2List() and then operating on that shorter list will help. The overhead of created multiple lists kills the slight gain in efficiency.
Use the type specific functions (like llList2Float()) rather than type casting a string (like (float) llList2String()) wherever possible. It's quite a bit faster.
Strings as lists suck because llSubStringIndex doesn't have an offset so you end up splitting the "list" with two more calls each to get the piece you want and one to trim the list so you can use llSubStringIndex to find the next chunk. Meh.
You'd think that adding to the front of a linked list might be faster than adding to the end, but the way it's implemented in LSL apparently doesn't benefit from the features of linked lists yet retains all the disadvantages.


There's no difference between for and while loops. As indicated elsewhere there may be a small delay associated
with loops. I'm currently testing whether recursion can help. Unrolling the loop a bit (doing things in the loop twice or three times and handling the extra incrementing) can help...Well, I came up with something that works well for me. Doing recursion I eliminated 3 loops and a variable (list) cleanup of non-ordered elements problem I was having worked out perfect under recursion. But there's a ton of list copying going on. Good for my particular problem, but probably would rarely benefit most scripts. In simpler test scripts recursion was soundly beaten by standard list access.

better time check for performance testing

// time test // ready jack // v1.1
// takes two timestamps and returns the difference in seconds as a float
// only valid for short durations within an hour
float time_test(string ts_before, string ts_after) {
    list befores;
    list afters;
    float result;
    befores = llParseString2List(ts_before, ["T","Z",":"], []);
    afters = llParseString2List(ts_after, ["T","Z",":"], []);
    integer bmin = (integer) llList2String(befores, 2);
    integer amin = (integer) llList2String(afters, 2);
    float bsec = (float) llList2String(befores, 3);
    float asec = (float) llList2String(afters, 3);
    if (bmin < amin) {
        result += 60 * ((amin - 1) - bmin);
    } else if (amin < bmin) {
        result += 60 * (amin + 60 - bmin);

    if (bsec < asec) {
        result += asec - bsec;
    } else if (asec < bsec) {
        result += asec + 60.0 - bsec;
    return result;

- Exactly how much quicker was this? Its awful coding practice, but in a place like SL, performance is obviously more of an issue NikEbisu

I'm not sure if this is meant for the above section or the following. For the above section the difference is trivial. You'd have to be looping constantly for a long time to notice. I think I have more detailed info on the StyleGuide page (it's gone now...but just use ++x). The difference for the following section (i.e. using a local variable to store a static list length) is huge. See the numbers at the bottom. -- RJ

23 Jan 2006 - Loop Speed Challenge!

Here's the test script:

// loop speed test // ready jack // 23 jan 2006 // v1.0

list foo;

        integer i;
        for (i = 0; i < 300; ++i) {
            foo += (integer) llFrand(100.0);

    touch_start(integer total_number)
        integer count;
        integer i;
        llOwnerSay("test1 before: " + llGetTimestamp());
        for(i = 0; i < llGetListLength(foo); ++i) {
        llOwnerSay("test1 after: " + llGetTimestamp());
        integer len = llGetListLength(foo);
        llOwnerSay("test2 before: " + llGetTimestamp());
        for(i = 0; i < len; ++i) {
        llOwnerSay("test2 after: " + llGetTimestamp());

And some sample output:

Object: test1 before: 2006-01-23T08:36:03.964150Z
Object: test1 after: 2006-01-23T08:36:10.705834Z
Object: test2 before: 2006-01-23T08:36:12.778144Z
Object: test2 after: 2006-01-23T08:36:13.023281Z
Object: test1 before: 2006-01-23T08:36:23.486117Z
Object: test1 after: 2006-01-23T08:36:30.314002Z
Object: test2 before: 2006-01-23T08:36:32.386351Z
Object: test2 after: 2006-01-23T08:36:32.631740Z
Object: test1 before: 2006-01-23T08:36:35.974632Z
Object: test1 after: 2006-01-23T08:36:42.691158Z
Object: test2 before: 2006-01-23T08:36:44.763031Z
Object: test2 after: 2006-01-23T08:36:45.006860Z
Object: test1 before: 2006-01-23T08:36:49.975469Z
Object: test1 after: 2006-01-23T08:36:56.694460Z
Object: test2 before: 2006-01-23T08:36:58.765095Z
Object: test2 after: 2006-01-23T08:36:59.009746Z

Scripts and Snippets


Sample Code

Float Script
// Float 2.0 - An attachment script that lets you conveniently work at high altitudes.
// By Ready Jack in or around March 2004

// Usage: float [command|value]

// Examples:
//      float       (keeps the current altitude)
//      float 1.0   (same as float by itself)
//      float off   (turns it off)
//      float 0.0   (same as off)
//      float +     (increase the buoyancy by gDelta - see below)
//      float -     (decrease the buoyancy by gDelta)
//      float 10.0  (rocket skyward)
//      float -10.0 (sink like a rock)

// change this to a private channel for invisible key mapping
// or set it to 0 for spoken commands
integer gComChannel = 5; 

// this is the ammount the current value is adjusted by for the +/-
float gDelta = 0.05; 

float gCurrentFloat;
    on_rez(integer param)
        llListen(gComChannel,"",llGetOwner(), ""); 
    listen(integer channel, string name, key id, string message)
        list tokens = llParseString2List(message, [" "], []);
        if (llList2String(tokens, 0) == "float")
            if (llGetListLength(tokens) == 1)
                gCurrentFloat = 1.0;
            else if (llGetListLength(tokens) == 2)
                if (llList2String(tokens, 1) == "off")
                    gCurrentFloat = 0.0;
                else if (llList2String(tokens, 1) == "+")
                    gCurrentFloat += gDelta;
                else if (llList2String(tokens, 1) == "-")
                    gCurrentFloat -= gDelta;
                    gCurrentFloat = (float) llList2String(tokens, 1);

Global Dynamic Storage - an associative array with appropriate getter/setter functions. - TODO

In-world Persistent Storage - A bit of a kludge. Reads ini format text from a note in inventory into a data structure which can be used and modified in place and then dumped to the chat window in a format which can be copy/pasted back into the note. Good for saving the locations of dynamicly rezzed objects and other similar problems.

group detect

conversation enhancer


This script will cause an object to disappear or appear to fall through the ground, but the physics of the object stay where expected. Put it in an object and touch it a few times and you'll see what I mean. You can get the object back back dragging the ground where it is. As you as you select it, it will reappear.

vector gPos;
integer gFirst = TRUE;

                | STATUS_ROTATE_Z, FALSE);
        gPos = llGetPos();
    touch_start(integer total_number)
        vector target;
        if (gFirst)
            target = gPos + <0,0,1.0>;
            gFirst = FALSE;
            target = gPos + <0,0,5.0>;
            gFirst = TRUE;
        llSetStatus(STATUS_PHYSICS, TRUE);
        llMoveToTarget(target, 0.5);
        llSetStatus(STATUS_PHYSICS, FALSE);

After observing this some more, it seems that the disappearance occurs after the sleep during the combination of disabling physics and stopping the llMoveToTarget. Changing the order of the two last lines doesn't help. My guess is that the client is letting the object freefall into oblivion before the server has a chance to update the physics setting.
There are 2 comments on this page. [Display comments/form]