Welcome to the Second Life Forums Archive

These forums are CLOSED. Please visit the new forums HERE

How to optimize code and when not to.

Strife Onizuka
Moonchild
Join date: 3 Mar 2004
Posts: 5,887
02-16-2006 11:28
LSL is like Cerberus the three headed dog. The heads are "Speed", "Size" & "Readability" If you try to get this dog to do any fancy tricks, one or more of the heads is going to bite you. And then there is the tail, its named "EVIL LSL compiler bugs" and when the dog turns its back on your, the tail swings around and knocks you off your feet.

The first thing is to know when you should optimize code and how much. First question you should ask is "when the script breaks am i going to be able to debug it without having to rewrite this?" This is a good opertunity to write in comments in your code. I like to write them for the mysterious reader who is peaking over my shoulder. Next question "Will this increase speed or decrease size or maybe both; is that my goal?" In LSL most first pass optimizations will decrease size and increase speed. As the optimizations get more complex, you will probably only get one or they other, probably at the cost of the other. An important trick is to learn how to dodge the tail.

Sometimes you can dodge the tail, sometimes it hits you. The the main errors are "39.4-a" this will compile as "39.4"; "if(test);{}" and the "you fool you wrote a bit of code to complex to parse in 16k memory, i'm going to die a horrible death and return a nonsensical error code". This last one is the worst, its not that your code won't fit in the memory space, it's that the compilers stack is too small. There are other horrible compiler issues but i hardly ever see them (for example string & key not_compairs do not return just TRUE).

Ok Ok, i'll quit stalling and get onto the optimizations :D

