1
0
mirror of https://github.com/elua/elua.git synced 2025-01-25 01:02:54 +08:00
elua/doc/en/arch_ltr.html
Dado Sutter 17487f9ebe - doc folder replaced by some work done on Led Lab for the new doc and site
- This should actually be a merge but we made a mistake on the initial repo creation
  and a merge was not possible.
- Below there is a resumed log of the commit messages for the few steps, just for
  the record.

- The merged commit messages for this work are:

 - Removing Portuguese doc content
 - Ignore folder names fixed on .gitignore
 - Removed doc files which content migrated to the CMS
 - docdata.lua updated accordingly
 - Doc build checked ok
 - Overall doc structure and contents still being refined
 - Removing folder cache from git versioning
 - Removing folder dist from git versioning. The folders above are generated by the buildall.lua script and are not part of the sources
 - Adding .gitignore file with objects info to inform git what to ignore
 - Removed file
 - Merge branch 'master' of git@repos.giga.puc-rio.br:elua-doc.git
 - Moving all files and folders to a working doc folder
 - Css updated
 - Index page added and CSS adjusts
 - Merge branch 'master' of git@repos.giga.puc-rio.br:elua-doc.git
 - Signed-off-by: Guilherme Sanchez <guilhermesanchezpacheco@gmail.com>
 - Merge branch 'master' of git@repos.giga.puc-rio.br:elua-doc.git
 - files deleted
 - Changed function that creates functions submenus.
 - Menu inserted with árvore, CSS adjusts, google search
 - Changed past design to new design
 - CSS updated
 - initial import

- The commit ids were also preserved but they are related to this "other"
  work done on Led Lab. We'll keep the repo just in case.

4dce3f77c47b0c3001a2075a946e80ee52759b49 - Removing Portuguese doc content
78d8847525cacf045fe7e672cff6bd1e058a6a4b Ignore folder names fixed on .gitignore
48dee6b7962168ab1098bf709ead6f3cfe6b7964 - Removed doc files which content migrated to the CMS - docdata.lua updated accordingly - Doc build checked ok - Overall doc structure and contents still being refined
2aa2fe0c554db03dbc7029c34d0f4500fe625b37 - Removing folder cache from git versioning - Removing folder dist from git versioning   The folders above are generated by the buildall.lua script and are not part of the sources - Adding .gitignore file with objects info to inform git what to ignore
af6cc2890edf1855af319dc999a03feee5f9bee0 Removed file
6a180e72eb4f4860620cafc0685000e9f2174cfe Merge branch 'master' of git@repos.giga.puc-rio.br:elua-doc.git
eb430112e78ae537459ab315e228ebca84bdf2d4 Moving all files and folders to a working doc folder
d28a7c99489915630bd2625f3756fecf0d08ce37 Css updated
32836ffe382f04ab07c3e6f018c7b449a20d7a8d Index page added and CSS adjusts
1461d9957d9d25a1467cb57ab8717aa213a37e8d Merge branch 'master' of git@repos.giga.puc-rio.br:elua-doc.git
ae1934c04f35a29e25bb4495ae8a31cd9c000b5b Signed-off-by: Guilherme Sanchez <guilhermesanchezpacheco@gmail.com>
b5f31d70f1fac8d3fba325c9867a03f976775698 Merge branch 'master' of git@repos.giga.puc-rio.br:elua-doc.git
ec9ad8446b7ea38b252c6a416e70774349835e45 files deleted
bd7a80151b2030720ba8d8a303467d8c25a4b4b2 Changed function that creates functions submenus.
6a7494acaec694fadbb13520bcbccc51a6b95dfe Inserido menu com árvore, ajustes no CSS, busca do google
e979f1c259d425c9a3be83f9cda20eddffe073bb Changed past design to new design.
381459e95286886b052103a0253e60b29e064d7a CSS updated
4f81d2f1195efe733fe5f97517be325d75937bc3 initial import
2011-05-06 06:49:21 -03:00

265 lines
17 KiB
HTML

