Jump to content

Code Refactoring, and IIF() - Why not?


Love Zhaoying
 Share

You are about to reply to a thread that has been inactive for 701 days.

Please take a moment to consider if this thread is worth bumping.

Recommended Posts

As far as performance goes... I've been coding a 6502 processor emulator in LSL and if anything, it has taught me that a lot of common logic of keeping your code clean is detrimental both to performance and memory use.

Don't use: functions, neither user defined or built-in, if you can avoid it; lists; globals (defining "constants" is ineffecient, even if not as dramatically so as functions -- inline every numeric value).

Do use: integers; addition, subtraction and bit operations; hardcoded magic values; jumps and labels instead of function calls.

Yes, that'll be a mess, but LSL does live in its own strange constrained box so if you absolutely must squeeze every bit of performance and memory out of it, it's going to look more like glorified assembly than a normal script.

And hey the emulator runs at performance rivaling a ~400 Hz 6502 CPU... can't quite hit the typical 1 MHz mark, but it's something.

  • Thanks 1
Link to comment
Share on other sites

i used your test harness Qie to test 'preline' code vs inline code

'preline' meaning assign a default value so to only do an IF and not an IF ELSE

result = FALSE;
if (true)
  result = TRUE;
result = TRUE;
if (false)
  result = FALSE;

i ran the test in 14 sets. 7 on Lewis and 7 on Plum

each set was 20 reps of 100,000. 2,000,000. Alternating the order within each rep

resetting the script between each set

data below

is pretty marginal the difference but 'preline' came out in front 11 times vs 3 times for inline

so pretty much we can confirm what we already thought. If we after performance then is no substitute for copious lines of inline code. And seems like 'prelining' can give some tiny bit more performance over the long run

 

 

Lewis region
work: Totes times:
    16.866480 : inline conditionals     
    16.758780 : preline conditionals (p)
work: Totes times:
    16.978610 : inline conditionals
    16.735420 : preline conditionals (p)
work: Totes times:
    16.068230 : inline conditionals   
    16.000920 : preline conditionals (p)
work: Totes times:
    17.401600 : inline conditionals
    17.158410 : preline conditionals (p)
work: Totes times:
    16.423780 : inline conditionals
    15.900300 : preline conditionals (p)
work: Totes times:
    16.888160 : inline conditionals       (i)
    17.113820 : preline conditionals
work: Totes times:
    16.911920 : inline conditionals
    16.623720 : preline conditionals (p)
Plum region
work: Totes times:
    16.404080 : inline conditionals
    16.154130 : preline conditionals (p)
work: Totes times:
    17.001260 : inline conditionals       (i)
    17.334990 : preline conditionals
work: Totes times:
    16.446620 : inline conditionals       (i)
    16.600650 : preline conditionals
work: Totes times:
    15.578240 : inline conditionals
    15.357420 : preline conditionals (p)
work: Totes times:
    17.160240 : inline conditionals
    16.642140 : preline conditionals (p)
work: Totes times:
    16.090590 : inline conditionals
    15.512880 : preline conditionals (p)
work: Totes times:
    16.578000 : inline conditionals
    16.045150 : preline conditionals (p)

  • Thanks 2
Link to comment
Share on other sites

4 hours ago, Love Zhaoying said:

"are we sure the operands are always evaluated left-to-right"

In my limited experience LSL evaluates right-to-left when given the choice. I had a nasty time once trying to debug something that came down to

integer i;
llSay(llList2CSV([++i,++i,++i,++i]));
// output: 4,3,2,1

(it was less obvious in context, not reduced to the minimum example)

ETA: above code actually outputs 1,2,3,4 whereas  (string)(++i)+(string)(++i)+(string)(++i)+(string)(++i); is "4321"

Edited by Quistess Alpha
  • Like 1
  • Thanks 2
Link to comment
Share on other sites

1 minute ago, Quistess Alpha said:

In my limited experience LSL evaluates right-to-left when given the choice. I had a nasty time once trying to debug something that came down to

