Jump to content

In Mono, is llGetListLength() actually 100% faster than (some_list != []) ?


primerib1
 Share

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

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

Recommended Posts

I'm reading the "LSL Hacks" page here: https://wiki.secondlife.com/wiki/LSL_Hacks#llGetListLength.28myList.29_and_.28myList_.21.3D_.5B.5D.29

There's a statement:

Quote

Mono's llGetListLength function has been optimized and is about 100% faster than list!=[];

Is that true? So if I don't really care for the 30-byte saving (as claimed in the llGetListLength page), then I'm much better off using llGetListLength() instead of (some_list != []) ?

 

UPDATE: The answer.

 

 

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

2 minutes ago, primerib1 said:
Quote

Mono's llGetListLength function has been optimized and is about 100% faster than list!=[];

Is that true? So if I don't really care for the 30-byte saving (as claimed in the llGetListLength page), then I'm much better off using llGetListLength() instead of (some_list != []) ?

   It will be interesting to hear the answer.  If true, it will be nice to retire another "hack".  I avoid all "hacks" at this point.  Just a habit.  Remember the hack where you clear the list before setting / appending to it, to save memory? (Or was it a string?)

 

Link to comment
Share on other sites

Okay so I put this into a script into a fresh prim:

list Li;

default
{
    state_entry()
    {
        integer limit = llGetMemoryLimit();
        llSetMemoryLimit(limit-1);
        llSleep(1);
        llSetMemoryLimit(limit+1);
        llSay(0, "Hello, Avatar!");
    }

    touch_start(integer total_number)
    {
        llOwnerSay("Touched!");
        Li = [];
        integer i;
        for (i=0;i<3000;++i) Li += i;
        float start;
        float end;
        llOwnerSay("Testing...");
        integer c = 0;
        start = llGetTime();
        for (i=0;i<llGetListLength(Li);++i)
            c += llGetListLength(Li);
        end = llGetTime();
        llOwnerSay("llGLL = " + (string)(end - start));
        start = llGetTime();
        for (i=0;i<(Li != []);++i)
            c += (Li != []);
        end = llGetTime();
        llOwnerSay("!=[] = " + (string)(end - start));
        Li = [];
    }
}

and touched the prim 10x, got the following result:

[12:07] Object: : llGLL = 0.068285
[12:07] Object: : !=[] = 0.179226
[12:08] Object: : llGLL = 0.021732
[12:08] Object: : !=[] = 0.200405
[12:08] Object: : llGLL = 0.044613
[12:08] Object: : !=[] = 0.179665
[12:08] Object: : llGLL = 0.023026
[12:08] Object: : !=[] = 0.179146
[12:09] Object: : llGLL = 0.045074
[12:09] Object: : !=[] = 0.221535
[12:09] Object: : llGLL = 0.069214
[12:09] Object: : !=[] = 0.154846
[12:11] Object: : llGLL = 0.065170
[12:11] Object: : !=[] = 0.221985
[12:12] Object: : llGLL = 0.021912
[12:12] Object: : !=[] = 0.223358
[12:13] Object: : llGLL = 0.020721
[12:13] Object: : !=[] = 0.155853
[12:13] Object: : llGLL = 0.044342
[12:13] Object: : !=[] = 0.135681

It seems llGetListLength() is indeed much faster than (some_list != []) when running on Mono.

 

  • Haha 1
Link to comment
Share on other sites

15 minutes ago, primerib1 said:

It seems llGetListLength() is indeed much faster than (some_list != []) when running on Mono.

IIRC mono is abysmal at creating lists from scratch. try (some_list != NULL_LIST) where NULL_LIST=[] is a global variable.

  • Like 1
Link to comment
Share on other sites

UPDATE

So I created a global var containing an empty list, and perform the comparison as such:

for (i=0;i<(Li != EMPTYL);++i)
    c += (Li != EMPTYL);

and the result is drastically different!

[22:46] Object: : llGLL = 0.021755
[22:46] Object: : !=[] = 0.023201
[22:46] Object: : llGLL = 0.090172
[22:46] Object: : !=[] = 0.021141
[22:46] Object: : llGLL = 0.043770
[22:46] Object: : !=[] = 0.024620
[22:47] Object: : llGLL = 0.090179
[22:47] Object: : !=[] = 0.044090
[22:47] Object: : llGLL = 0.068314
[22:47] Object: : !=[] = 0.022766
[22:47] Object: : llGLL = 0.043884
[22:47] Object: : !=[] = 0.019699
[22:48] Object: : llGLL = 0.066299
[22:48] Object: : !=[] = 0.022705
[22:48] Object: : llGLL = 0.020813
[22:48] Object: : !=[] = 0.023819
[22:49] Object: : llGLL = 0.065613
[22:49] Object: : !=[] = 0.022949
[22:49] Object: : llGLL = 0.022476
[22:49] Object: : !=[] = 0.020981

(some_list != EMPTYLIST) is consistently faster, or as fast as llGetListLength()

