Don't click here unless you want to be banned.

LSL Wiki : MemoryUsage

HomePage :: PageIndex :: RecentChanges :: RecentlyCommented :: UserSettings :: You are ia360925.us.archive.org

Memory Usage


Important note: The information on this page is obtained from methodical testing, and contains a degree of speculation regarding some of the inner workings of LSL. While the numbers have a high degree of confidence from the author, the conclusions based on the numbers are not meant to be definitive or authoritative, and are subject to much change as more information is revealed.

Mono script memory usage with inline lists has some weirdness. It takes up to 3.5 times the amount of bytes the same data takes in a dynamically built list. MonoListMemoryUsage

Since it looks I can't create this page called MonoListMemoryUsage, though, I'll just plop it right here. Someone with write access please create the page and remove this from here.



I had noticed that lists in Mono seem to eat more memory than you'd expect.
So I finally decided to do some research. Here's the code I used and the
results. The test method was adding integers to the list called test,
compiling and recording results. I adjusted the zero point so the result is
0 with an empty list.

list test = [
0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
];
default
{
    state_entry()
    {
        llOwnerSay((string)(61508-llGetFreeMemory()) + " bytes for " +
        (string)(llGetListLength(test)) + " items in list.");
    }
}

[04:02 AM] Object: 192 bytes for 16 items in list.
[04:02 AM] Object: 268 bytes for 17 items in list.
[04:06 AM] Object: 280 bytes for 18 items in list.
[04:06 AM] Object: 804 bytes for 19 items in list.
[04:10 AM] Object: 1328 bytes for 52 items in list.
[04:10 AM] Object: 1852 bytes for 53 items in list.
[04:09 AM] Object: 1984 bytes for 64 items in list.
[04:13 AM] Object: 2252 bytes for 65 items in list.
[04:15 AM] Object: 2504 bytes for 86 items in list.
[04:15 AM] Object: 3028 bytes for 87 items in list.
[04:18 AM] Object: 3424 bytes for 120 items in list.
[04:19 AM] Object: 3948 bytes for 121 items in list.
[04:17 AM] Object: 4032 bytes for 128 items in list.
[04:17 AM] Object: 4556 bytes for 129 items in list.
[04:22 AM] Object: 4856 bytes for 154 items in list.
[04:22 AM] Object: 5380 bytes for 155 items in list.
[04:26 AM] Object: 5776 bytes for 188 items in list.
[04:25 AM] Object: 6300 bytes for 189 items in list.

here I decided the exact details don't need to be gained for longer than
this and decided to see what's the size limit for the list of integers when
defined in the source.

[04:28 AM] Object: 11984 bytes for 364 items in list.
[04:28 AM] Object: 23376 bytes for 716 items in list.
[04:29 AM] Object: 46672 bytes for 1420 items in list.
[04:30 AM] Object: 58704 bytes for 1868 items in list.

the next attempt I made compiled but gave me a stack heap collision... This
could be a clue, I expected it to not compile. I proceeded to narrow down
the maximum size it still runs at.

[04:36 AM] Object: 61464 bytes for 1970 items in list.

This was the largest size I could get it to compile with. one more and it
stops. All integers.

For comparison, I made a script that dynamically adds to the list. and keeps
updating floating text with the count. It counted up to 3082 before giving
an error. Compiled without mono, the result was 472. I was also pretty happy
to see that it was significantly slower to complete. Using the
optimization in specified in the comment made it go up to 1064

list test;
default
{
    state_entry()
    {
        while (1) {
            // no difference with mono: test = (test = []) + test + 1
            test += 1; 
            llSetText((string)llGetListLength(test),<1.0,1.0,1.0>,1.0);
        }
    }
}

I also thought I'd see how code compiled without mono fares here and
modified the script to give accurate information that way. This is the
largest list I could specify so the script worked. the sizes above this
first gave stack heap collisions and if I went much above this, refused to
compile.

[04:52 AM] Object: 16008 bytes for 1067 items in list.

This is very close to the count achievable by the dynamic list in code compiled
without mono.

I decided to make one more test. I used the dynamic list build code,
like above, but added an extra (and unused) list with 1536 integers in it and compiled
the code in Mono. It counted up to 601.



These results were obtained from empirical testing and observation using the llGetFreeMemory function, and working around its reporting limitations. The results are believed to be accurate, with experience from using these values as a reference giving a high degree of confidence that they are such. This information is of value to scripters who wish to know just how much memory they may be using in their scripts' data structures, and is quite useful in helping avoid the dreaded stack/heap collision error.

Variable Type Memory Usage (bytes) as Standalone Variable Memory Usage (bytes) as List Element Notes
integer 10 15
float 10 15
vector 18 23
rotation 22 27
string 18 + 1 per character 12 + 1 per character
key 18 + 1 per character 12 + 1 per character Keys are basically identical to strings; generally, their normal values are 36 characters in length, so the base size plus 36 is a common value for how much space they take up.
list 21 n/a This is simply the basic space taken up by a list variable, no matter if it has 0 or a hundred elements. The size of the elements stored within is given in the third column of this table.

As you can see, there is quite a bit of overhead for each declared variable. As global variables, they take an extra 6 bytes each over their minimum data representation. Numeric-type list elements all have an extra 5 bytes of overhead above this, which is most likely pointer values, since they are often described as being implemented as linked lists. In fact, from the size of the list variable, it would infer it is comprised of 3 such links; probably a head pointer, a tail pointer, and possibly a count. Why these values are 5 bytes (40 bits) given that the addressable space for each script is only 14 bits is a mystery. Strings similarly have a curious amount of overhead, most likely a pointer to the actual string data on the stack or heap, and a length. In addition, why strings/keys lose the 6 bytes of overhead when put into a list (plus not suffering an additional 5 byte overhead from the link data), and the other types do not is another mystery.