integer i;
llSay(llList2CSV([++i,++i,++i,++i]));
// output: 4,3,2,1

(it was less obvious in context, not reduced to the minimum example)

Do you think that my example code works then (left-to-right) only because I am declaring (and assigning) the variable in the left-most operand?

Link to comment
Share on other sites

21 minutes ago, Love Zhaoying said:

my example code

could you be a bit more specific? there was a lot of code thrown around and at a glance reading back I'm not seeing any examples of equal precedence operations being evaluated. more often than not, things work out in the way you'd expect because operations are either associative(evaluation order doesn't matter), or because operator evaluation order is fairly sane, I'd expect assignment and unary operations to get evaluated first.

Edited by Quistess Alpha
  • Thanks 1
Link to comment
Share on other sites

2 hours ago, Qie Niangao said:

And the list was substantially worse than I expected, especially for constant two-element lists. (Now I'm wondering how lame this compiler could be, and whether those lists could possibly be cons'd-up each time the expression is encountered at runtime.)

Ask a stupid question, get the answer one dreads:

integer IIF(integer condition, integer truevalue, integer falsevalue)
{
    if (condition)
        return truevalue;
    else
        return falsevalue;
}

integer REPS = 10000;

default
{
    state_entry()
    {
        integer true = TRUE; // superstitious attempt to prevent an optimization?
        integer false = FALSE;
        integer result;
        float ignoreTime;
        float totalIIFtime;
        float totalListTime;
        float totalListvarTime;
        float totalInlineTime;
        integer countdown;
        list trueFalse = [TRUE, FALSE];
        
        ignoreTime += llGetAndResetTime();   // for consistency?
        for (countdown = REPS; countdown; --countdown)
        {
           result = IIF(true, TRUE, FALSE);
           result = IIF(false, TRUE, FALSE);
        }
        totalIIFtime += llGetAndResetTime();
        for (countdown = REPS; countdown; --countdown)
        {
           result = llList2Integer([TRUE, FALSE], true);
           result = llList2Integer([TRUE, FALSE], false);
        }
        totalListTime += llGetAndResetTime();
        for (countdown = REPS; countdown; --countdown)
        {
           result = llList2Integer(trueFalse, true);
           result = llList2Integer(trueFalse, false);
        }
        totalListvarTime += llGetAndResetTime();
        for (countdown = REPS; countdown; --countdown)
        {
           if (true)
                result = TRUE;
            else
                result = FALSE;
            if (false)
                result = TRUE;
            else
                result = FALSE;
        }
        totalInlineTime += llGetAndResetTime();
        llOwnerSay("Total times:\n\t"
            +(string)totalIIFtime+" : IIF (user function call)\n\t"
            +(string)totalListTime+" : llList2Integer indexing\n\t"
            +(string)totalListvarTime+" : llList2Integer indexing of constant-valued list variable\n\t"
            +(string)totalInlineTime+" : inline conditionals");
    }
}


[10:39] Object: Total times:
    0.224018 : IIF (user function call)
    2.065748 : llList2Integer indexing
    0.178834 : llList2Integer indexing of constant-valued list variable
    0.021195 : inline conditionals

So yeah, it must be consing-up those constant lists at runtime, every time they're encountered. (And here I'd been eschewing variable references, duh.) So I guess using the llList2Integer function is roughly the same cost as the IIF() call, but god help us if we inline the constant list values.

  • Like 2
Link to comment
Share on other sites

20 minutes ago, Quistess Alpha said:

could you be a bit more specific? there was a lot of code thrown around and at a glance reading back I'm not seeing any examples of equal precedence operations being evaluated. more often than not, things work out in the way you'd expect because operations are either associative(evaluation order doesn't matter), or because operator evaluation order is fairly sane, I'd expect assignment and unary operations to get evaluated first.

Sure!  Oops, I did not declare it in the first parameter, just assigned it.

// The next line surprised me - it works!                
        SetThing(sThingName = GetThingName(),  GetThingValue(sThingName) + 1*factor );

 

Link to comment
Share on other sites

7 minutes ago, Qie Niangao said:

So yeah, it must be consing-up those constant lists at runtime, every time they're encountered. (And here I'd been eschewing variable references, duh.) So I guess using the llList2Integer function is roughly the same cost as the IIF() call, but god help us if we inline the constant list values.

