Scripting Helpers is winding down operations and is now read-only. More info→
Ad
Log in to vote
0

Local variables, and saving memory?

Asked by
LuaQuest 450 Moderation Voter
8 years ago

Back and fourth people are arguing when to, and when to not use local variables (functions included). I realize local variables create a value confined to it's current scope, but what about when it's not in a scope?

When that happens, people start talking about memory. (i.e: using local variables / functions saves memory because it's not being stored in getfenv)

So something like:

local Local_Variable = "Local variable" -- less memory?

Not_Local_Variable = "Normal variable" -- more memory?

Even if the memory consumption is too low to even consider, is this true? Do locals actually save memory whether it's significant or not?

If someone could clear this up for me, i'd appreciate it. Thanks.

0
One thing to keep in mind -- this could vary by implementation (though it's likely ROBLOX's implementation won't change anytime soon) BlueTaslem 18071 — 8y

1 answer

Log in to vote
2
Answered by
BlueTaslem 18071 Moderation Voter Administrator Community Moderator Super Administrator
8 years ago

In the standard Lua implementation, which I am 99% sure is the implementation that ROBLOX uses, globals are more or less stored in a hashtable while locals are stored in a stack.


The global hashtable is a re-sizable data-structure that "hashes" strings, turning them into numbers. (table gets its name from hashtable) These numbers are then used to look up a place where the value is approximately in the hashtable. This allows you to retrieve arbitrary information from a very large array very quickly.

The hashtable is nice because it allows dynamically setting/getting values at runtime, without knowing what variables will be needed (this is necessary for things like loadstring).

Here's what the compiled code looks like:

> x = 5; print(x)
; source chunk: (interactive mode)
; x86 standard (32-bit, little endian, doubles)

; function [0] definition (level 1)
; 0 upvalues, 0 params, 2 stacks
.function  0 0 2 2
.const  "x"  ; 0
.const  5  ; 1
.const  "print"  ; 2
[1] loadk      0   1        ; 5
[2] setglobal  0   0        ; x
[3] getglobal  0   2        ; print
[4] getglobal  1   0        ; x
[5] call       0   2   1
[6] return     0   1
; end of function

Local variables are not stored in this way. Partly this is for performance -- while hashtables are fast at picking out one thing from many, there's a performance penalty because of the hash and the "approximately" part1.

Local variables are also different in that they have a history -- you can have many local xs, as long as they are in later scopes (probably in different recursive calls to a function).

This is done with a stack. Each scope produces a new "layer" on the stack. Since local variables are known when they're compiled, they can each be assigned a unique number, starting at 02.

Instead of referring to the variable by its name, you refer to its position relative to the current scope.

However, the variable name is still included, so that error messages can be useful. It's simply only used when defining the variable, rather than when it's wanted to get.

> local x = 5; print(x);
; source chunk: (interactive mode)
; x86 standard (32-bit, little endian, doubles)

; function [0] definition (level 1)
; 0 upvalues, 0 params, 3 stacks
.function  0 0 2 3
.local  "x"  ; 0
.const  5  ; 0
.const  "print"  ; 1
[1] loadk      0   0        ; 5
[2] getglobal  1   1        ; print
[3] move       2   0
[4] call       1   2   1
[5] return     0   1
; end of function

There's relatively few memory differences between the two results.

Hashtables inherently use more memory than necessary so that they remain fast, but this will be less than 2x. The hashtable is just of pointers, so that means you likely have no more than 16 bytes extra per global variable in comparison to the number of local variables.

To store the name, parent, Transparency, and Reflectance of a Part (skipping its Surfaces, color, position, velocity, etc) you'd need at least 32 bytes.

In other words, switching to locals will not actually save you anything. If you really are memory conscious, there is definitely actual memory in your program that you don't need.

Wisdom

Rules of Optimization

  1. Don't optimize
  2. (only for experts) Don't optimize, yet.
  3. Profile before optimizing

This means -- if you don't have a problem, don't create one by making your code weird.

And only change your code to make it better if you actually have evidence that

  • your change will make it better, not worse (measure -- don't just think)
  • the thing you're changing is actually the problem (e.g., it's using 1 KB of the 500 MB you're using -- not a problem)

  1. As far as I know, the standard Lua implementation doesn't use a hash for this lookup -- it uses the memory address of the string, and guarantees all equal strings are stored in the same location in memory. 

  2. Other things, in addition to local variables, go here. Things like number constants and names also live on the stack. 

0
Another brilliant answer, thank you. LuaQuest 450 — 8y
Ad

Answer this question