Tricks for saving string memory
|
|
Yumi Murakami
DoIt!AttachTheEarOfACat!
Join date: 27 Sep 2005
Posts: 6,860
|
01-03-2008 21:27
I know that the following trick works for reducing memory when appending lists or strings:
theStr = (theStr = []) + theStr + thingToAppend;
Can you do any similar thing when clipping out parts of strings? eg,
theStr = llGetSubString( (theStr=[])+theStr, start, end);
Does this work? And does it save any memory?
|
|
Alicia Sautereau
if (!social) hide;
Join date: 20 Feb 2007
Posts: 3,125
|
01-03-2008 21:32
as this is a topic i`ll take to heart now i`ll add a question aswell  is there anyway to remove strings/lists from memory besides resetting?
|
|
Deanna Trollop
BZ Enterprises
Join date: 30 Jan 2006
Posts: 671
|
01-03-2008 21:42
From: Alicia Sautereau is there anyway to remove strings/lists from memory besides resetting? some_string = ""; some_list = [];
|
|
Yumi Murakami
DoIt!AttachTheEarOfACat!
Join date: 27 Sep 2005
Posts: 6,860
|
01-03-2008 21:46
From: Deanna Trollop some_string = ""; some_list = []; There is unfortunately some problem with this though. I've had it told to me by another scripter I trust that the LSL memory space can suffer from (shudder) fragmentation. So if you a) allocate a 10 byte string, b) allocate an int, c) clear the 10 byte string, then d) try to store a 15 byte string, then that space you freed up from the 10 byte string isn't big enough, so it can't be used and the effect is just as if you hadn't freed the 10 bytes 
|
|
Deanna Trollop
BZ Enterprises
Join date: 30 Jan 2006
Posts: 671
|
01-03-2008 21:49
From: Yumi Murakami theStr = llGetSubString( (theStr=[])+theStr, start, end); Does this work? And does it save any memory? It does work and it should save memory, for the same reason. In this case, it passes a copy of theStr to llGetSubString, then (since concatenation operations are evaluated right-to-left) nulls out the value of theStr, so you don't have 2 copies in memory at the same time. This can be used whenever passing a string/list to a function *when the return value of that function will be assigned back to the string/list variable passed to it.*
|
|
Void Singer
Int vSelf = Sing(void);
Join date: 24 Sep 2005
Posts: 6,973
|
01-04-2008 08:17
there seems to be some confusion here though.... and perhaps it's just me but (myString = []) shouldn't compile... you're assigning an empty list to a string, which should result in a type mismatch for strings you use stringname = (stringname=""  + stringname + other stuff; for lists you use listname = (listname=[]) + listname + otherstuff; warning the following does NOT work listname = (listname=[]) + someListFunction( function stuff ); but this should listname = someListFunction( (listname=[]) + listname, other functions stuff ); or maybe it's listname = somelistfunction (listname=[], other function stuff ); I forget =P
_____________________
| | . "Cat-Like Typing Detected" | . This post may contain errors in logic, spelling, and | . grammar known to the SL populace to cause confusion | | - Please Use PHP tags when posting scripts/code, Thanks. | - Can't See PHP or URL Tags Correctly? Check Out This Link... | - 
|
|
Deanna Trollop
BZ Enterprises
Join date: 30 Jan 2006
Posts: 671
|
01-04-2008 09:06
From: Void Singer (myString = []) shouldn't compile It will if myString is a list.  But yeah, implied datatypes aside, I lazily followed the existing example without correcting the crossed syntax. From: someone warning the following does NOT work listname = (listname=[]) + someListFunction( function stuff ); Well, assuming you're passing listname to someListFunction, yes it will, it just won't gain you anything in terms of memory savings. From: someone but this should listname = someListFunction( (listname=[]) + listname, other functions stuff ); Correct. From: someone or maybe it's listname = somelistfunction (listname=[], other function stuff ); Nope. That'll just null out the value of listname and pass that null list to somelistfunction. Depending on what somelistfunction does, it's likely you'll just get back a null list.
|
|
Lear Cale
wordy bugger
Join date: 22 Aug 2007
Posts: 3,569
|
01-04-2008 10:22
From: Yumi Murakami There is unfortunately some problem with this though. I've had it told to me by another scripter I trust that the LSL memory space can suffer from (shudder) fragmentation. So if you a) allocate a 10 byte string, b) allocate an int, c) clear the 10 byte string, then d) try to store a 15 byte string, then that space you freed up from the 10 byte string isn't big enough, so it can't be used and the effect is just as if you hadn't freed the 10 bytes  The surest workaround for fragmentation is to allocate all the permanent stuff first, before the stuff that comes and goes. There are other means in languages other than LSL, using chunks of same-sized items for frequently allocated and freed things. When you have data items and can't predict the lifespan of those items, there's really very little you can do about fragmentation other than to be sure to have plenty of free memory.
|
|
Monica Balut
Beam-Me
Join date: 18 Feb 2007
Posts: 311
|
01-04-2008 12:18
version 1: listname = somelistfunction (listname=[], other function stuff ); someotherfunction(listname, other parameters);
version 2:
someotherfunction(somelistfunction (listname=[], other function stuff ), other parameters);
If you just embedded somelistfunction into the parameter list of someotherfunction as in version 2, does it make a difference using the (listname=[]) part since you are not storing it in the intermediate listname as in version 1? Should you use someotherfunction((listname=[]) + somelistfunction (listname=[], other function stuff ), other parameters);
|
|
Alicia Sautereau
if (!social) hide;
Join date: 20 Feb 2007
Posts: 3,125
|
01-04-2008 13:47
snippet from my other post: global: integer DEBUG; in the code: if (DEBUG) llSay(0, "state :: 1.3"  ; have like 16-18 in the script but with just removing them, memory drops to 10.18kb from 12.58??? any idea what the heck is hogging memory with that simple if statement?
|
|
Meade Paravane
Hedgehog
Join date: 21 Nov 2006
Posts: 4,845
|
01-04-2008 13:53
From: Alicia Sautereau any idea what the heck is hogging memory with that simple if statement? Probably 1/2 of the savings is from having fewer string constants..
_____________________
Tired of shouting clubs and lucky chairs? Vote for llParcelSay!!! - Go here: http://jira.secondlife.com/browse/SVC-1224- If you see "if you were logged in.." on the left, click it and log in - Click the "Vote for it" link on the left
|
|
Alicia Sautereau
if (!social) hide;
Join date: 20 Feb 2007
Posts: 3,125
|
01-04-2008 14:00
From: Meade Paravane Probably 1/2 of the savings is from having fewer string constants.. damn thanks  guess i`ll do the debugging in the proccessing script wich is < 2kb 
|
|
Meade Paravane
Hedgehog
Join date: 21 Nov 2006
Posts: 4,845
|
01-04-2008 14:04
That or use something like this...
Debug (integer a, integer b) { if (DEBUG) llSay (0, "state :: " + (string)a + "." + (string)b); }
...though I haven't tested that and have no idea if it'll net you any real savings. It'll execute a little bit slower - probably shouldn't do this is any tight loops or other things that run frequently.
_____________________
Tired of shouting clubs and lucky chairs? Vote for llParcelSay!!! - Go here: http://jira.secondlife.com/browse/SVC-1224- If you see "if you were logged in.." on the left, click it and log in - Click the "Vote for it" link on the left
|
|
Alicia Sautereau
if (!social) hide;
Join date: 20 Feb 2007
Posts: 3,125
|
01-04-2008 14:14
From: Meade Paravane That or use something like this...
Debug (integer a, integer b) { if (DEBUG) llSay (0, "state :: " + (string)a + "." + (string)b); }
...though I haven't tested that and have no idea if it'll net you any real savings. It'll execute a little bit slower - probably shouldn't do this is any tight loops or other things that run frequently. ah ok i get it, have the if statement declared only once even when inactive and just call apon it by calling it interresting, gonna give that a try right now  edit: it saved abit but as it`s still being called on from: 12.254883 KiB to: 11.930664 KiB exit state: from: 12.533203 KiB to: 12.208984 think the savings comes from reduced code but thanks for the idea 
|
|
Lear Cale
wordy bugger
Join date: 22 Aug 2007
Posts: 3,569
|
01-04-2008 16:27
Not only does Meade's suggestion factor out one "if", it also eliminates all the strings but one. I generally use a debug function that takes a string. This wouldn't save nearly as much, but it makes debug output easier to read. And when I'm done it's easier to comment them out (or just comment out the insides of the debug function, which doesn't help as much).
|
|
Deanna Trollop
BZ Enterprises
Join date: 30 Jan 2006
Posts: 671
|
01-04-2008 22:02
From: Monica Balut version 1: listname = somelistfunction (listname=[], other function stuff ); someotherfunction(listname, other parameters);
version 2:
someotherfunction(somelistfunction (listname=[], other function stuff ), other parameters); First off, as noted above in my reply to Void, this syntax will simply nullify the contents of listname, then pass that null list to somelistfunction. You need a concatenation of a null list assignment and the original list: ( listname = [] ) + listname That said, From: someone If you just embedded somelistfunction into the parameter list of someotherfunction as in version 2, does it make a difference using the (listname=[]) part since you are not storing it in the intermediate listname as in version 1? The usage of this syntax at all depends on what someotherfunction does with the list passed to it. Is it supposed to process the list in some manner, then return another list? somelistfunction is obviously this type. Or does it just process the list data to then manipulate global variables and/or return some other datatype? Someotherfunction might be of this type, but it's unclear, as it might return a list, but here you're simply discarding any return value by not assigning it to a variable. As I also noted above, this syntax is only useful when passing to a function if it will return a list that will be assigned to the passed list variable. Otherwise, all you've done is some processing with a list, and nullified the original. So assuming a case where you have several functions which process a list and return a list, you would use something like: listname = someotherfunction( somelistfunction( ( listname = [] ) + listname, foo, bar ), blah, yadda ); This passes a copy of listname to somelistfunction (along with parameters foo and bar ) and nullifies the value of listname itself, so you aren't holding 2 copies of the list data in memory at the same time. Whatever the return result of somelistfinction is, that is then passed to someotherfunction (along with blah and yadda). There's no need to nullify listname again, since it's already blank. Finally, the return value of someotherfunction is assigned back to the listname variable.
|
|
Alicia Sautereau
if (!social) hide;
Join date: 20 Feb 2007
Posts: 3,125
|
01-04-2008 23:21
i`ll throw something else in
replaced 7 if statements and created 7 states, each for 1 specific query and then jump from the query handler to a needed state
using the same code just placed in it`s own state instead in an if, saved about 1.4kb memory
have seen that so far from GPL scripts (ingame and wiki) that not many seem to like using a large amount of state`s but from redoing my hogger, it saved valuable memory and depending on the situation, in my case, makes using linked messages to skip to commands i need to execute easier
but, are there any drawbacks of having a dozen states that i`m not aware of?
|
|
Hewee Zetkin
Registered User
Join date: 20 Jul 2006
Posts: 2,702
|
01-05-2008 00:48
I'd like to add that the trick being discussed more than likely does not actually "save memory". If myList takes up N bytes, EITHER way you add M bytes to it the end result will be that N+M bytes are used. What the trick does is keep the memory used (actually the memory tracked by whatever form of bookkeeping is done by memory management) from very temporarily jumping to something like 2*N+M bytes before dropping back down to N+M. I only say this because it MAY be you are worrying too much about saving a few bytes. Unless you are dealing with some large strings/lists, or constantly hovering very close to the edge of that 16kB limit, you may not even need the hack. There are several modes of thought where that goes. One is that you should always use the hack, to develop good habbits for when you need it. Another is that you should only sacrifice readability/maintainability when you really have to. I'm not going to tell you one is better than the other. It is a nasty little LSL world we operate in every day. Hold on there and keep praying for Mono. 
|
|
Alicia Sautereau
if (!social) hide;
Join date: 20 Feb 2007
Posts: 3,125
|
01-05-2008 04:28
From: Meade Paravane That or use something like this...
Debug (integer a, integer b) { if (DEBUG) llSay (0, "state :: " + (string)a + "." + (string)b); }
...though I haven't tested that and have no idea if it'll net you any real savings. It'll execute a little bit slower - probably shouldn't do this is any tight loops or other things that run frequently. to come back on this from my previous post, the saving isn`t much but with cleaning the code of redundent if`s and mess, lower mem usage by a tiny bit and pass it on the the proccesser script wich hardly uses any memory comm file: string bug_msg(string a) { if (DEBUG_link) llMessageLinked(LINK_THIS, LINK_PROC, "debug|"+a, usrNow); return a; } bug_msg("state :: 0.1"  ; proccesser link_message: if (list(0) == "debug"  llSay(0, list(1)); thanks for the idea as it helped clean some of the mess 
|
|
Void Singer
Int vSelf = Sing(void);
Join date: 24 Sep 2005
Posts: 6,973
|
01-05-2008 11:44
From: Alicia Sautereau to come back on this from my previous post, the saving isn`t much but with cleaning the code of redundent if`s and mess, lower mem usage by a tiny bit and pass it on the the proccesser script wich hardly uses any memory comm file: string bug_msg(string a) { if (DEBUG_link) llMessageLinked(LINK_THIS, LINK_PROC, "debug|"+a, usrNow); return a; } bug_msg("state :: 0.1"  ; proccesser link_message: if (list(0) == "debug"  llSay(0, list(1)); thanks for the idea as it helped clean some of the mess  lol I just wrote almost exatly what meade wrote on your other thread... the basic principle is to eliminate repeated calls on indivdual if statmenets.... loading a debug message out to another script would likely be more effiecient in your example if you just sent a linked message with your debug info to the other script and let THAT script determine whether or not to say it.... in fact you'd only have to remove the debug script to remove the messages so wouldn't even need to determine an if condition (but you'd be left with all the extra link messages, so not optimal for the final usage as a base rule if you find you are writing any block of code multpile times you should consider making it a function... and any function can be offloaded to a subscript (although with a certain amount of overhead, for messages and flow logic)... offloading can be tricky if you don't get the flow right, functions are not tricky at all because it's the same as inserting the code wherever it's called (with the slight overhead of passing variables in some cases)
_____________________
| | . "Cat-Like Typing Detected" | . This post may contain errors in logic, spelling, and | . grammar known to the SL populace to cause confusion | | - Please Use PHP tags when posting scripts/code, Thanks. | - Can't See PHP or URL Tags Correctly? Check Out This Link... | - 
|
|
Alicia Sautereau
if (!social) hide;
Join date: 20 Feb 2007
Posts: 3,125
|
01-05-2008 12:51
From: Void Singer lol I just wrote almost exatly what meade wrote on your other thread... the basic principle is to eliminate repeated calls on indivdual if statmenets....
loading a debug message out to another script would likely be more effiecient in your example if you just sent a linked message with your debug info to the other script and let THAT script determine whether or not to say it.... in fact you'd only have to remove the debug script to remove the messages so wouldn't even need to determine an if condition (but you'd be left with all the extra link messages, so not optimal for the final usage
as a base rule if you find you are writing any block of code multpile times you should consider making it a function... and any function can be offloaded to a subscript (although with a certain amount of overhead, for messages and flow logic)... offloading can be tricky if you don't get the flow right, functions are not tricky at all because it's the same as inserting the code wherever it's called (with the slight overhead of passing variables in some cases) the pain of having the stuff in 2 threads, no idea who said what lol trying to stuff repeated things into functions for the sake of easy handling and different states to get rid of big chunks of nested if`s wich worked quite well  tried with directing all the debug messages to the other script and check with a DEBUG there if to show or not, the only problem with that was that the memory usage increased by +- 0.5kb compared to just having a single linked message going out just glad that i`ve got it down ALOT with the 3 scripts, 1.7kb, 5.9kb and the hogger down to 11.3 after fetching everything that is needed 
|
|
Void Singer
Int vSelf = Sing(void);
Join date: 24 Sep 2005
Posts: 6,973
|
01-05-2008 16:14
another thing I've noticed many people do is add NULL_KEY to link messages, sensor calls, etc, when they aren't checking for a NULL_KEY...
since NULL_KEY is just a stand in for a 36 character string, it adds overhead when "" works just as well without the overhead... unless you specifically need to check for the special case of a NULL_KEY (vs just making sure you have a valid key) there's not a case I can think of that it's needed...
if (inputKey){ //-- must be type key to work correctly use (key)inputKey if you aren't sure it will be
returns the same false as
if (NULL_KEY){ or if (NULL_KEY == inputKey){
_____________________
| | . "Cat-Like Typing Detected" | . This post may contain errors in logic, spelling, and | . grammar known to the SL populace to cause confusion | | - Please Use PHP tags when posting scripts/code, Thanks. | - Can't See PHP or URL Tags Correctly? Check Out This Link... | - 
|
|
Lear Cale
wordy bugger
Join date: 22 Aug 2007
Posts: 3,569
|
01-06-2008 07:57
From: Hewee Zetkin I'd like to add that the trick being discussed more than likely does not actually "save memory". If myList takes up N bytes, EITHER way you add M bytes to it the end result will be that N+M bytes are used. What the trick does is keep the memory used (actually the memory tracked by whatever form of bookkeeping is done by memory management) from very temporarily jumping to something like 2*N+M bytes before dropping back down to N+M. Are you sure about this? That was my assumption too on first learning of the trick, so I did some testing and found that the memory was permanently lost (or rather, lost as long as the item remained in the list). It wasn't a rigorous test, and things may have changed since then. I'm interested in any information you have about this, thanks!
|
|
Lear Cale
wordy bugger
Join date: 22 Aug 2007
Posts: 3,569
|
01-06-2008 08:03
From: Alicia Sautereau i`ll throw something else in
replaced 7 if statements and created 7 states, each for 1 specific query and then jump from the query handler to a needed state
using the same code just placed in it`s own state instead in an if, saved about 1.4kb memory
have seen that so far from GPL scripts (ingame and wiki) that not many seem to like using a large amount of state`s but from redoing my hogger, it saved valuable memory and depending on the situation, in my case, makes using linked messages to skip to commands i need to execute easier
but, are there any drawbacks of having a dozen states that i`m not aware of? Not drawbacks per se, just the fact that when you choose something to be the crucial "state variable" on which you base your states, it's much harder to change later. With functional logic, it's easier to refactor or add new variables. With states, whenever you want the same behavior in many states, you need to duplicate code. So, the general rule is to use separate states where very different behavior is desired for the different states. It's an art (choosing the semantics for your states). Good choices can lead to very elegant implementations, but sometimes even elegant implementations aren't always easy to adapt to a new requirement. And the general rule about rules applies: know the reason for the rule and violate it when you have a purpose and the cost of breaking the rule is justified. That judgement takes experience, including plenty of practice actually following the rule. In your case, especially if this is a simple or single-purpose helper script that holds a lot of data, if saving memory is crucial then go for it.
|
|
Alicia Sautereau
if (!social) hide;
Join date: 20 Feb 2007
Posts: 3,125
|
01-06-2008 08:56
From: Lear Cale Not drawbacks per se, just the fact that when you choose something to be the crucial "state variable" on which you base your states, it's much harder to change later. With functional logic, it's easier to refactor or add new variables. With states, whenever you want the same behavior in many states, you need to duplicate code.
So, the general rule is to use separate states where very different behavior is desired for the different states. It's an art (choosing the semantics for your states). Good choices can lead to very elegant implementations, but sometimes even elegant implementations aren't always easy to adapt to a new requirement.
And the general rule about rules applies: know the reason for the rule and violate it when you have a purpose and the cost of breaking the rule is justified. That judgement takes experience, including plenty of practice actually following the rule.
In your case, especially if this is a simple or single-purpose helper script that holds a lot of data, if saving memory is crucial then go for it. breaking rules is my favorite thing to, do but a good rule there is some repeative code and some not, i`ve tried to follow the rule for aslong as i could while rewriting everything, but while testing new things, placing 1 time executed code in it`s own state from a nested block of if`s wich was being proccessed on each call, saved alot of memory main -> slave: llMessageLinked(LINK_THIS, LINK_PROC, "start", usrNow); slave -> http: llMessageLinked(LINK_THIS, LINK_COMM, "user:check", id); http: if (xtea_list(0) == "user:check"  state user_check; state user_check { state_entry() { bug_msg("user_check :: 0.1"  ; kl_state = "user:check"; xtea_add("username", llKey2Name(usrNow)); xtea_add("userkey", usrNow); xtea_add("option", "user_check"  ; llSay(0, "Checking user registration..."  ; xtea_send(xtea_post); state http; } } slave -> http: llMessageLinked(LINK_THIS, LINK_PROC, "user:registerd", usrNow); slave -> user:registerd, (check usrNow-id + more)true, timer(60.0) not to time out slave -> main -> user:registerd,true, timer(60.0) not to time out posting some actual code for a change also due to another post regarding the NULL_KEY, with using each http_post in it`s own state, out of a nested IF, i can execute it from the main script or the slave script at any time if the LinkedMessage ID wich is returned by all linkedmessages does not match the usrNow wich the main script is keeping track of, saw in example scripts to just use "" if not doing anything with it  sending from the query state to the general state http for checking if the body isn`t empty and then send it to the according handling state, or in this case if the first word in the returned query is "user", it goes to state http_user to handle the incoming data and pass a couple of variables back to the slave script for handling after wich the slave script sends 1-2 vars in an array to the main script if at any stage the linkedmessage doesn`t matches the usrNow, the script can send the command to recheck only the user registration (or other commands) as they are in their own states i think that overusing the nested IF`s wich are executed on each pass consumes a good chunk of memory and most likely more then needed sim resources (or atleast with my script) when most of the calls are single time executed code to confirm a user exists why go over the same code if user=true with each pass instead of placing it in it`s own state and in the mainscript just check if usrNow=id,iif not -> http check user, return true or false with a dozen queries, parsing all retruned strings and returning needed info to the other scripts, it`s down to less then 12kb memory where befor after only several queries the scripts crashed in a stack-heap think i`ve learned the lesson now to never make the same mistake again stuff 1 time ran code in a state and only call opun it if needed to save the script from running half a dozen IF`s that won`t be used again untill the next user starts it or the session time expires if`s are evil if used wrong 
|