Reproducing the data


To reproduce this data, use the following baseline script, and then insert the specified variable declarations as globals:

Script Free Memory (bytes) Difference from Baseline (bytes)
default {
    state_entry() {
        llOwnerSay((string)llGetFreeMemory());
    }
}
16172 (baseline) 0
integer i;
16162 10
float f;
16162 10
vector v;
16154 18
rotation r;
16150 22
string s;
16154 18
string s="";
16154 18
string s="Some characters to test this theory!";
16118 54 (18 + 36)
key k;
16154 18
key k="";
16154 18
key k="01234567-89ab-cdef-0123-456789abcdef";
16118 54 (18 + 36)
list l;
16151 21
list l=[];
16151 21
list l=[12345];
16136 36 (21 + 15)
list l=[1.2345];
16136 36 (21 + 15)
list l=[<1.2,3.4,5.6>];
16128 44 (21 + 23)
list l=[<1.2,3.4,5.6,7.8>];
16124 48 (21 + 27)
list l=[""];
16139 33 (21 + 12)
list l=["Some characters to test this theory!"];
16103 69 (21 + 12 + 36)
key k="";
16121 51 (18 + 21 + 12)
key k="01234567-89ab-cdef-0123-456789abcdef";
16049 123 (18 + 36 + 21 + 12 + 36)
list l=[12345,1.2345,<1.2,3.4,5.6>,
        <1.2,3.4,5.6,7.8>, 
        "Some characters to test this theory!",
        "01234567-89ab-cdef-0123-456789abcdef"];
15975 197 (21 + 15 + 15 + 23 + 27 + 12 + 36 + 12 + 36)

Anticipated questions:

Q: Why does it use more memory if you assign the values inside of the events?
A: First, you have to remember that llGetFreeMemory is a low-water mark. In other words, once you use X amount of memory, even if you free it up, the function will still report (16384 - X) free memory. That said, temporary copies of variables use up stack space, as well as all the pass-by-value parameters in function calls, all adding more overhead which is reported as part of the memory use. Expressions also tend to create multiple temporary variables which hold intermediate results. As a result, you will see the amount of memory used double, triple, or go even higher.

Q: What about literal constants in code?
A: It is somewhat difficult to determine exactly, because even variable assignments appear to make multiple copies at times. However, it appears that they use about the same, if not the same amount of memory as if they were each defined as a global variable. Also bear in mind that the LSL compiler is NOT in any way, shape, or form, an optimizing compiler. Thus, it does not do things like "constant folding", where the exact same constant value used in multiple sections of the code is only stored once, with all instances in the code referencing that same value.


Memory | Types | Variables

Memory management in LSL seems to be a bit peculiar. I ran my own similar test and ended up with this:
// Second Life Server 1.26.2.11726
//
// baseline 62180 bytes

//integer  i;                     //   4 bytes
//float    f;                     //   4 bytes
//vector   v;                     //  12 bytes
//rotation r;                     //  16 bytes
//string   s;                     //  18 bytes (18 + 2 * length)
//string   s = "A";               //  20 bytes (18 + 2 * length)
//string   s = "AA";              //  22 bytes (18 + 2 * length)
//key      k;                     //   4 bytes
//key      k=NULL_KEY;            //   4 bytes
//key      k="00000000-0000-0000-0000-000000000000"; //   4 bytes
//key      k="8dcd4a48-2d37-4909-9f78-f7a9eb4ef903"; //   4 bytes
//list     l;                     // 104 bytes
//list     l = [0];               // 116 bytes (104 + 12 * 1)
//list     l = [0, 1];            // 128 bytes (104 + 12 * 2)
//list     l = [0.0];             // 116 bytes
//list     l = [0.0, 1.0];        // 128 bytes
//list     l = [""];              // 118 bytes (104 + (12))
//list     l = ["A"];             // 120 bytes (104 + (12 + 2 * 1))
//list     l = ["AA"];            // 122 bytes (104 + (12 + 2 * 2))
//list     l = ["", ""];          // 118 bytes (104 + (12))
//list     l = ["A", "A"];        // 120 bytes (104 + 16)
//list     l = ["A", "B"];        // 136 bytes (104 + 34)
//list     l = ["A", "B", "C"];   // 152 bytes (104 + 48)

default
{
    state_entry()
    {
//      integer  i;          //   4 bytes
//      float    f;          //   4 bytes
//      vector   v;          //  12 bytes
//      rotation r;          //  16 bytes
//      string   s;          //   4 bytes
//      string   s = "A";    //   4 bytes
//      string   s = "AA";   //   4 bytes
//      key      k;          //   4 bytes
//      key      k=NULL_KEY; //   4 bytes
//      key      k="00000000-0000-0000-0000-000000000000"; //   4 bytes
//      key      k="8dcd4a48-2d37-4909-9f78-f7a9eb4ef903"; //   4 bytes
//      list     l;          //   4 bytes
        llOwnerSay ((string)llGetFreeMemory());
    }
}
Some of it makes sense, other parts make my brain twist. (P.S. I tried to add this as a comment, but I think there might be a limitation on comment length?)
There is no comment on this page. [Display comments/form]