LSL if statements generaly only want integers though you can feed them strings, keys, integers, floats, and rotations & vectors (i'm not sure what passing it a rot would do). When fed one of these, it makes sure that the value isn't zero or zero in length.

the ";" results in the value being popped off the stack if there was one. One way to make things faster and save memory is pretty obvious (if you think like me), don't pop it off the stack.
CODE

integer a;
a = 4;
b = a;
//becomes ... and we save 12 bytes and our script runs faster yeah
integer a = b = 4;
------------------------
a = llGetAttached();
if(a == 5)
{llOwnerSay((string)(a * b));}
//becomes ... and we save 6 bytes, again faster script, but it begins to get unreadable.
if((a = llGetAttached()) == 5)
{llOwnerSay((string)(a * b));}
------------------------
string b = llGetSubString(a,0,0);
return llParseStringKeepNulls(llDeleteSubString(a,0,0), ,[]);
//becomes... NOOOOO DON'T, this was a trick, in this case "a" is a huge string,
//it takes up a good portion of memory, if we were to make this...
return llParseStringKeepNulls(llDeleteSubString(a,0,0), [llGetSubString(a,0,0)],[]);
//we would end up with our string in memory at least 3 times at the time of executing the command
//(once as "a" once without it's first char and once coppied as the value for llGetSubString)
//by seperating we optimized for script runtime memory. :P


Memory: sometimes you have to give a little to get a little. Calling functions is expensive. Don't call a function twice, call it once and store the result. Calling an LL function without a return or any paramaters costs 18 bytes. If it returns a value, add 1 byte, add on space for paramaters, and if it's a user functions add on 2 more bytes.

cost per paramater based on type.
CODE

type bytes (on stack)
--------------------------
list 4
string 4
integer 4
float 4
vector 12
rotation 16


Jumps aren't evil, but use them sparingly. Like guns, jumps aren't evil; just the people who abuse them.

CODE

while(1){}
//becomes ... rarely useful, saves 6 bytes
@a;{}jump a;


do{}while(test); are faster then regular while(test){} loops and use 5 fewer bytes.

for() loops is just an invitation for truoble; there is very litte optimization you can do with a for loop. Use a while() instead.

Reduce & recycle.
do the math, don't put 4*6 in your code. You don't stay smart resting on your lorals.
if your tight on memory then recycle your variables, do you need a new counter for each loop? sure your code will be unreadable but it won't crash. I am notorious for using leter varriable names, and recycling them.

SLEEP, don't write optimized code when your tired. Like the woman you came home from the bar with, what you though was a good idea at 4AM really wasn't (nor should you write guides on optimizing when your tired).

avoid lists.
avoid strings & keys.
heck avoid using everything.
like the program that printed it source, but did so by not having any.

If your going to write optimized code you reallly need to grok your code first. Make sure it works first because chances are in the process of optimizing it you will introduce new bugs, and its alot easier to squish them when you don't have to hunt down the older ones....


I'll post more tips on getting faster code after i get some sleep *yawn*
_____________________
Truth is a river that is always splitting up into arms that reunite. Islanded between the arms, the inhabitants argue for a lifetime as to which is the main river.
- Cyril Connolly

Without the political will to find common ground, the continual friction of tactic and counter tactic, only creates suspicion and hatred and vengeance, and perpetuates the cycle of violence.
- James Nachtwey
Argent Stonecutter
Emergency Mustelid
Join date: 20 Sep 2005
Posts: 20,263
02-17-2006 10:16
From: someone
Calling an LL function without a return or any paramaters costs 18 bytes.


OK, is this "the opcodes generated by LSL for a function call take up 18 bytes" or "a function's stack frame is 18 bytes long"? In the latter case, that 18 bytes is the same 18 bytes that the next function call uses, so unless you're doing recursion it shouldn't be a concern.
Lex Neva
wears dorky glasses
Join date: 27 Nov 2004
Posts: 1,361
02-17-2006 11:13
I dunno, Strife. Most of the optimizations you're talking about here are trying to save tiny bits of memory by using constructs that compile to smaller bytecodes. If you need to save 16 bytes, you're doing something wrong. Very often, tidying up your algorithms will save you orders of magnitude more memory than trying to use convoluted syntactical constructs like you've shown. I know you're being clever, and you've made the proper warnings about commenting and not optimizing while low on sleep... but I'm still not with you on this.

You haven't talked about things like large constants in code. If you initialize a global list variable to a long constant list, you lose that list's memory TWICE, once to store it in the code and once to store it in memory at runtime. You briefly touched on the fact that lists get copied at the drop of a hat, and can balloon up your script's memory usage at runtime. I think stuff like this is far more important than saving 6 bytes by using a jump rather than a while.

When it comes right down to it, if you find you really need to save those 6 bytes, chances are some combination of events during the running of your script (or, say, memory leaks? I've heard rumors) will cause your script to overrun its stack anyway, sometime in the future. If you manage to just squeak under the limit by saving 6 bytes here and 18 there, you don't have enough "wiggle room" and your script will probably eventually crash. Far better to just refactor it into two scripts that use link messages... better safe than sorry.
Strife Onizuka
Moonchild
Join date: 3 Mar 2004
Posts: 5,887
02-17-2006 11:57
Don't knock it. Say your doing lots of vector math and hitting the memory limit; your script has lots of vectors or rotations hardcoded.

CODE

rotation a = <0,0,0,1>;
//becomes... and saves 8 bytes
rotation a = <0.0,0.0,0.0,1.0>;


say you have 20 -> 40 rotations.
thats 160 -> 320 bytes or 1%->2% of your scripts memory.
and did i mention your script is faster?

when i refere to bytes, i mean LSL bytecode.

memory leaks are rare, i've yet to write a script that leaks memory because of bugs in LSL. I don't use states much and so i don't need the state-function hack.

By reducing the number of variables in your stack, you also reduce the memory the stack requires and the size of the bytecode. If you are using recursion this is a must. When a script is compiled the amount of memory required for all its declaired variables including paramaters are added up and stored in the function table (same with events, in actuality events and functions are handled exactly the same thing). One of the diservices LSL does is not let you use the return variable until the very end.
CODE

integer a = 5 * 6;
return a;
//becomes... saves 8 bytescode, 4 stack
return 5 * 6;


a few bytes aren't worth saving, but if your coding style is sloppy your scripts will be slower and you will hit the memory limits sooner.

Most modern good programing practices are unreasonable for LSL. We are writing code for a VM of 16k of memory, which places us firmly in the 1970s, long before most modern good programing practices were invented. Back then, numbering your bunch cards was a good programing practice. Any system that forced pass by value would not have sold.
_____________________
Truth is a river that is always splitting up into arms that reunite. Islanded between the arms, the inhabitants argue for a lifetime as to which is the main river.
- Cyril Connolly

Without the political will to find common ground, the continual friction of tactic and counter tactic, only creates suspicion and hatred and vengeance, and perpetuates the cycle of violence.
- James Nachtwey
Strife Onizuka
Moonchild
Join date: 3 Mar 2004
Posts: 5,887
02-17-2006 12:31
Challenge, send me a script that is close to compile time memory limits, i'll (try) to rewrite it and to tell you exactly how many bytes have been saved. The script has to be a real world script; not something contrived for this excersize.
_____________________
Truth is a river that is always splitting up into arms that reunite. Islanded between the arms, the inhabitants argue for a lifetime as to which is the main river.
- Cyril Connolly

Without the political will to find common ground, the continual friction of tactic and counter tactic, only creates suspicion and hatred and vengeance, and perpetuates the cycle of violence.
- James Nachtwey
Lex Neva
wears dorky glasses
Join date: 27 Nov 2004
Posts: 1,361
02-17-2006 13:36
I guess I wasn't completely clear in what I was saying. Some of the optimizations you've suggested, especially that rotation one you just brought up that uses integers instead of floats, make perfect sense. They're no-lose optimizations. But on the other hand, some of the things you suggested in your initial post involve jumping through hoops, going through your code with a fine-toothed comb, and, most importantly, obfuscating your code. I know we're kind of in a warzone here with LSL as you've described, but it's kind of a bit drastic to severely sacrifice your readability for 16 bytes here and 12 there. That's all I'm saying.

Well, okay, I guess it's not all: I'm also saying that you'll probably save yourself a lot of time and energy just breaking a script up into multiple scripts that fit comfortably within the script buffer. Your code will be more readable, and you won't get bitten two months down the road because a certain execution path happened to make your code keep too much data around at once and overrun the stack. I'm not saying that it's impossible to tell when something like that will happen (although often it can be difficult to see!), but I am saying that you can very often save a lot of time by just using multiple intercommunicating scripts, and your code will probably make a lot more sense, too.

As to your challenge, I'm going to have to pass. I learned my lesson the first time I ran into the fact that my code itself wouldn't fit into 16k, much less its variables, and from then on I planned my large scripted systems from the outset to use multiple scripts when necessary. The point here isn't that I doubt your ability to save a lot of memory by scrimping a few bytes per line, it's that I don't think it's worth the time you're going to spend doing it.
Argent Stonecutter
Emergency Mustelid
Join date: 20 Sep 2005
Posts: 20,263
02-17-2006 14:42
From: Strife Onizuka
CODE

rotation a = <0,0,0,1>;
//becomes... and saves 8 bytes
rotation a = <0.0,0.0,0.0,1.0>;
You're pulling my leg. LSL doesn't even do OBVIOUS compile-time conversions, and compiles code to convert each integer to a float? And it even does this at a global level? If that's the case, why can't you use constant expressions at the global level?
Argent Stonecutter
Emergency Mustelid
Join date: 20 Sep 2005
Posts: 20,263
02-17-2006 14:42
From: Strife Onizuka
Challenge, send me a script that is close to compile time memory limits, i'll (try) to rewrite it and to tell you exactly how many bytes have been saved. The script has to be a real world script; not something contrived for this excersize.

Franimation Overrider. It needs it anyway!
Ziggy Puff
Registered User
Join date: 15 Jul 2005
Posts: 1,143
02-17-2006 14:55
Tooting my own horn here a little bit...

When I wrote the ZHAO (which is my HUD AO based on the Franimation AO), I added some new options - more sits, walks and ground sits. Which meant more lines in the notecard. That script wouldn't run, period. Trying to read a notecard would always give me a stack-heap collision. Digging through the script, I discovered at least one reason why - it fires off all the llGetNotecardLine requests in parallel, and keeps a list of the request keys (since the responses aren't guaranteed to be in the same sequence as the requests). As the responses come in, the key is checked against the saved list, and the appropriate anim name is updated.

I changed that and serialized the notecard reading, so I only keep one key around and request the next line from inside the dataserver event. That went from stack-heap collisions *every* time I tried to read a notecard (even an empty notecard, so the actual animations list was empty), to about 3K free memory reported by the script with a moderately filled-in notecard. And I didn't notice any huge increase in notecard read times. Even if I had, I'd rather have a slower script that runs, than a faster script that runs out of memory :)

