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

LSL Wiki : StyleGuide

HomePage :: PageIndex :: RecentChanges :: RecentlyCommented :: UserSettings :: You are ec2-204-236-235-245.compute-1.amazonaws.com

LSL Style Guide


This document describes good LSL coding practice. Were you looking for the WikiStyleGuide?

Style in coding is usually summed up in two categories; Cleanliness and Speed.


Cleanliness

This is easy once you know the rules, but there are a few things that most programmers are picky about. The biggest one is indents/tabs. Take this code, for example:

default
{
state_entry()
{
if (x==1)
llSay(0, "X is: "+(string)1);
}
}

This is not properly indented/tabbed. After every bracket, you go in an additional tab. When you close the bracket, you go back out a tab. The LSL editor takes care of this... in a limited fashion, but you should always watch your tabs. That same code, properly tabbed, looks like:

default
{
     state_entry()
     {
          if (x == 1)
               llSay(0, "X is:" + (string) 1);
     }
}

This code is properly tabbed, and it makes following FlowControl much easier. Other than that, keep your hard-returns to the end of every line of code, and you should be fine.

This compares other styles of tabbing out your scripts, check it out! :)

Cleanliness Tips: (Please contribute!)

The Java Code Conventions can also be a good place to look for tips.



Speed


The other style point is speed, which is a much more difficult thing to master. Basically, you are looking for faster-executing code here. Using x++; is slower than using x=x+1; Simple things like that can speed your code up a great deal. LSL shouldn't be compared to programming a PC. It's more like programming an embedded system. You have very little memory and CPU to work with, so writing faster and less memory-intensive code is paramount.

Indeed. You have only 16kb of available memory, which has to be shared between bytecode, stack and heap. You also are going to have a hard time telling LSL exactly what to do with that memory, since you have no arrays or pointers. Memory is your single most valuable resource, and you see how much you have left by calling llGetFreeMemory(). -AF

If you wish to throw caution to the wind take a look at this thread:
"How to optimize code and when not to"

Tweaks: (Please contribute!)



Furthermore, if you are debugging around people, or in general, it is nice to get into the habit of llOwnerSay(..) rather than llSay(0, ..) because it often bothers others around you who are attempting to have a conversation. -SleepyXue


integer len = llGetListLength(list);
for(i = 0; i < len; i++)

Has anyone tested if declaring variables outside of loops helps speed things up? -Chris

I just did. I'd recommend getting the length outside the loop. The difference was about an order of magnitude in the test I put together. You can see the script and some sample results on my page. - RJ

It actualy depends if you set a default value. string a;while(b){a = llGetSubString(a,--b,b);} is going to be marjonaly slower then while(b){string a = llGetSubString(a,--b,b);} only because the null declaration has been removed. -BW



The list does advantages though, they do provide a convenient syntax for storing SMALL data structures. Just avoid storing anything with more than 10-20 items in a list and you'll be ok.

For integer arrays, there is a kludge you can use to get around using lists. Strings are much faster and take up about 1/40th the amount of memory for very small values. Here's the trick:

string letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890!@#$%^&*() []{}-=_+;':,.<>/\|`~";

string stack = "";
integer s = 0;

//Push back an integer
push(integer val)
{
     stack += llGetSubString(letters,val,val);
     ++s;
}

//Pop off a value
integer pop()
{
     if(s)
         return llSubStringIndex(letters,llGetSubString(stack,--s,s));
     return -1;
}

You could take this a step forward, using two or more characters to encode a value instead of just one.

In my own recent tests (in 1.6.9), storing integer data in strings is about half the speed of storing them in lists. My case was a bit different than the above example, but actually the conversions should have been faster (using typecasting). Any else have any other recent experience? -- ZarfVantongerloo


