WoW:Memory usage: Difference between revisions

From AddOn Studio
Jump to navigation Jump to search
mNo edit summary
m (Move page script moved page Memory usage to Memory usage without leaving a redirect)
 
(9 intermediate revisions by 9 users not shown)
Line 1: Line 1:
{{uitech}} __NOTOC__
{{merge|Lua object memory sizes}}
{{uitech}}
Information on the '''memory usage''' of data in the WoW [[Lua]] environment.


== Data ==
There are two basic classes of data that can be assigned to variables, passed around, etc, in Lua.


Some information on the memory usage of data in the WoW LUA environment (Expanding upon a forum posting).  
=== Values ===
Simple data types like numbers and booleans are represented as their actual '''values''', and are passed around as such.


= Data =
=== References ===
There are two basic classes of data that can be assigned to variables, passed around, etc, in LUA.
Complex data types (Userdata, Strings, and Tables) are represented as '''references''' to their actual value. Each reference fits in the same amount of memory as a value, but then the actual object that is referenced takes up additional space.
 
== Values ==
Simple data types like numbers and booleans are represented as their actual <b>values</b>, and are passed around as such.
 
== References ==
Complex data types (Userdata, Strings, and Tables) are represented as <b>references</b> to their actual value. Each reference fits in the same amount of memory as a value, but then the actual object that is referenced takes up additional space.


=== Strings ===
=== Strings ===
The contents of strings are immutable, so once created a string cannot be changed, just replaced with another string. This allows LUA to maintain a pool of every distinct string value in use, such that multiple occurrences of the same string are all in fact references to the same unchangeable string object.
The contents of strings are immutable, so once created a string cannot be changed, just replaced with another string. This allows Lua to maintain a pool of every distinct string value in use, such that multiple occurrences of the same string are all in fact references to the same unchangeable string object.


If you have a table containing 30,000 entries of "Visit http://www.wowwiki.com/ to find out how to get more out of the World of Warcraft", there's actually only one instance of the text itself. Each occurrence of the string is a reference to that shared instance.
If you have a table containing 30,000 entries of "Visit http://www.wowwiki.com/ to find out how to get more out of the World of Warcraft", there's actually only one instance of the text itself. Each occurrence of the string is a reference to that shared instance.
Line 21: Line 20:
Unlike strings, tables can be altered, and may grow as entries are added to them. A table is made up of 3 parts:
Unlike strings, tables can be altered, and may grow as entries are added to them. A table is made up of 3 parts:


* The table itself - 32 bytes
* The table itself - 36 bytes
* 'List' entries - Those with consecutive integer indexes starting at 1 - 16 bytes each
* 'List' entries - Those with consecutive integer indexes starting at 1 - 16 bytes each
* 'Map' entries - Those with non-consecutive integer indexes, and string indexes.- 80 bytes each
* 'Map' entries - Those with non-consecutive integer indexes, and string indexes.- 40 bytes each


An empty table is just the minimum 32 bytes. As you start adding entries then it will begin to accumulate memory in which to store those. Memory accumulation isn't linear however, instead there is an exponental algorithm, so that as the table gets larger, incrementally larger numbers of elements are pre-allocated.
An empty table is just the minimum 32 bytes. As you start adding entries then it will begin to accumulate memory in which to store those. Memory accumulation isn't linear however, instead there is an exponential algorithm, so that as the table gets larger, incrementally larger numbers of elements are pre-allocated.


For example, the size of a list of numbers increases as follows as new values are inserted: 32, 48, 64, 96, 96, 160, 160, 160, 160, etc..
For example, the size of a list of numbers increases as follows as new values are inserted: 36, 52, 68, 100, 100, 164, 164, 164, 164, etc..


Tables which mix both forms of values (list entries, and map entries) use a mix of the two entry types
Tables which mix both forms of values (list entries, and map entries) use a mix of the two entry types


= Local Variables =
In Lua 5.1 tables that are created with literal initializers such as x = { "a", "b", key = "value" } allocate exactly what they need without any extra pre-allocation.
 
=== Closures ===
Closures are the manifestation of function prototypes within the Lua environment. Lua closures consist of a base object, with some storage for each upvalue the closure references, and then there's allocated space for each upvalue itself. In WoW 2.2.0 (Measured on the PTR on 2007-08-08) the memory usage for each is as follows:
 
* 28 bytes for the base closure
* 4 bytes for each upvalue it references
* 32 bytes for each distinct upvalue value
 
This means that an arrangement like...
 
function createClosures(a,b,c)
  return function() return 1,a,b,c end,
          function() return 2,a,b end
end
 
...allocates 172 bytes of memory every time it is run (28 bytes for each closure (56), 4 bytes for each of the upvalue references, three from the first closure, and two from the second (20), and 32 bytes for each of the three distinct upvalues (96))
 
=== Frames ===
Since a bulk of a frame's existence is on the C/C++ side of things, it's a bit difficult to get a true sense of their memory impact, however it's possible to measure the Lua footprint of the frame alone. It turns out that a frame's Lua footprint is initially just the size of its table representation (76 bytes, in WoW 2.2), there doesn't appear to be any other "magic" internal repository of other data.
 
Having said that, it's a little tricky to fully test this because many of Lua's internal structures (The global environment, the Lua string table, the internal C library reference table) are dynamically managed and grow with the exponential algorithm mentioned earlier, so it's possible that there's some table entries there being consumed by the frames.
 
Most puzzling (and the largest reason I suspect there is a hidden internal data structure somewhere) is that there doesn't appear to be a readily measurable impact of :SetScript on a frame, yet the frame seems to manage to keep a reference to its handler quite happily. I'm guessing that each non-nil script handler really consumes about 16 bytes in the form of an entry in the library's reference table, but I could be entirely wrong! - [[User:Flickering|Flickering]] 06:10, 9 August 2007 (UTC)
 