So... in this case, it lines up with what Lex said. The solution was to change something in the design of the script, not save a few bytes here and there.

Having said that, I appreciate Strife's advice. There will be situations where that kind of optimization will become necessary. IMO it's not recommended as standard coding practice, and I think Strife makes that very clear in his original post.
Eloise Pasteur
Curious Individual
Join date: 14 Jul 2004
Posts: 1,952
02-18-2006 01:12
Some of the things Strife's said (which he did say at the top in fairness) I sincerely doubt I'll ever use, readibility is more important.

He's said it before, and I've listened to it, about floats being less memory intensive if given as floats, something I now try to do. I'm sure I can, if I try, find a script I've got that never uses floats, but the vast majority of them do and usually quite a few. Saving a few bytes per float, so what, but saving a few bytes lots of times in a tight memory environment is always to be welcomed I'd have thought.
Strife Onizuka
Moonchild
Join date: 3 Mar 2004
Posts: 5,887
02-18-2006 06:29
I felt it would be good if i choose a scirpt at random from the script library. Then i had an evil idea and picked one of Lex's. I chose PipeMaker.

So i spent some time and whacking away at the script.
PipeMaker
Before: 7080
After: 6342
Diff: 738
Or just over 10% of the scripts memory was recovered with a little clever coding. As i didn't want to actualy rewrite the script, i did not recover all that could be recovered. I tried to keep the structures of the script as close to the origional. The rest that could have been recovered would have come though restructuring.
_____________________
Truth is a river that is always splitting up into arms that reunite. Islanded between the arms, the inhabitants argue for a lifetime as to which is the main river.
- Cyril Connolly