Variables are our friends! 

Too bad we don't have "true" constants..you know, "const" keyword or "#define". Not that it would apply to lists. /me eyerolls

  • Like 1
Link to comment
Share on other sites

So..because of the nature of things, I propose (without any data of course) that the IIF() scenario probably also uses less memory than the list scenario(s).

Maybe if I beat someone else to it (and take a break from my RL programming job later), I can check this myself..*hint*

Y'all spoil us! Me likey!

Link to comment
Share on other sites

6 hours ago, Wulfie Reanimator said:

This "IIF" function is better known as a ternary. The common syntax is:

condition ? true : false

It's definitely a useful tool, but it can also make code harder to read, especially once you start nesting them.

I see the problem, you should never let your ternaries nest, or they multiply.

"Have your ternaries spayed and neutered today!"

Link to comment
Share on other sites

1 hour ago, Quistess Alpha said:
4 hours ago, Love Zhaoying said:

"are we sure the operands are always evaluated left-to-right"

In my limited experience LSL evaluates right-to-left when given the choice.

Let me just dust off this wiki page from... 2010. https://wiki.secondlife.com/wiki/Category:LSL_Operators

Basically "Does this operator rely on what's on the right? Then they're processed right-to-left." So assignment, typecasting, and unary-operators. Everything else goes left-to-right.

Edited by Wulfie Reanimator
  • Thanks 1
Link to comment
Share on other sites

4 minutes ago, Wulfie Reanimator said:

Let me just dust off this wiki page from... 2010. https://wiki.secondlife.com/wiki/Category:LSL_Operators

Oh dear. . .

integer i;
llSay(0, llList2CSV([++i,++i,++i,++i]));
// output: 1,2,3,4
i=0;
llSay(0, (string)(++i)+(string)(++i)+(string)(++i)+(string)(++i));
// output: 4321

That's what I get for writing out an example off the top of my head without checking in-world.

  • Like 2
Link to comment
Share on other sites

1 hour ago, Qie Niangao said:

So yeah, it must be consing-up those constant lists at runtime, every time they're encountered. (And here I'd been eschewing variable references, duh.) So I guess using the llList2Integer function is roughly the same cost as the IIF() call, but god help us if we inline the constant list values.

Huzzah, my old code isn't doomed!

  • Like 1
Link to comment
Share on other sites

15 minutes ago, Wulfie Reanimator said:

Let me just dust off this wiki page from... 2010. https://wiki.secondlife.com/wiki/Category:LSL_Operators

Basically "Does this operator rely on what's on the right? Then they're processed right-to-left." So assignment, typecasting, and unary-operators. Everything else goes left-to-right.

That's diabolical! And works in my favor!

Link to comment
Share on other sites

is a pretty significant difference between const and var lists

so I will be paying attention to this with my list work going forward


i thought to level up IIF testing to deal with a more complex case. So I compared this
v = llList2Integer([a, b, c, d, e], (v > a) + (v > b) + (v > c) + (v > d));

in the more complex case, when our IIF breaks on condition successful then is pretty competitive with inlining. So altogether I think there is some merit in IIF depending on use case, code clarity also being a factor in the choice

running it 6 times at Sea of Fables region

[19:09] work: x Total times:
    0.466231 : IIF (user function call)
    0.711628 : llList2Integer indexing of constant-valued list variable
    0.288379 : inline conditionals
[19:09] work: x Total times:
    0.443376 : IIF (user function call)
    0.866850 : llList2Integer indexing of constant-valued list variable
    0.444371 : inline conditionals
[19:09] work: x Total times:
    0.399680 : IIF (user function call)
    1.022209 : llList2Integer indexing of constant-valued list variable
    0.400074 : inline conditionals
