primerib1 Posted January 14, 2023 Share Posted January 14, 2023 (edited) 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 January 15, 2023 by primerib1 1 Link to comment Share on other sites More sharing options...
Rolig Loon Posted January 14, 2023 Share Posted January 14, 2023 I vaguely remember when that note was added (or maybe I remember when I stumbled on it years ago), and I recall a similar discussion at the time. I haven't used some_list != [] since then. 1 Link to comment Share on other sites More sharing options...
Love Zhaoying Posted January 14, 2023 Share Posted January 14, 2023 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 More sharing options...
primerib1 Posted January 14, 2023 Author Share Posted January 14, 2023 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. 1 Link to comment Share on other sites More sharing options...
Quistess Alpha Posted January 14, 2023 Share Posted January 14, 2023 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. 1 Link to comment Share on other sites More sharing options...
primerib1 Posted January 14, 2023 Author Share Posted January 14, 2023 36 minutes ago, Quistess Alpha said: IIRC mono is abysmal at creating lists from scratch. try (some_list != NULL_LIST) where NULL_LIST=[] is a global variable. Interesting. Might be worth a try Link to comment Share on other sites More sharing options...
primerib1 Posted January 15, 2023 Author Share Posted January 15, 2023 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. 1 Link to comment Share on other sites More sharing options...
Quistess Alpha Posted January 15, 2023 Share Posted January 15, 2023 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 More sharing options...
primerib1 Posted January 15, 2023 Author Share Posted January 15, 2023 (edited) 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 January 15, 2023 by primerib1 1 1 Link to comment Share on other sites More sharing options...
Love Zhaoying Posted January 15, 2023 Share Posted January 15, 2023 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"! 1 Link to comment Share on other sites More sharing options...
primerib1 Posted January 15, 2023 Author Share Posted January 15, 2023 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 😛 1 Link to comment Share on other sites More sharing options...
Love Zhaoying Posted January 15, 2023 Share Posted January 15, 2023 (edited) 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 January 15, 2023 by Love Zhaoying Link to comment Share on other sites More sharing options...
primerib1 Posted January 15, 2023 Author Share Posted January 15, 2023 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 More sharing options...
Love Zhaoying Posted January 15, 2023 Share Posted January 15, 2023 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 More sharing options...
Xiija Posted January 15, 2023 Share Posted January 15, 2023 (edited) @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 January 15, 2023 by Xiija Link to comment Share on other sites More sharing options...
Lucia Nightfire Posted January 15, 2023 Share Posted January 15, 2023 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. 1 Link to comment Share on other sites More sharing options...
VenKellie Posted January 15, 2023 Share Posted January 15, 2023 i use llGetListLength in a for loop all the time. seems to be fast and accurate. for (i = 0; i < llGetListLength(jsonlist); i++) Then just use i to get the string in the list like json string json = llList2String(jsonlist, i); Link to comment Share on other sites More sharing options...
primerib1 Posted January 16, 2023 Author Share Posted January 16, 2023 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 More sharing options...
Quistess Alpha Posted January 16, 2023 Share Posted January 16, 2023 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 More sharing options...
primerib1 Posted January 16, 2023 Author Share Posted January 16, 2023 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. 1 Link to comment Share on other sites More sharing options...
Lucia Nightfire Posted January 16, 2023 Share Posted January 16, 2023 (edited) 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 January 16, 2023 by Lucia Nightfire Link to comment Share on other sites More sharing options...
primerib1 Posted January 16, 2023 Author Share Posted January 16, 2023 2 hours ago, Lucia Nightfire said: I mean script time does not advance until a minimum of one server frame has advanced. Ahh, thanks for the explanation! I'll do another test later today using the llGetTimestamp() 1 Link to comment Share on other sites More sharing options...
Frionil Fang Posted January 16, 2023 Share Posted January 16, 2023 (edited) 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 January 16, 2023 by Frionil Fang accuracy 2 Link to comment Share on other sites More sharing options...
Love Zhaoying Posted January 16, 2023 Share Posted January 16, 2023 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"? Link to comment Share on other sites More sharing options...
Quistess Alpha Posted January 16, 2023 Share Posted January 16, 2023 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()? 2 Link to comment Share on other sites More sharing options...
Recommended Posts
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