Without the political will to find common ground, the continual friction of tactic and counter tactic, only creates suspicion and hatred and vengeance, and perpetuates the cycle of violence.
- James Nachtwey
Kyrah Abattoir
cruelty delight
Join date: 4 Jun 2004
Posts: 2,786
02-18-2006 07:25
i use a little trick about the NULL_KEY

when you have a script with a lot of linkmessages and key comparison(thet usually need NULL_KEY)

try this:

key null = NULL_KEY;

and use "null" each time you need NULL_KEY

it savec a good amount of bytes for me
_____________________

tired of XStreetSL? try those!
apez http://tinyurl.com/yfm9d5b
metalife http://tinyurl.com/yzm3yvw
metaverse exchange http://tinyurl.com/yzh7j4a
slapt http://tinyurl.com/yfqah9u
Strife Onizuka
Moonchild
Join date: 3 Mar 2004
Posts: 5,887
02-18-2006 08:18
To be able to effectively optimize any script you must grok the code first. If you do not grok it to its entirety you are liable to intoduce logic errors.

Some scripts will not optimize nicely or at all. There is only so much logic you can rewrite.

The only time when LSL will typecast your integers to floats at compile time is when they are globals. LL's way of handles globals is kinda funny.
_____________________
Truth is a river that is always splitting up into arms that reunite. Islanded between the arms, the inhabitants argue for a lifetime as to which is the main river.
- Cyril Connolly

Without the political will to find common ground, the continual friction of tactic and counter tactic, only creates suspicion and hatred and vengeance, and perpetuates the cycle of violence.
- James Nachtwey
Strife Onizuka
Moonchild
Join date: 3 Mar 2004
Posts: 5,887
02-18-2006 10:04
If you want a faster while loop and don't care about size use
CODE

while(test)
{

}
becomes.... saves you 1 operation and 5 fewer bytes parsed per cycle.
at the cost of mem requirements of test + 2. If you need fast code, this is for you.
if(text)
{
do
{

}while(test);
}


a while loop is really
CODE

@loop
if(test)
{

jump loop;
}


when the param of a do-while loops is true it jumps.
when the param of a while, for, or if, is false it jumps.

CODE

if(test)
jump somewhere;
sadly the LSL compiler does not recognize this and convert it to the type of if that a do-while uses (which would free up 5 bytes).


CODE

if(test)
{}
else
{}
is really
if(test)
{jump after;}
{}

likewise

if(test)
{}
else if(test2)
{}
is really
if(test)
{jump after;}
if(test2)
{}
@after;

example of how the LSL compiler sucks.
if(test)
{jump go;}
else if(test2)
{}
is really
if(test)
{jump go; jump after;}
if(test2)
{}
@after;

it should remove dead code IMHO.
_____________________
Truth is a river that is always splitting up into arms that reunite. Islanded between the arms, the inhabitants argue for a lifetime as to which is the main river.
- Cyril Connolly