default
{
    state_entry()
    {
        llSay(0, "Timing normal loop");
        string s = "";
        integer i;
        
        float t = llGetTime();
        for(i=0; i<1000; i++)
            s += (string)i;
        t = llGetTime() - t;
        
        llSay(0, "Time: "+(string)t+" seconds.");
        
        llSay(0, "Testing unrolled loop x10");
        string q = "";
        
        t = llGetTime();
        
        for(i=0; i<1000; )
        {
            q += (string)(i++);
            q += (string)(i++);
            q += (string)(i++);
            q += (string)(i++);
            q += (string)(i++);
            q += (string)(i++);
            q += (string)(i++);
            q += (string)(i++);
            q += (string)(i++);
            q += (string)(i++);
        }
        
        t = llGetTime() - t;
        llSay(0, "Time: "+(string)t+" seconds.");
    }
}

On average, the second loop runs about 6-8 seconds faster. Here were the times:

Normal:
30.575254
31.783129
28.784805

Unrolled:
22.258141
24.367455
22.544336

Time will vary dramatically with server load, but it is still clear that this is much faster. The tradeoff is the extra memory involved in unrolling your loop. - AF


Counter Opinion: Tweaks Considered Harmful

Unless your script is running up against the memory limit (you are getting Stack-Heap collisions), or you are writing performance critical code (and if you're thinking about this question, then you're not), don't worry about tweaking your code at all.

If you are running up against the memory limit, consider if any functionality of the script can be moved to a helper script. For example, if you have a giant list of strings that are used with llSetText, put these in a separate script with the llSetText code, and send the list index to this script using a link message

The most important attribute of code is correctness that is, does it do what it should. The next most important are readibility and maintainability, that is can you (after leaving the computer to watch Buffy for an hour) or someone else figure out what the code's supposed to do, easily spot bugs, and fix them without the whole script falling down like a house of cards? Tweaking and optimizing your code runs counter to all of these important attributes. However, it is good practice to write your code efficiently the first time, while keeping it neat. For example, using the ++i instead of i++ is a good habit.

First make your code work.
Then make it correct.
Then make it neat.
Then add any extra features you want.
Then if --and only if-- performance is an issue, look for slow bits you can improve (the best place to look for these is loops, are you doing something every iteration that you only need to do once?)
Then paint the house, wash the dog, bake cookies, do the dishes, vacuum the rug.
Then tweak and optimize your code in any way that does not sacrifice readibility or correctness.

Tweaking and optimizing your code is often clever, and many times has no effect if you do it blindly. Try to find where the slow points are, and optimize those (and only after finishing everything else.) Or you can do it right the first time. It's easier to do it right once than to try and optimize it without breaking it.

BBC's opinion: if you are writing a script that has to be fast, your design sucks. Never design for speed, design for usability.
BW's thought: some scripts have to be designed for speed: rapid firing guns. Other scripts have to optimized to stay below the 16k memory limit: databases. It's best to optimize it after you have a working model. For databases simple compression isn't a bad idea. For guns and other scripts that need speed it's best to reduce the amount of math that is required and overall function calls; with speed the name of the game is: "good enough" not "perfect".
I think that writing scripts to be efficient is a good practice. No one wants a script that takes five seconds to do one basic function. Also, efficient scripts put less load on the sim, and is usually easier to read. --KeknehvPsaltery

If you're writing code that is slow, you aren't taking into consideration people's patience for your script. In a language like C++ with little luxuries like arrays and an almost unlimited memory allowance that work efficiently and quickly I say good design is god. However, LSL has many little quirks and if we did things the way that work the most logically our code would be horribly slow to the point of unusability not to mention cause memory problems. For instance I'm making a simple tic tac toe game that checks for a winner each time a square is clicked. I tried doing that with lists and somehow thought people wouldn't want to have to wait the 5 seconds to make each and every move that it wound up taking. That might work with chess, but not so much with tic tac toe. My results have been much more successful using bitwise math even though it's less readable and using vectors as 3 deep mini arrays even though it's almost incomprehensible to a person looking at it for the first time.
In short, I think that speed and memory are far more neccessary than clean, cookie cutter, leave it to beaver code. What you need to do is be all kinds of tweaky, crazy and edgy to dodge the LSL pitfalls and gotchas but comment and document each and every piddly bit thoroughly.
/ / = GOD -bafiliusGoff


WikiStyleGuide | Hacks | Lag | NamingConventions
There are 13 comments on this page. [Display comments/form]