Global and local variables

Started by PunkNoodle, Jan 30, 2017, 05:25 PM

Previous topic - Next topic

PunkNoodle

I browsed for a while and one thing I don't understand of Squirrel language is, when to use global and when local. As far as I can see from script releases, some people prefer to use only global variables at the beginning of the script while others would still use only local at the beginning of the script. Which one of the two options is favourable and why?

P.s. I did read the following from: https://electricimp.com/docs/resources/efficientsquirrel/
Quote from: Electric ImpChoose between top-level locals and globals...

Helps with: memory usage, code size

Variables declared local at the top level of a program, ie. apparently not within any function, are in fact local to a notional main() function that corresponds to the main body of the program. They look like this:
local r = 0, g = 0, b = 0;If you have a large number of such top-level locals, they can take up extra run-time memory and generate a bigger code footprint than do members of the root table — global variables — which look like this:
r <- 0 ; g <- 0 ; b <- 0;However, using locals will be faster — and may in future become faster still as this approach makes optimization of the code easier. Locals are also discarded once no part of the program references them, whereas globals are retained throughout the run-time of the program. This means that, for instance, local functions only used during your program's set-up will be automatically discarded from run-time memory once they are no longer needed.

...but in functions, local is faster than global

Helps with performance

Local variables in functions are looked up by index, not by string hash. This means they are much faster than global variables (or class members, or any other sort of variable). So this:
f <- "";
function readSerial() {
    for (local i = 0; i < 1000 ; ++i) {
        f += hardware.uart1289.read().tochar();
    }
}
is slower than this:
f <- "";
function readSerial() {
    local f2 = "";
    for (local i = 0; i < 1000 ; ++i) {
        f2 += hardware.uart1289.read().tochar();
    }
    f = f2;
}

Although clarifying some stuff, it still doesn't reply to my question completely. If you don't know what I'm talking about please don't reply.

jWeb

#1
Nobody cares when you declare global and/or local variables as long as they're there when you need them.

function A() {
    print(SOMEVAR);
}

function B() {
    SOMEVAR <- "Hello World!";
}

B();
A();

In that particular example. By the time the A() function is compiled, "SOMEVAR" does not even exist. Only when the B() function is called the variable is created.

MakeTimer(this, function() {
    // Must catch the error so the timer won't destroy itself!
    try {
        print(SOMEVAR);
    } catch (e) print(e);
}, 1000, 5);

MakeTimer(this, function() {
    SOMEVAR <- "Hello World!";
}, 3000, 1);

In that particular example "SOMEVAR" does not even exist until the second timer create's it after 3 seconds.

So as you can see. Nobody cares when you create global variables as long as they're there when used. That's one of the issues with Squirrel. Because if you spell the name of a variable wrong. You won't know about it until you execute that code. And most of the times that happens in production environments (which is bad!).

Ideally, you'd want to reduce the number of things that you create in the global table. Because if you create too many then it'll slow down the search for the others. I believe there are some video tutorials around this forum that explain how contexts/scopes and closures work in Squirrel.

And also, that guide from the quote you included in your program should be more than enough to answer all your questions of when to use them when performance is a concern.

In addition to that guide, I'll add this image to further guide anyone in choosing to store their variables in the most optimal location.



EDIT: Further clarification because the given information might be interpreted wrong.

By "globally in the root table" I mean either:
SOMEVAR <- "Hello World!";
// Or manually: getroottable().rawset("SOMEVAR", "Hello World!");

By "locally to the script" I mean local outside of a function:
// myscript.nut

local SOMEVAR = "Hello World!";

function ABC() {
    print(SOMEVAR);
}

function IJK() {
    print(SOMEVAR);
}

function XYZ() {
    print(SOMEVAR);
}

ABC();
IJK();
XYZ();

/* Output:
Hello World!
Hello World!
Hello World!
*/

And by "locally to the function" I mean:
function ABC() {
    local SOMEVAR = "Hello";
    print(SOMEVAR);
}

function IJK() {
    local SOMEVAR = "World";
    print(SOMEVAR);
}

function XYZ() {
    local SOMEVAR = "!";
    print(SOMEVAR);
}

ABC();
IJK();
XYZ();

/* Output:
Hello
World
!
*/

Please note that even though function environments can be altered, you'll still be referring to the variable locally to a script:
SOMEVAR <- "NOT Hello World!"; // global
local SOMEVAR = "Hello World!"; // local

function ABC() {
    print(SOMEVAR);
}

ABC();
::ABC(); // force root table as the environment
ABC.call(getroottable()); // force root table as the environment

/* Output:
Hello World!
Hello World!
Hello World!
*/

This behavior was explained in the quote from the initial post.

PunkNoodle

#2
That image is what I was looking for and it cleared my doubts. Multumiri / thanks thanks and thanks!