Without the political will to find common ground, the continual friction of tactic and counter tactic, only creates suspicion and hatred and vengeance, and perpetuates the cycle of violence.
- James Nachtwey
Argent Stonecutter
Emergency Mustelid
Join date: 20 Sep 2005
Posts: 20,263
02-18-2006 10:32
From: Kyrah Abattoir
key null = NULL_KEY;
Holy Mother of Kemeny, that's an old interpreted BASIC trick. Ick. Just... ick. I feel dirty.
Strife Onizuka
Moonchild
Join date: 3 Mar 2004
Posts: 5,887
02-18-2006 10:49
variable recycling, i touched on it before. Here is why it's a must.

CODE

some(integer function)
{
if(function)
{
integer a = 5;
integer b = 6;
doSomethingElse(a,b);
}
else
{
integer a = 5;
integer b = 6;
doSomethingElse(b,a);
}
}
//want to guess how much stack space has been reserved for the variables?
//If you said 8, your wrong, 12 while logical is still wrong. 20 bytes.
//In theory there are only ever 12 bytes needed for variables.
//The compiler is stupid. It reserves unique memory spaces for every variable.
//I kid you not. LSL allocates memory for 5 integers.
//
some(integer function)
{
integer a = 5;
integer b = 6;
if(function)
{
doSomethingElse(a,b);
}
else
{
doSomethingElse(b,a);
}
}//OMG this only uses 12 bytes, see what happens when you recycle?
//Not only does this cost 8 less in stack, we have free-ed up 24 bytes of bytecode.

//Every (local) variable allocated costs bytecode too
// 1 byte per state change (in the memory space)
// 1 byte per return (in the memory space)
// 1 byte for just existing (in the memory space)
_____________________
Truth is a river that is always splitting up into arms that reunite. Islanded between the arms, the inhabitants argue for a lifetime as to which is the main river.
- Cyril Connolly

Without the political will to find common ground, the continual friction of tactic and counter tactic, only creates suspicion and hatred and vengeance, and perpetuates the cycle of violence.
- James Nachtwey
Strife Onizuka
Moonchild
Join date: 3 Mar 2004
Posts: 5,887
02-18-2006 11:00
^_^ earthday is just a few months away but i feel like we need it now
Recycle, Reuse, Reduse
  1. Recycle your variables.
  2. Reuse your code by making functions.
  3. Reduse you code by merging lines.


(ok maybe that last cup of coffee was a bit strong)
_____________________
Truth is a river that is always splitting up into arms that reunite. Islanded between the arms, the inhabitants argue for a lifetime as to which is the main river.
- Cyril Connolly

Without the political will to find common ground, the continual friction of tactic and counter tactic, only creates suspicion and hatred and vengeance, and perpetuates the cycle of violence.
- James Nachtwey
Lex Neva
wears dorky glasses
Join date: 27 Nov 2004
Posts: 1,361
02-18-2006 11:01
Strife.... I'm not sure if you get my point here. Maybe I should just get off my soapbox exit this thread... you clearly seem to be having fun.

You've taken my PipeMaker, which was already arguably somewhat spaghetti-ish code that I hacked together in one morning, and cut 10% of its memory use. It was already nowhere near the memory limit in code size or runtime memory usage. There was no need to do what you've done. Every single one of your optimizations drastically reduced the readability of my code. Here's an example of an optimization you made:

CODE

// my code
torus_rotation = llGetRot();
torus_position = llGetPos() + llRot2Up(torus_rotation) * myScale.z * 0.5 - llRot2Left(torus_rotation) * (torus_radius - myScale.x/2.0);

vector face_center = llGetPos() + llRot2Up(torus_rotation) * myScale.z * 0.5;
rotation rot = llAxisAngle2Rot(llRot2Up(torus_rotation), DEG_TO_RAD * torus_rot);

torus_position = offsetRot(torus_position, face_center, rot);
torus_rotation = torus_rotation * rot;
torus_scale = <myScale.x, torus_radius*2.0, torus_radius*2.0>;
torus_hole_size = <1.0,myScale.y/(2*torus_radius),0.0>;



CODE

// your code
torus_position = offsetRot(
(torus_position += (llRot2Up(torus_rotation) * myScale.z * 0.5)) -
llRot2Left(torus_rotation) * (torus_radius - myScale.x * 0.5),
torus_position,
rot = llAxisAngle2Rot(llRot2Up(torus_rotation), DEG_TO_RAD * torus_rot));
torus_scale.x = myScale.x;
torus_hole_size.y *= myScale.y;


