Welcome to the Second Life Forums Archive

These forums are CLOSED. Please visit the new forums HERE

512-byte segments

Lear Cale
wordy bugger
Join date: 22 Aug 2007
Posts: 3,569
12-21-2009 07:38
I recently learned that in Mono, each function, each state, and the global variables are all in separate code segments, which are multiples of 512 bytes in size.

ACK!

This strongly discourages the use of small functions, which is a very bad thing.

It also makes it very difficult for us to figure out the memory costs of different implementation options.

Does anyone know more about the 512-byte segment size granularity, why it exists, and whether it could be reduced? (I'd like to see it be 1 byte, but I can understand variables needing to be aligned on say 4- or 8-byte boundaries.)

Thanks!
Kitty Barnett
Registered User
Join date: 10 May 2006
Posts: 5,586
12-21-2009 11:59
There's some other oddness going on as well.

Empty functions don't seem to take up any space at all (so presumably they're optimized out) and it does look like there's some rudimentary inlining (as far as I can tell very small "trivial" functions that don't call another function do get inlined).

(Since the script limits are being discussed on SLdev with Kelly Linden participating to some degree I cc'ed one asking for clarification on Friday but no response so far)
Lear Cale
wordy bugger
Join date: 22 Aug 2007
Posts: 3,569
12-21-2009 14:59
Thanks, Kitty.

Unused functions don't take space: are you sure you called it? I assume this is an optimization.
Void Singer
Int vSelf = Sing(void);
Join date: 24 Sep 2005
Posts: 6,973
12-21-2009 15:05
I suppose that I should have paid more attention to the scripting list recently.

that number doesn't seem to add up though.... in terms of overall script size, or vm.

(but having another thing screw with accurate memory measurement in mono? that makes perfect sense to me) =/
_____________________
|
| . "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...
| -
Viktoria Dovgal
Join date: 29 Jul 2007
Posts: 3,593
12-21-2009 15:29
From: Void Singer
that number doesn't seem to add up though.... in terms of overall script size, or vm.

You're right, it is more involved. SVC-4387 has some better illustrations of what happens.
Talarus Luan
Ancient Archaean Dragon
Join date: 18 Mar 2006
Posts: 4,831
12-21-2009 23:35
It ALLOCATES script space in 512-byte blocks.

I pointed this out in SoSL last week when the topic came up.

Create a basic script, no functions. Record the space used. Add a simple function. Check the space; it should use 512 more bytes. Add another simple function. You should observe no change in the memory size. If you continue adding more small functions, eventually, it will jump up another 512 bytes, then no more for a bit, then another 512 bytes, etc.

It DOES NOT allocate 512 bytes "per function" (unless the function compiles to that amount of code or more). If it did, I'd have several scripts which would be using more than 64k.
Kitty Barnett
Registered User
Join date: 10 May 2006
Posts: 5,586
12-22-2009 01:58
From: Talarus Luan
It DOES NOT allocate 512 bytes "per function" (unless the function compiles to that amount of code or more). If it did, I'd have several scripts which would be using more than 64k.
Feel free to explain the following:

Version 1:
CODE

default
{
state_entry()
{
}

touch_start(integer total_number)
{
llSay(0, (string)llGetFreeMemory());
}
}

Free memory: 62,204

Version 2:
CODE

debug(string str)
{
llOwnerSay(str);
}

default
{
state_entry()
{
}

touch_start(integer total_number)
{
llSay(0, (string)llGetFreeMemory());
}
}

Free memory: 61,692
Difference: +512 bytes due to debug()

Version 3:
CODE

debug(string str)
{
llOwnerSay(str);
}

default
{
state_entry()
{
debug("Starting up...");
}

touch_start(integer total_number)
{
llSay(0, (string)llGetFreeMemory());
}
}

Free memory: 61,180
Difference: +512 bytes due to state_entry() [empty=optimized away]

Version 4:
CODE

debug(string str)
{
llOwnerSay(str);
}

default
{
state_entry()
{
debug("Starting up...");
}

touch_start(integer total_number)
{
llSay(0, (string)llGetFreeMemory());
}

timer()
{
debug("Timer event");
}
}

Free memory: 60,668
Difference: +512 bytes due to timer()

Version 5:
CODE

debug(string str)
{
llOwnerSay(str);
}

integer f(integer x)
{
integer idx = 2; integer cnt = x;
for (; idx < cnt; idx++)
x *= idx;
return x;
}

default
{
state_entry()
{
debug("Starting up...");
}

touch_start(integer total_number)
{
llSay(0, (string)llGetFreeMemory());
}

timer()
{
debug("Timer event");
}
}

Free memory: 60,156
Difference: +512 bytes due to f()

Version 6:
CODE

debug(string str)
{
llOwnerSay(str);
}

integer f(integer x)
{
integer idx = 2; integer cnt = x;
for (; idx < cnt; idx++)
x *= idx;
return x;
}

default
{
state_entry()
{
debug("Starting up...");
llSetTimerEvent(10.0);
debug("Timer started");
}

touch_start(integer total_number)
{
llSay(0, (string)llGetFreeMemory());
llSay(0, (string)f(5));
}

timer()
{
debug("Timer event");
}
}

Free memory: 60,156
Difference: +0 bytes since though we added extra code to two functions it still fits within the 512 bytes

If multiple functions could share a code segment in general then those added 4 shouldn't be eating up 2,048 but rather all fit into one single 512 segment since they are all rather trivial (or 2 at the most).

(Each script needs to execute at least one line of code and then be reset and rerun for an an accurate result - ie for versions 1-2 that means you have to touch it first, reset and then touch again)
Talarus Luan
Ancient Archaean Dragon
Join date: 18 Mar 2006
Posts: 4,831
12-22-2009 14:25
Hmmm. I guess either something changed, or I was mistaken.

When I ran a similar test earlier last week, I started out with just this:

CODE
default {
state_entry() {
llOwnerSay((string)llGetFreeMemory());
}
}


I got 62180 bytes. I then added a single simple function:

CODE
integer IsKey(key pKey) {
if (pKey)
return 2;
else
return (pKey == NULL_KEY);
}

default {
state_entry() {
llOwnerSay((string)llGetFreeMemory());
}
}


which gave 61668 bytes. Next, I made a copy of that function and renamed it:

CODE
integer IsKey(key pKey) {
if (pKey)
return 2;
else
return (pKey == NULL_KEY);
}

integer IsKeyB(key pKey) {
if (pKey)
return 2;
else
return (pKey == NULL_KEY);
}

default {
state_entry() {
llOwnerSay((string)llGetFreeMemory());
}
}


which still gave 61668 bytes.

I just tried that exact same experiment again, and this time the third script is giving 61156 bytes. More copies continue to decrease the memory by 512 bytes as you all are observing.

What is puzzling is why the prior test didn't show that at the time. I went back into my chat logs to see if I was misreading the output, but there it is:

[2009/12/15 15:10] Object: Hello, Avatar!
[2009/12/15 15:11] Object: 62180
[2009/12/15 15:11] Object: 61668
[2009/12/15 15:13] Object: 61668

So, either I am insane, or something has changed.
Kitty Barnett
Registered User
Join date: 10 May 2006
Posts: 5,586
12-22-2009 14:40
From: Talarus Luan
So, either I am insane, or something has changed.
I first noticed the 512 byte multiple per function thing about a year ago now though so it's been there for quite a while.

Once I ran out of memory I started going through every function deleting portions, recompiling and checking the memory, then pasting the deleted bit into the calling functions to see if I could keep the 512 bytes gain, etc etc.
All in all I ended up freeing enough extra Kbs to avoid splitting the script into two just by rearranging code which shouldn't have really resulted in that significant change if multiple functions could share the same code segment (or maybe they do but under some specific unknown set of circumstances).

It would be good for LL to actually be open about what's going on under the hood (and update whenever things change). Compiling a script really shouldn't be this unpredictable.
Lear Cale
wordy bugger
Join date: 22 Aug 2007
Posts: 3,569
02-04-2010 10:48
From: Talarus Luan
It ALLOCATES script space in 512-byte blocks.

I pointed this out in SoSL last week when the topic came up.

Create a basic script, no functions. Record the space used. Add a simple function. Check the space; it should use 512 more bytes. Add another simple function. You should observe no change in the memory size. If you continue adding more small functions, eventually, it will jump up another 512 bytes, then no more for a bit, then another 512 bytes, etc.

It DOES NOT allocate 512 bytes "per function" (unless the function compiles to that amount of code or more). If it did, I'd have several scripts which would be using more than 64k.
My testing shows that you're not correct. Each function takes 512 bytes. However, it did appear that in some cases, functions that are not called did not take space. That may have been due to not taking into account resetting that Kitty says we need, and which I don't quite understand yet.

Also, each state handler seems to take 512 bytes. (eek!)
Kitty Barnett
Registered User
Join date: 10 May 2006
Posts: 5,586
02-05-2010 03:23
From: Lear Cale
That may have been due to not taking into account resetting that Kitty says we need, and which I don't quite understand yet.
I just meant that after you save a script inside of a prim it instantly starts executing.

The free memory reported then won't match what is reported after you reset the scripts (but once you reset you'll get consistent numbers each time you reset).

I *think* the "free script memory" after resetting matches the amount of free memory when you just drop the script into a prim from inventory but I'm not entirely sure of that from memory.

So the reason I suggested always resetting the script no matter how you made it run the first time is so that everyone would be working with reproducable situations :).