Indeed, the Mono overhead of static list creation seems to be quite significant.

  • Like 1
Link to comment
Share on other sites

16 hours ago, primerib1 said:

start = llGetTime();

...

end = llGetTime();

llOwnerSay("llGLL = " + (string)(end - start));

For what it's worth, you can use llResetTime() or even llGetAndResetTime() to save the 2 variable overhead, at the expense of hypothetically messing with another portion of your script somewhere that depends on llGetTime();

Link to comment
Share on other sites

24 minutes ago, Quistess Alpha said:

For what it's worth, you can use llResetTime() or even llGetAndResetTime() to save the 2 variable overhead, at the expense of hypothetically messing with another portion of your script somewhere that depends on llGetTime();

I actually used that and for unknown reasons sometimes I get 0.0000 for the timing.

It's like sometimes llResetTime() got delayed and when I asked llGetTime() the runtime panicked and just reset at that exact moment 😂

That's why I just grab llGetTime() repeatedly in the tests.

Edited by primerib1
  • Like 1
  • Thanks 1
Link to comment
Share on other sites

26 minutes ago, Quistess Alpha said:

For what it's worth, you can use llResetTime() or even llGetAndResetTime() to save the 2 variable overhead, at the expense of hypothetically messing with another portion of your script somewhere that depends on llGetTime();

In my current profiling, I wasn't using llResetTime at all, since I am doing multiple levels of timing (can't reset at an inner level, or the outer level loses its time).

However, I noticed that my "initial" first-time log has like "400s" so..I need to at least reset the time when I first start "flip the profiling flag"!

  • Like 1
Link to comment
Share on other sites

2 hours ago, Love Zhaoying said:

However, I noticed that my "initial" first-time log has like "400s" so..I need to at least reset the time when I first start "flip the profiling flag"!

This is why I usually throw away the first data I have and then iterate 10 times afterwards.

Lotsa magic behind the scenes happening for the first data point, usually 😛

  • Like 1
Link to comment
Share on other sites

5 minutes ago, primerib1 said:
2 hours ago, Love Zhaoying said:

However, I noticed that my "initial" first-time log has like "400s" so..I need to at least reset the time when I first start "flip the profiling flag"!

This is why I usually throw away the first data I have and then iterate 10 times afterwards.

Lotsa magic behind the scenes happening for the first data point, usually 😛

Yeah, my first data point is literally when I "turn on profiling". All that does is set a global.

I'm seeing less and less magic lately - just don't look behind the curtain!

Ref. song, "Who's Behind the Door", by "Zebra"..

Edited by Love Zhaoying
Link to comment
Share on other sites

Okay so to summarize the answer to the question, which is the title of this thread:

YES, BUT...

YES llGetListLength(some_list) is indeed faster than (somelist != []), especially if the latter gets called repeatedly ...

BUT if we don't use a literal empty list, and rather store the empty list in a global variable, and do the inequality comparison to that global variable (e.g., some_list != EMPTYL), then using inequality comparison to get the list length is still a bit faster than calling llGetListLength().

 

Link to comment
Share on other sites

1 minute ago, primerib1 said:

Okay so to summarize the answer to the question, which is the title of this thread:

YES, BUT...

YES llGetListLength(some_list) is indeed faster than (somelist != []), especially if the latter gets called repeatedly ...

BUT if we don't use a literal empty list, and rather store the empty list in a global variable, and do the inequality comparison to that global variable (e.g., some_list != EMPTYL), then using inequality comparison to get the list length is still a bit faster than calling llGetListLength().

 

I kept missing it, but were you also asking the similar question about "list1 != list2"? 

That compares lengths - exactly the same as "list1 != []".  

I guess it is the same question, really. Maybe with a different answer given your BUT above?

 

Link to comment
Share on other sites

@primerib1

i tried your code with a few changes, and it was faster? ... hmm

...

list Li;
list empty;
default
{
    state_entry()
    {
        integer limit = llGetMemoryLimit();
        llSetMemoryLimit(limit-1);
        llSleep(1);
        llSetMemoryLimit(limit+1);
        llSay(0, "Hello, Avatar!");
    }

    touch_start(integer total_number)
    {  list x;                        // change here
        llOwnerSay("Touched!");
        Li = [];
        integer i;
        for (i=0;i<3000;++i) Li += i;
        float start;
        float end;
        llOwnerSay("Testing...");
        integer c = 0;
        start = llGetTime();
        for (i=0;i<llGetListLength(Li);++i)
            c += llGetListLength(Li);
        end = llGetTime();
        llOwnerSay("llGLL = " + (string)(end - start));
        start = llGetTime();
        for (i=0;i<(Li !=  x);++i) // change here
            c += (Li != x);       // change here
        end = llGetTime();
        llOwnerSay("!=[] = " + (string)(end - start));
        Li = [];
    }
}

 

Edited by Xiija
Link to comment
Share on other sites