That's not a direct substitution because you refactored the scale out of that block, but it'll do. So let's see here... you took my already-obfuscated and ill-commented code, and you stuffed it all into one moster line. You use a += and an = as arguments to a function. Your code does exactly the same as mine, and yes, probably uses as much as a few hundred bytes less of bytecode.

Then again, you've removed all of my intermediate variables. in leiu of any kind of comments to explain what exactly I was doing, I built the answer piece by piece, naming each piece in an intermediate variable as I went. It provided at least some readability... but your optimization run made this seciton about as intelligible to an outside viewer as assembly code.

Then there's the fact that you "optimized" away a line in the on_rez event of the configure state, llSetColor(<1,1,1>,ALL_SIDES). I know it seemed like there was no reason for this, but there was: people sometimes pick up the red pipe piece from a friend because it's allow-to-copy like the rest. That code helps fix that issue, and make the thing look right when they rez it. It's not redundant. Kind of a contrived example of what I'm talking about, but it does prove that something can easily be lost in the optimization, and not just code size.

Sorry for using a cliche here, but you've pretty much made my point for me. You picked a script that absolutely didn't need optimization, and optimized it anyway. I wrote the code in a purposefully not-too-tight manner so that it was more readable, because I knew I had memory to spare, and you tightened it up and sacrificed a ton of readability.

For that, you gained 10% memory usage -- a mere 738 bytes. Not worth it. Sure, this is a bit of a contrived example, because my code didn't even come close to the 16k limit before you optimized it. Pretend it did, then. Say it was just a little too big to fit. This script is a great example of something that could be split into two scripts. One would do all of the UI and parsing stuff (ie the configure state), and the other could handle calculating all of the primitive params; just shunt all o fthat makeTorus and makeCylinder stuff into a new script that comes to play when the "master" rezzes the "slave".

The reason I'd almost always argue using two scripts in a situation like that rather than trying to optimize is a matter of programmer time and likelihood of positive outcome. Two scripts will almost definitely fit all of the code (and if not, use 3!), and it will probably not take much effort or time to refactor the script into two scripts. On the other hand, I bet you spent at least an hour or two just on optimizing my code, and you would have spent even more on a full-size script. You would spend even more later trying to debug that obscure optimized code, and even more important, there's no guarantee you can optimize it enough. There are limits.

Don't get me wrong, here, Strife. I said this before, and it looks like you didn't listen: I'm not doubting your ability to scrimp and save bytes. You proved that in the first post in this thread, and I never doubted it. You're a hacker, you're doing this optimizing because you enjoy tinkering. All I'm saying is that I think it's very unlikely that these optimizations you're suggesting have any practical use, and that they often lead to more problems than they solve. It's the same reason we don't write everything in assembly code.
Strife Onizuka
Moonchild
Join date: 3 Mar 2004
Posts: 5,887
02-18-2006 11:16
I admit, i am enjoying meself. I didn't choose the script as an example of a script that needed optimization. I choose it as an example of a script from the wilds. To prove how much can be saved on an arbitrary script. It was a script that, as i read it, i was thinking "yes i can reduce this script without having to climb inside the orig scripters head". It's a good script, nice intermediate variables , it has good structure, all things that make optimization easier.

I'm sorry about the llSetColor i was toying with the idea of adding a state to cycle the config state, so that i could remove all of the code in on_rez and i accidently deleted that line while moving up. But decided against changing the scripts structure. I'll repost with it placed back in. My goal was to keep 100% of the orig scripts functionality.

From: someone

That's not a direct substitution because you refactored the scale out of that block, but it'll do.

I factored the constant part of it out. The dynamic portion stayed, so in my mind it is a decent optimization. Pretty self explanitory compaired to the multiliner.
_____________________
Truth is a river that is always splitting up into arms that reunite. Islanded between the arms, the inhabitants argue for a lifetime as to which is the main river.
- Cyril Connolly

Without the political will to find common ground, the continual friction of tactic and counter tactic, only creates suspicion and hatred and vengeance, and perpetuates the cycle of violence.
- James Nachtwey
Strife Onizuka
Moonchild
Join date: 3 Mar 2004
Posts: 5,887
02-18-2006 11:40
This one drives me nuts.
CODE

integer i;
string s;
key k;
float f;
vector v;
rotation r;
list g;


i = 1;
s = "s";
k = "k";
f = 1.0;
v = <1.0,1.0,1.0>
g = [i,s,k,f,v,r];

why?
because it's the same as
CODE