$$HEADER$$
<h3>Modules and LTR</h3>
<p>LTR (Lua Tiny RAM) is a Lua patch (written specifically for <b>eLua</b> by Bogdan Marinescu) that significantly decreases the RAM usage of Lua scripts,
thus making it possible to run large Lua programs on systems with limited RAM. This section gives a full description of LTR. If you're writing <b>eLua</b>
modules, this page will certainly be of interest to you, as it shows how to interact with LTR in a portable and easy to configure way.</p>
<h2>Motivation</h2>
<p>The main thing that drove me to write this patch is the relatively high Lua memory consumption at startup (obtained by running
<i>lua -e "print(collectgarbage'count')"</i>). It's about 17k for regular Lua 5.1.4, and more than 25k for some of eLua's platforms. These figures are
mainly a result of registering many different modules to Lua. Each time you register a module (via luaL_register) you create a new table and populate
it with the module's methods. But a table is a read/write datatype, so luaL_register is quite inefficient if you don't plan to do any write operations
on that table later (adding new elements or manipulating existing ones). I found that I almost never have to do any such operations on a module's
table after it was created. I just query it for its elements. So, from the perspective of someone worried about memory usage, I'd rather have a
different type of table in this case, one that wouldn't need any RAM at all, since it would be read only, so it could reside entirely in ROM.</p>
<p>There's one more thing related to this context: Lua's functions. While Lua does have the concept of C functions, they still require data structures
that need to be allocated (see lua_pushcclosure in lapi.c for details), as they can have upvalues or environments. Once again, this isn't something I
use often with eLua. Most of the time my functions (especially the ones exported by a C module) are very simple, and they don't need upvalues or
environments at all. In conclusion, having a "simpler" function type that improves memory usage.
</p>
<h2>Details</h2>
<p>The patch adds two new data types to Lua. Both or them are based on the lightuserdata type already found in Lua, and they share the same basic
attributes: they don't need to be dynamically allocated (as they're just pointers on steroids) and they're compared in the same way lightuserdatas
are compared (by value). And of course, they are not collectable, so the garbage collector won't have anything to do with them. The new types are:</p>
<ol>
<li><b>lightfunctions</b>: these are "simple" functions, in the sense that they can't have upvalues or environments. They are just pointers to regular
C functions. Other than that, you can use them from Lua just as you'd use any other function.</li>
<li><b>rotables</b>: these are read-only tables, but unlike the read-only tables that one can already implement in Lua with metamethods, they have a
very specific property: they don't need any RAM at all. They are fully constant, so they can be read directly from ROM. They have a number of
special features and limitations when compared with a regular table:
<ul>
<li>rotables can only contain values of type "lightfunction", lua_Number or pointers to other rotables.</li>
<li>you can't add/delete/modify elements from rotables (obviously). However, rotables will honour the "__newindex" metamethod.</li>
<li>you can use rotables as metatables for both "regular" tables and for Lua types (via debug.setmetatable)</li>
<li>a rotable can have another rotable (or itself) as a metatable</li>
<li>you can iterate over rotables with pairs/ipairs/next just as you do with "regular" tables.</li>
</ul></li></ol>
<p>Just as with lightuserdata, you can only create lightfunctions and rotables from C code and never from Lua itself.</p>
<h2>Testing</h2>
<p>I tested my patch with the (<a target="_blank" href="http://lua-users.org/lists/lua-l/2006-03/msg00723.html">Lua 5.1 test suite</a>). The test suite
was an excellent testing tool. I thought I had the patch ready until I found the test suite and ran it. After another week of work, I had something
that could be called functional :)</p>
<p>I tested everything via "make generic", which is how I always build Lua for my embedded environments. This means (among other things) that I didn't
test pipes and dynamic module loading, although I don't see why they wouldn't work.</p>
<p>I never tested the patch in a multithreaded environment with threads running different lua_States. I never even used regular Lua like this,
so I can't make assumptions about how my patch would behave in a multithreaded environment. It doesn't use any global or static variables, but you
might encounter other problems with it.</p>
<h2>Results</h2>
<p>The table below summarizes the RAM usage in KBytes (as obtained by running <i>lua -e "print(collectgarbage'count')"</i> from the <b>eLua</b> shell).
<b>OPT=0</b> is LTR's "compatibility mode" (basically this means that the patch is disabled, so you're running plain Lua) and <b>OPT=2</b> is the
patch in action.</p>
<table style="width: 325px;" class="table_center">
<tbody>
<tr>
<th style="text-align: left;">Platform</th>
<th style="text-align: center;">OPT=0</th>
<th style="text-align: center;">OPT=2</th>
</tr>
<tr>
<td>AVR32</td>
<td style="text-align: center;">23.75</td>
<td style="text-align: center;">5.42</td>
</tr>
<tr>
<td>AT91SAM7X</td>
<td style="text-align: center;">25.16</td>
<td style="text-align: center;">5.42</td>
</tr>
<tr>
<td>STR7</td>
<td style="text-align: center;">24.92</td>
<td style="text-align: center;">5.42</td>
</tr>
<tr>
<td>STR9</td>
<td style="text-align: center;">22.23</td>
<td style="text-align: center;">5.42</td>
</tr>
<tr>
<td>LPC2888</td>
<td style="text-align: center;">22.23</td>
<td style="text-align: center;">5.42</td>
</tr>
<tr>
<td>i386</td>
<td style="text-align: center;">16.90</td>
<td style="text-align: center;">5.42</td>
</tr>
<tr>
<td>LM3S</td>
<td style="text-align: center;">27.14</td>
<td style="text-align: center;">5.42</td>
</tr>
</tbody>
</table>
<p>As you can see, the differences are significant, and (more importantly) it doesn't matter how many modules you load in <b>eLua</b>, the RAM consumption
doesn't modify.</p>
<p>Currently, there aren't any performane measurements related to LTR. It's clear from the implementation that the patch slows down the virtual machine,
but a precise performance penalty figure is not known. Experience suggests that the performance penalty is minimal, and it certainly can't be observed
with "regular" (non-computationally intensive) Lua programs.</p>
<h2>How to enable LTR</h2>
<p>Enabling LTR is very easy: all you need to do is specify the <b>optram=1</b> as a parameter to scons when building <b>eLua</b>, as explained
<a href="building.html">here</a>. You don't even need to specify this explicitly, as LTR is enabled by default for all <b>eLua</b> targets.</p>
<p>When <b>optram</b> is 0, LTR is not active. In this mode, the patch just tries to keep the modified version as close as possible to the unpatched version
in terms of speed and functionality. You might want to use this if you want full Lua compatibility (although this is rarely an issue in practice),
or need to overcome the read-only limitations of rotables (but check <a href="faq.html#rotables">this</a> first). If your program behaves weird and you
suspect that LTR might be the cause of your problems, recompiling with <b>optram=0</b> is a quick way to eliminate or confirm your suspicions.</p>
<p>When <b>optram</b> is 1 (default), all the LTR optimizations are enabled. The implementation of the Lua standard libraries is modified to take advantage
of the new datatypes. In particular, the IO library is modified to use the registry instead of environments, thus making it more resource-friendly,
the side effect being that this mode doesn't support pipes in the <b>io</b> module (which isn't an issue for <b>eLua</b>).
It also leaves the <b>_G</b> (globals) table with a single method (<i>__index</i>) and sets it as its own metatable, so all accesses to globals are
now sligthly slower because of the <i>__index</i> metamethod call.</p>
<h2>Writing LTR-compatible modules</h2>
<p>The LTR patch introduces a specific method for writing modules in such a way that they're fully compatible with both <b>optram=0</b> and <b>optram=1</b>.
If you're writing a new <b>eLua</b> module you should use this method, as it keeps code coherency. </p>
<p>We'll show this method using a simple example. Let's assume that you want to register a simple module called "mod" that has a single function named "f".
For regular Lua, you'd do something like this:</p>
<p><pre><code>static const luaL_reg mod_map[] =
{
{ "f", f_implementation },
{ NULL, NULL }
};
LUALIB_API int luaopen_mod( lua_State *L )
{
luaL_register( L, "mod", mod_map );
return 1;
}</code></pre></p>
<p>For the rotables implementation, however, you'd need to define the same thing like this:</p>
<pre><code>const luaR_entry mod_map[] = <span class="warning">// note: no static this time</span>
{
{ LRO_STRKEY( "f" ), LRO_FUNCVAL( f_implementation ) },
{ LRO_NILKEY, LRO_NILVAL }
};
<span class="warning">// note: in this case the "luaopen_mod" function isn't really needed anymore</span>
LUALIB_API int luaopen_mod( lua_State *L )
{
return 0;
}</code></pre>
<p>A few points about the rotables example above:</p>
<ul>
<li>a rotable needs a "map" (mod_map) array much like a regular module, but you need to define that array with special macros:
<ul>
<li><b>for keys</b>: <b>LRO_STRKEY("str")</b> defines a string key, <b>LRO_NUMKEY(n)</b> defines an integer key, and <b>LRO_NILKEY</b> defines a NULL
(empty) key</li>
<li><b>for values</b>: <b>LRO_FUNCVAL(f)</b> defines a lightfunction value, <b>LRO_NUMVAL(f)</b> defines a number value, <b>LRO_RO(p)</b> defines a
rotable value (p is the pointer to the rotable) and <b>LRO_NILVAL</b> defines a NULL (empty) value.</li>
</ul></li>
<li>all the "global" rotables in the system (the ones that must be visible from <b>_G</b>, like the rotables of all the modules exported to Lua) must be
included in a special array, called <b>lua_rotable</b> (defined in <i>linit.c</i>). Simply including the rotable's definition array (mod_map in this case)
in the lua_rotable array makes it visible globally, thus you don't need to call any kind of register function. This is why <b>luaopen_mod</b> now returns
0.</li>
</ul>
<p>The two forms above (for regular tables and for rotables) are clearly different, but we want to keep them both to be able to work at both <b>optram=0</b>
and <b>optram=1</b>. You can use #ifdefs to differentiate between the two cases in different optimization levels, but this becomes really annoying after
a (short) while. This is why I added another file called <b>lrodefs.h</b> (<i>src/lua</i>) that can be used to give an "universal" definition to our map
arrays. Here's how our example looks after rewriting it to take advantage of <b>lrodefs.h</b>:</p>
<pre><code><span class="warning">#define MIN_OPT_LEVEL 2 // the minimum optimization level at which we use rotables</span>
#include "lrodefs.h"
const <span class="warning"> LUA_REG_TYPE</span> mod_map[] = <span class="warning">// note: no more luaL_reg or luaR_entry</span>
{
{ LSTRKEY( "f" ), LFUNCVAL( f_implementation ) },
{ LNILKEY, LNILVAL }
};
// <span class="warning">note: no more LRO_something, just Lsomething (for example LRO_STRKEY becomes LSTRKEY)</span>
LUALIB_API int luaopen_mod( lua_State *L )
{
<span class="warning">LREGISTER</span>( L, "mod", mod_map ); // <span class="warning">note: no more luaL_register, no "return 1"</span>
}</code></pre>
<p>Now, if <b>LUA_OPTIMIZE_MEMORY</b> (a macro defined by the system as 0 when <b>optram=0</b> and as 2 when <b>optram=1</b>) is less than
<b>MIN_OPT_LEVEL</b>, the above definition will compile in its "regular table" format. If <b>LUA_OPTIMIZE_MEMORY</b> is 2, it compiles to the
rotables format. Problem solved :) <b>LREGISTER</b> will also take care of calling <b>luaL_register</b> and return 1 when <b>optram=0</b> and do
absolutely nothing when <b>optram=1</b>. You can see more examples of this in any module from <i>src/modules</i>, and you're encouraged to do so,
as this is only a very basic example; <i>src/modules</i> contains real life examples that can serve as a good basis for a new module.</p>
<p>As you know by now, rotables can have metatables, and also you can set a rotable as a metatable for a regular table. If a rotable must have a
metatable, then it needs a "__metatable" field to point to its metatable (which is also a rotable, not necessarily another rotable) and the usual
metatable functions. For example, let's make our <b>mod</b> rotable its own metatable and declare an <b>__index</b> function. Moreover, let's do
this for both <b>optram=0</b> and <b>optram=1</b>.</p>
<pre><code>static int mod_mt_index( lua_State *L )
{
return 0;
}
#define MIN_OPT_LEVEL 2 // the minimum optimization level at which we use rotables
#include "lrodefs.h"
const LUA_REG_TYPE mod_map[] =
{
{ LSTRKEY( "f" ), LFUNCVAL( f_implementation ) },
<span class="warning">#if LUA_OPTIMIZE_MEMORY &gt; 0
{ LSTRKEY( "__metatable" ), LROVAL( mod_map ) },
#endif</span>
{ LSTRKEY( "__index" ), LFUNCVAL( mod_mt_index) },
{ LNILKEY, LNILVAL };
};
LUALIB_API int luaopen_mod( lua_State *L )
{
#if LUA_OPTIMIZE_MEMORY &gt; 0
return 0;
#else
luaL_register( L, "mod", mod_map );
<span class="warning">// Set "mod" as its own metatable
lua_pushvalue( L, -1 );
lua_setmetatable( L, -2 );</span>
return 1;
#endif
}</code></pre>
<p>If you want to register a module using a regular Lua table, but use lightfunctions instead of regular functions, use <i>luaL_register_light</i> instead
of <i>luaL_register</i> (same syntax). </p>
<p>More important things to keep in mind when working with LTR:</p>
<ul>
<li>currently, <b>MIN_OPT_LEVEL</b> should be always set to 2</li>
<li>you need a C99-compatible compiler to use LTR (because of the compile-time explicit union initialization that's needed to declare const rotables).
Fortunately this isn't a issue right now, as all current eLua targets use GCC and GCC knows how to handle this.</li>
<li>your linker command file should export two symbols: <b>stext</b> and <b>etext</b>. They should be declared before and after the .rodata* section
placement (generally you'd declare stext at the beginning of .text definition and etext and the end of .text definition, see for example
<i>src/lua/at91sam7x256/flash256.lds</i>). These are needed by the patch to differentiate between a regular table and a rotable (although this is likely
to change in a future version of the patch.</li>
<li><span class="warning">remember to declare all your rotable's definition array as 'const'!!</span> Forgetting to do so will not only increase
memory usage, it will also make the patch nonfunctional, because of the way it recognizes rotables (see above).</li>
</ul>
<a name="config" /><h2>LTR and module configuration at build time</h2>
<p>With unpatched Lua, you can specify what modules to be part of the Lua image by modifying <i>src/lua/linit.c</i>. In the particular case of <b>eLua</b>,
one has to declare a list of the modules that must be compiled in <i>src/platform/&lt;name&gt;/platform_conf.h</i> like this:</p>
<pre><code>#define LUA_PLATFORM_LIBS\
{ AUXLIB_PIO, luaopen_pio },\
{ AUXLIB_TMR, luaopen_tmr },\
{ AUXLIB_PD, luaopen_pd },\
{ AUXLIB_UART, luaopen_uart },\
{ AUXLIB_TERM, luaopen_term },\
{ AUXLIB_PWM, luaopen_pwm },\
{ AUXLIB_PACK, luaopen_pack },\
{ AUXLIB_BIT, luaopen_bit },\
{ LUA_MATHLIBNAME, luaopen_math }
</code></pre>
<p>Things are a bit more complex with LTR but not by much. The list of modules that must be compiled is declared via a preprocessor macro in
<i>src/platform/&lt;name&gt;/platform_conf.h</i> and it looks like this:</p>
<pre><code>#define LUA_PLATFORM_LIBS_ROM\
_ROM( AUXLIB_PIO, luaopen_pio, pio_map )\
_ROM( AUXLIB_TMR, luaopen_tmr, tmr_map )\
_ROM( AUXLIB_PD, luaopen_pd, pd_map )\
_ROM( AUXLIB_UART, luaopen_uart, uart_map )\
_ROM( AUXLIB_TERM, luaopen_term, term_map )\
_ROM( AUXLIB_PWM, luaopen_pwm, pwm_map )\
_ROM( AUXLIB_PACK, luaopen_pack, pack_map )\
_ROM( AUXLIB_BIT, luaopen_bit, bit_map )\
_ROM( LUA_MATHLIBNAME, luaopen_math, math_map )</code></pre>
<p>(<b>IMPORTANT NOTE</b>: the fact that there are no commas between two different _ROM declarations (as seen above) is NOT an error;
on the contrary, this is intentional. Try using commas and you'll get in trouble very soon :) ).</p>
<p>Note the 3rd parameter of the <b>_ROM</b> macro, which is the name of the definition array for the (ro)table. That's it. The code in linit.c will take
care of everything else, including initializing the list of modules in LUA_PLATFORM_LIBS_ROM with regular tables instead of rotables at <b>optram=0</b>
(to maintain compatibility with regular Lua). You can also have a list of modules that you want to use with regular tables no matter what the
optimization level is. In that case, list it in the <b>LUA_PLATFORM_LIBS_REG</b> macro via the old syntax for <b>LUA_PLATFORM_LIBS</b>, as shown
above (the regular Lua syntax for defining a module to be registered with luaL_register). If you want this module to use lightfunctions instead of
regular functions (at <b>optram=1</b>), use <i>luaL_register_light</i> instead of <i>luaL_register</i>.
</p>
$$FOOTER$$