9 hours ago, Lucia Nightfire said:

llGetTime() should never be used with subsecond precision reliant speed testing as it is server frame advance dependent.

You should be using llGetTimestamp() instead as it is not server frame advance dependent.

I don't understand... what do you mean "server frame advance" here?

I only go by the spec of llGetTime:

Quote

Script time matches normal time, it is unaffected by time dilation. For example, if you call llResetTime on two objects in separate simulators at the same time, and later call llGetTime on both at the same time, their values will be equal regardless of differences in dilation.

 

Link to comment
Share on other sites

19 minutes ago, primerib1 said:

what do you mean "server frame advance" here?

To give the illusion of running thousands of scripts on a region that has a few orders of magnitude fewer processors, scripts are constantly stopped from running to give time for all the other scripts in the region time to run. the time it takes from when your script is stopped to when it starts running again after everyone else has had their turn is (I believe/assume) about 0.02 seconds, which is why llGLL reports roughly 0.02, 0.04 or 0.06 seconds, and not 0.01, 0.03 or 0.05. I've not considered or tried using llGetTimestamp() to measure small intervals so I can't comment on it being more or less accurate.

Link to comment
Share on other sites

46 minutes ago, Quistess Alpha said:

To give the illusion of running thousands of scripts on a region that has a few orders of magnitude fewer processors, scripts are constantly stopped from running to give time for all the other scripts in the region time to run. the time it takes from when your script is stopped to when it starts running again after everyone else has had their turn is (I believe/assume) about 0.02 seconds, which is why llGLL reports roughly 0.02, 0.04 or 0.06 seconds, and not 0.01, 0.03 or 0.05. I've not considered or tried using llGetTimestamp() to measure small intervals so I can't comment on it being more or less accurate.

Isn't that actually more accurate to reflect the actual time used by the script?

I mean, if the SIM suspends the script temporarily for whatever reason, I don't want the time spent in suspension to be counted in the time taken to perform a certain task.

So, by that logic, llGetTime() should in fact be ideal as it measures only the elapsed time of the script itself while running.

  • Thanks 1
Link to comment
Share on other sites

1 hour ago, primerib1 said:

I don't understand... what do you mean "server frame advance" here?

I mean script time does not advance until a minimum of one server frame has advanced.

Display_Bytes_Check(string s)
{
    integer bytes = ((3 * llSubStringIndex(llStringToBase64(s) + "=","=")) >> 2);
    if ((display_bytes + bytes) < 1024)
    {
        display += s;
        display_bytes += bytes;
    }
    else
    {
        llOwnerSay(display);
        display = s;
        display_bytes = bytes;
    }
}
string display;
integer display_bytes;
default
{
    touch_end(integer i)
    {
        if (llDetectedKey(0) == llGetOwner())
        {
            llResetTime();
            for (i = -50; i < 0; i++)
            {
                display += "\n" + (string)llGetEnv("frame_number") + " | " + (string)llGetTime() + " | " + llGetTimestamp();
            }
            list T = llParseString2List(display,["\n"],[]);
            display_bytes = ((3 * llSubStringIndex(llStringToBase64(display = "Test A\nFrame Number | llGetTime() | llGetTimestamp()") + "=","=")) >> 2);
            for (i = -llGetListLength(T); i < 0; i++)
            {
                Display_Bytes_Check("\n" + llList2String(T,i));
            }
            Display_Bytes_Check("\nFree Memory: " + (string)llGetFreeMemory());
            llOwnerSay(display);
            llResetScript();
        }
    }
}

Drop the above script into a box a click and notice repeated calls to llGetTime() within the same frame number return the same value whereas timestamp is unaffected.

Edited by Lucia Nightfire
Link to comment
Share on other sites

6 hours ago, primerib1 said:

Ahh, thanks for the explanation!

I'll do another test later today using the llGetTimestamp()

This won't help. I checked llGetTimestamp on a fast operation and the results are again constrained to multiples of server frames, sometimes reporting an operation taking 8 microseconds (= completed on same frame), sometimes 44 milliseconds (completed after 2 frames).

The more accurate method is to repeat the test a massive number of times until the total elapsed time between the start and end of timing hits... well, as high as you're comfortable waiting, more is better. Depending on what you're resting, it might take thousands to even millions of loops, so put that for(tests = 0; tests < 10000; ++tests) to good use. Done this way, any timing you get from either using llGetTime()/llResetTime() or calculating from llGetTimestamp() are just about identical.

Edited by Frionil Fang
accuracy
  • Like 2
Link to comment
Share on other sites

1 hour ago, Love Zhaoying said:

Well, doesn't the method needed for recording time depend on whether you want "actual elapsed time" or "elapsed time that was allotted to the script"?

I don't believe "elapsed time that was allotted to the script" is something we have access to, which is the problem. maybe someone could file a Jira to extend llScriptProfiler()?

  • Like 2
Link to comment
Share on other sites

You are about to reply to a thread that has been inactive for 628 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...