integer i = 0;
string s = "";
key k = "";
float f = 0.0;
vector v = ZERO_VECTOR;
rotation r = ZERO_ROTATION;
list g = [];

i = 1;
s = "s";
k = "k";
f = 1.0;
v = <1.0,1.0,1.0>
g = [i,s,k,f,v,r];

If you have nicely named variables there is no reason for this waste and the cost to readabilty is so minimal it's not even worth considering. Even if you don't have nicely named variables, it's not worth it.
_____________________
Truth is a river that is always splitting up into arms that reunite. Islanded between the arms, the inhabitants argue for a lifetime as to which is the main river.
- Cyril Connolly

Without the political will to find common ground, the continual friction of tactic and counter tactic, only creates suspicion and hatred and vengeance, and perpetuates the cycle of violence.
- James Nachtwey
Masakazu Kojima
ケロ
Join date: 23 Apr 2004
Posts: 232
02-18-2006 11:55
"Jackson's Rules of Optimization: Rule 1. Don't do it. Rule 2 (for experts only). Don't do it yet--that is, not until you have a perfectly clear and unoptimized solution."

When optimizing for speed in LSL, I'd recommend having a harness that runs your function or script or whatever through several iterations and reports the final time. Try one change at a time and run it side-by-side with the previous version. Running them order will not give you any meaningful results. In many cases, running the same script 3 times in a row, I'll get results like 30 seconds, 45 seconds, and 28 seconds.

Reset each script before starting, and run them all multiple times. I once made a change that I thought improved the speed, but if I reset both scripts before starting, it was slower in every single case. If I reset just one of the scripts, it'd run faster than the other.

You should also have as many test cases as possible to verify that your result is still correct. A function that is 100x faster isn't any good if it doesn't work.

From: Lex Neva
All I'm saying is that I think it's very unlikely that these optimizations you're suggesting have any practical use, and that they often lead to more problems than they solve. It's the same reason we don't write everything in assembly code.
While tricks that save a few bytes probably aren't going to be applicable to an entire script, there are many cases where you need one function to run faster or in less memory, and this is where cutting every little corner helps. I think the unix timestamp function I posted a while back is a perfect example of something to optimize. Moving this out to another script would make it an ugly mess of spaghetti and black magic, and in the end it'd be even slower and probably even use more memory.

If you're optimizing for memory simply because your script needs more, then I'd agree you are doing something wrong. In many cases, though, saving a few bytes of memory in LSL translates to a significant performance improvement.

I once spent a week trying to out-optimize gcc on a small but important function and ended up with a savings of something like 0.1s on 10000 iterations. One of the reasons you don't have to write any assembly code these days is because compiler writers have collected all of these obscure optimizations with "no practical use" and taken care of them for you. In LSL you do not have the luxury of an optimizing compiler, and you don't even have the option of dropping into ASM to optimize by hand. The best you can do is cut corners to make the compiler give you something reasonable.

I'm also pretty sure this thread was prompted by talk of an optimizing LSL->LSL compiler, which is something where all of these little tricks would be reasonable.
Strife Onizuka
Moonchild
Join date: 3 Mar 2004
Posts: 5,887
02-18-2006 12:31
From: Masakazu Kojima
I'm also pretty sure this thread was prompted by talk of an optimizing LSL->LSL compiler, which is something where all of these little tricks would be reasonable.


Shoot, you found me out.

So you (group) complain it's not readable, why do you think i said it was one of the 3 head of Cerberus? I have no sympathy.

Zip File above updated. I took a second pass at the script and optimized a few parts i had ignored during the first pass.
Before: 7080
After: 6059 (and i wasted 12 on readability :P)
Diff: 1021 (14.4%) or just over 1/7 of the script.
_____________________
Truth is a river that is always splitting up into arms that reunite. Islanded between the arms, the inhabitants argue for a lifetime as to which is the main river.
- Cyril Connolly

Without the political will to find common ground, the continual friction of tactic and counter tactic, only creates suspicion and hatred and vengeance, and perpetuates the cycle of violence.
- James Nachtwey
Folco Boffin
Mad Moo Cow Cultist
Join date: 27 Feb 2005
Posts: 66
02-18-2006 13:58
From: Strife Onizuka
Shoot, you found me out.

So you (group) complain it's not readable, why do you think i said it was one of the 3 head of Cerberus? I have no sympathy.