== Local Variables ==
 
Locally scoped variables in functions are stored on the stack, rather than being part of the garbage collected memory heap, however if the variable contains a reference type, then the actual data referenced is not on the stack. (Though it's not actually a stack, per se, see the Lua source code for details)
 
== Reference Functions ==
 
In WoW 2.2, based on the PTR on 2007-08-08, the following functions can be used to determine the size of a dynamically allocated hash or array table:
 
function DynamicHashTableSize(entries)
  if (entries == 0) then
    return 36;
  else
    return math.pow(2, math.ceil(math.log(entries) / math.log(2))) * 40 + 36;
  end
end


Locally scoped variables in functions are stored on the stack, rather than being part of the garbage collected memory heap, however if the variable contains a reference type, then the actual data referenced is not on the stack. (Though it's not actually a stack, per se, see the LUA source code for details)
function DynamicArrayTableBytes(entries)
  return 36 + math.pow(2, math.ceil(math.log(entries) / math.log(2))) * 16
end

Latest revision as of 04:48, 15 August 2023

Information on the memory usage of data in the WoW Lua environment.

Data[edit]

There are two basic classes of data that can be assigned to variables, passed around, etc, in Lua.

Values[edit]

Simple data types like numbers and booleans are represented as their actual values, and are passed around as such.

References[edit]

Complex data types (Userdata, Strings, and Tables) are represented as references to their actual value. Each reference fits in the same amount of memory as a value, but then the actual object that is referenced takes up additional space.

Strings[edit]

The contents of strings are immutable, so once created a string cannot be changed, just replaced with another string. This allows Lua to maintain a pool of every distinct string value in use, such that multiple occurrences of the same string are all in fact references to the same unchangeable string object.

If you have a table containing 30,000 entries of "Visit http://www.wowwiki.com/ to find out how to get more out of the World of Warcraft", there's actually only one instance of the text itself. Each occurrence of the string is a reference to that shared instance.

Tables[edit]

Unlike strings, tables can be altered, and may grow as entries are added to them. A table is made up of 3 parts:

  • The table itself - 36 bytes
  • 'List' entries - Those with consecutive integer indexes starting at 1 - 16 bytes each
  • 'Map' entries - Those with non-consecutive integer indexes, and string indexes.- 40 bytes each

An empty table is just the minimum 32 bytes. As you start adding entries then it will begin to accumulate memory in which to store those. Memory accumulation isn't linear however, instead there is an exponential algorithm, so that as the table gets larger, incrementally larger numbers of elements are pre-allocated.

For example, the size of a list of numbers increases as follows as new values are inserted: 36, 52, 68, 100, 100, 164, 164, 164, 164, etc..

Tables which mix both forms of values (list entries, and map entries) use a mix of the two entry types

In Lua 5.1 tables that are created with literal initializers such as x = { "a", "b", key = "value" } allocate exactly what they need without any extra pre-allocation.

Closures[edit]

Closures are the manifestation of function prototypes within the Lua environment. Lua closures consist of a base object, with some storage for each upvalue the closure references, and then there's allocated space for each upvalue itself. In WoW 2.2.0 (Measured on the PTR on 2007-08-08) the memory usage for each is as follows:

  • 28 bytes for the base closure
  • 4 bytes for each upvalue it references
  • 32 bytes for each distinct upvalue value

This means that an arrangement like...

function createClosures(a,b,c)
  return function() return 1,a,b,c end,
         function() return 2,a,b end
end

...allocates 172 bytes of memory every time it is run (28 bytes for each closure (56), 4 bytes for each of the upvalue references, three from the first closure, and two from the second (20), and 32 bytes for each of the three distinct upvalues (96))

Frames[edit]

Since a bulk of a frame's existence is on the C/C++ side of things, it's a bit difficult to get a true sense of their memory impact, however it's possible to measure the Lua footprint of the frame alone. It turns out that a frame's Lua footprint is initially just the size of its table representation (76 bytes, in WoW 2.2), there doesn't appear to be any other "magic" internal repository of other data.

Having said that, it's a little tricky to fully test this because many of Lua's internal structures (The global environment, the Lua string table, the internal C library reference table) are dynamically managed and grow with the exponential algorithm mentioned earlier, so it's possible that there's some table entries there being consumed by the frames.

Most puzzling (and the largest reason I suspect there is a hidden internal data structure somewhere) is that there doesn't appear to be a readily measurable impact of :SetScript on a frame, yet the frame seems to manage to keep a reference to its handler quite happily. I'm guessing that each non-nil script handler really consumes about 16 bytes in the form of an entry in the library's reference table, but I could be entirely wrong! - Flickering 06:10, 9 August 2007 (UTC)

Local Variables[edit]

Locally scoped variables in functions are stored on the stack, rather than being part of the garbage collected memory heap, however if the variable contains a reference type, then the actual data referenced is not on the stack. (Though it's not actually a stack, per se, see the Lua source code for details)

Reference Functions[edit]

In WoW 2.2, based on the PTR on 2007-08-08, the following functions can be used to determine the size of a dynamically allocated hash or array table:

function DynamicHashTableSize(entries)
  if (entries == 0) then
    return 36;
  else
    return math.pow(2, math.ceil(math.log(entries) / math.log(2))) * 40 + 36;
  end
end
function DynamicArrayTableBytes(entries)
  return 36 + math.pow(2, math.ceil(math.log(entries) / math.log(2))) * 16
end