[19:09] work: x Total times:
    0.488607 : IIF (user function call)
    0.933127 : llList2Integer indexing of constant-valued list variable
    0.377889 : inline conditionals
[19:09] work: x Total times:
    0.511082 : IIF (user function call)
    0.866693 : llList2Integer indexing of constant-valued list variable
    0.355385 : inline conditionals
[19:09] work: x Total times:
    0.443689 : IIF (user function call)
    0.823224 : llList2Integer indexing of constant-valued list variable
    0.399611 : inline conditionals


if did also look at inlining nested IFs but is slower than IF ELSEIF ELSE, and has to do with the need to assign result in each nest

result = e;
if (condition <= a)
{
  result = a;
  if (condition <= b)
  {
    result = b;
    if (condition <= c)
    {
      result = c;
      if (condition <= d)
        result = d;
    }
  }
}

integer IIF(integer condition, integer a, integer b, integer c,integer d)
{
    if (condition <= a)
        return a;
    if (condition <= b)
        return b;
    if (condition <= c)
        return c;
    if (condition <= d)
        return d;
    return condition;
}

integer REPS = 10000;


default
{
    state_entry()
    {
        integer true = TRUE; // superstitious attempt to prevent an optimization?
        integer false = FALSE;
        integer result;
        float ignoreTime;
        float totalIIFtime;
        float totalListTime;
        float totalListvarTime;
        float totalInlineTime;
        integer countdown;
        
        integer condition;
        integer length = 4;
        integer a = 0; integer b = 1; integer c = 2; integer d = 3; integer e = 4;
        list trueFalse = [a, b, c, d];
        
        ignoreTime += llGetAndResetTime();   // for consistency?
        for (countdown = REPS; countdown; --countdown)
        {
           for (condition = 0; condition < length; ++condition)
           {
              result = e;
              result = IIF(condition, a, b, c, d);
           }
        }
        totalIIFtime += llGetAndResetTime();
        for (countdown = REPS; countdown; --countdown)
        {
           for (condition = 0; condition < length; ++condition)
           {
              result = e;         
              result = llList2Integer([a, b, c, d],
                 (condition > a) + (condition > b) + (condition > c) + (condition > d)
              );
           }      
        }
        totalListTime += llGetAndResetTime();
        for (countdown = REPS; countdown; --countdown)
        {
           for (condition = 0; condition < length; ++condition)
           {
              result = e;
              result = llList2Integer(trueFalse,
                 (condition > a) + (condition > b) + (condition > c) + (condition > d)
              );
           }
        }
        totalListvarTime += llGetAndResetTime();
        for (countdown = REPS; countdown; --countdown)
        {
           for (condition = 0; condition < length; ++condition)
           {
              result = e;
              if (condition <= a)
                 result = a;
              else if (condition <= b)
                 result = b;
              else if (condition <= c)
                 result = c;
              else if (condition <= d)
                 result = d;
           }
        }
        totalInlineTime += llGetAndResetTime();
        llOwnerSay("Total times:\n\t"
            +(string)totalIIFtime+" : IIF (user function call)\n\t"
            +(string)totalListTime+" : llList2Integer indexing\n\t"
            +(string)totalListvarTime+" : llList2Integer indexing of constant-valued list variable\n\t"
            +(string)totalInlineTime+" : inline conditionals");
    }

 

  • Like 1
  • Thanks 1
Link to comment
Share on other sites

oops

the IIF I wrote above is wrong. Should be

integer IIF(integer condition, integer a, integer b, integer c,integer d, integer e)
{
    if (condition <= a)
        return a;
    if (condition <= b)
        return b;
    if (condition <= c)
        return c;
    if (condition <= d)
        return d;
    return e;
}

 

the extra parameter doesn't significantly affect the timing tho. I just post the correction

  • Like 1
Link to comment
Share on other sites

You are about to reply to a thread that has been inactive for 701 days.

Please take a moment to consider if this thread is worth bumping.

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
 Share

×
×
  • Create New...