Zip File above updated. I took a second pass at the script and optimized a few parts i had ignored during the first pass.
Before: 7080
After: 6059 (and i wasted 12 on readability :P)
Diff: 1021 (14.4%) or just over 1/7 of the script.

Just wanted to say I knew that the other thread produced this one. After all, I was the one that requested this thread. ^_^
I've not posted until now, but I've been reading it almost as soon as someone posts to it. While yes I agree that optimizing can cause readability issues, seeing how I program my LSL offline and run it through a c++ pre-processor to get the ability to have #include's and #define's and /* style comments. Then run ith through a white-space stripper, and then run the generated LSL through lslint to catch my missing ;'s among other errors.
Yes it's a lot of un-neccisary steps, yes it leads to LSL files with no comments, and yes I don't share these files with other people and expect them to understand what they do right off the bat without sitting down and looking at it. But then again, if I wanted them to see how to do it, I'd give them a copy of the .esl file with all the comments still in tact. (If you can call my usual commenting style commenting *lol* I usually don't comment anything out until I'm done. Get burnt sometimes as I gotta figure out just wtf I did, but I ususally remember without too much trouble as long as I don't take a half year break.)

But what I mostly wanted was to be able to get my scripts to run faster, and be smaller if possible. While I won't go as crazy as Strife with some of my reductions, some of the simpler ones I will. Such as <1.0,1.0,1.0> instead of <1,1,1> I didn't know that it actually took up more memory by having LSL convert it to a float on it's own. Nor did I know using vector zv = ZERO_VECTOR would run faster.

One of my current projects needs to run as close to real time as I can get, so I was really intrested in ways to speed up my code and save some bytes here and there while I'm at it.

And I hope Strife continues to go and demonstrate more ways to speed up code. Though hopefully we'll end up with Mono within the next 3 hours and not have to worry as much. Though I'd still like my code to be as fast and memory efficiant as possible even then.
_____________________
^-^

Signed,
Gorgarath,
Whom in this game called Second Life,
plays the avatar Folco Boffin,
and in this game called First Life,
plays the avatar John McDonnell.
Argent Stonecutter
Emergency Mustelid
Join date: 20 Sep 2005
Posts: 20,263
02-18-2006 14:10
From: Strife Onizuka
I factored the constant part of it out. The dynamic portion stayed, so in my mind it is a decent optimization. Pretty self explanitory compaired to the multiliner.
Strife: does Linden Labs guarantee the order of evaluation of function arguments? When they recompile your code for mono, will the meaning remain the same?

You wrote: offsetRot((torus_position += ...)..., torus_position, ...);.

If they perform straightforward code generation, the first two torus_position values will be the same. If they optimise it, unless the order of evaluation of arguments is defined as an invariant, they may evaluate the second argument before the first.

Also, I don't see how performing that assignment inside the function rather than before it actually saves anything.
Strife Onizuka
Moonchild
Join date: 3 Mar 2004
Posts: 5,887
02-19-2006 00:50
how functions work:
CODE

format:<return>0x63 0x5b <paramaters> 0x66 <offset> 0x5c <move>0x7011 0x08 <funciton call><func>
<return> is an optional byte, it reserves the space in the stack for the return.
0x63 reserve 4 bytes - possibly a counter for the stack?
0x5b copy something into previous 4 bytes? maybe null the bytes?
<paramaters> commands needed to generate parramaters in order, directly into the stack.
//these parts have to do with moving the stack pointer.
//the bytecode in this section are used nowhere else in LSL.
0x66 not sure
<offset> space needed by the function (aka it's memory requirements, get the value from the function table, LL functions use 0x00000000)
0x5c not sure
<move> sizeof(<paramater>) + <offset>
0x7011 pop two integers, add them, push result into stack
0x08
<funciton call> either bytecode for LL or User function lookup
<func> function index, either word for LL function or dword for user function


The paramaters are pushed into the stack and left there in order.

when your function returns, it uses the local variable copy codes, to a negitive address.
Which is a total waste, as that variable could be used by the user.

i'm confident that LL has kept the order of operations the same with function calls.
_____________________
Truth is a river that is always splitting up into arms that reunite. Islanded between the arms, the inhabitants argue for a lifetime as to which is the main river.
- Cyril Connolly

Without the political will to find common ground, the continual friction of tactic and counter tactic, only creates suspicion and hatred and vengeance, and perpetuates the cycle of violence.
- James Nachtwey
1 2 3 4