技術ノート 1ウェルカムページで明示的に述べられているように、Lua の実装の目標の一つは、低い組み込みコストです。これは、2つのことを意味します。1つ目は、Luaをアプリケーションに簡単に組み込むことができること、2つ目は、Luaの追加コードが大きすぎないことです。
最初の要件は、LuaのC APIの単純さによって満たされています。2番目の要件は、次のデモンストレーションによって満たされます。
以下は、Linux を実行している Intel マシンでコンパイルされた Lua 3.2 のいくつかの数値です (他のプラットフォームでの数値は異なるでしょうが、相対的にはほぼ同じでしょう)。
% size liblua.a liblualib.a
text data bss dec hex filename
4483 121 0 4604 11fc lapi.o (ex liblua.a)
1037 0 0 1037 40d lauxlib.o (ex liblua.a)
345 0 0 345 159 lbuffer.o (ex liblua.a)
5193 272 0 5465 1559 lbuiltin.o (ex liblua.a)
3183 0 0 3183 c6f ldo.o (ex liblua.a)
381 0 0 381 17d lfunc.o (ex liblua.a)
1363 0 0 1363 553 lgc.o (ex liblua.a)
5429 108 0 5537 15a1 llex.o (ex liblua.a)
222 0 0 222 de lmem.o (ex liblua.a)
686 156 0 842 34a lobject.o (ex liblua.a)
8560 244 0 8804 2264 lparser.o (ex liblua.a)
446 4 0 450 1c2 lstate.o (ex liblua.a)
1845 36 0 1881 759 lstring.o (ex liblua.a)
1109 0 0 1109 455 ltable.o (ex liblua.a)
1293 202 0 1495 5d7 ltm.o (ex liblua.a)
2035 0 0 2035 7f3 lundump.o (ex liblua.a)
4864 8 0 4872 1308 lvm.o (ex liblua.a)
336 0 0 336 150 lzio.o (ex liblua.a)
25 0 0 25 19 linit.o (ex liblualib.a)
1489 56 0 1545 609 ldblib.o (ex liblualib.a)
4236 264 0 4500 1194 liolib.o (ex liblualib.a)
1651 184 0 1835 72b lmathlib.o (ex liblualib.a)
5277 88 0 5365 14f5 lstrlib.o (ex liblualib.a)
このリストでは、text は実際にコードのサイズをバイト単位で表します。Lua コア (liblua.a) は 42810 バイト、Lua 標準ライブラリ (liblualib.a) は 12678 バイトであることがわかります。つまり、Lua コード全体で 55488 バイト、つまり 54K 未満です。言い換えれば、Lua がアプリケーションに与える影響は、54K の追加コードであり、これはかなり小さいものです。(もちろん、Lua は実行時にメモリを使用しますが、その量はアプリケーションによって異なります。)最近のマシンはメインメモリを何メガバイトも搭載しているので、54K は非常に小さいように思えますが、電子レンジやロボットで Lua を使用しようとしている人にとっては違いが生じる可能性があります。それでは、この 54K をさらに減らす方法を見てみましょう。(組み込みシステムで Lua を使用していない場合でも、以下の説明から何かを学ぶことができるかもしれません。)
最初にすべきことは、不要な標準ライブラリをすべて削除することです。たとえば、ldblib.o はほとんどのアプリケーションでは不要でしょう。しかし、標準ライブラリを削除しても、それらはすでに小さいので、あまり進展はありません。そこで、コアの数値をもう一度見てみましょう。ただし、今度はサイズでソートします。
text %core %whole filename 222 1% 0% lmem.o 336 1% 1% lzio.o 345 1% 1% lbuffer.o 381 1% 1% lfunc.o 446 1% 1% lstate.o 686 2% 1% lobject.o 1037 2% 2% lauxlib.o 1109 3% 2% ltable.o 1293 3% 2% ltm.o 1363 3% 2% lgc.o 1845 4% 3% lstring.o 2035 5% 4% lundump.o 3183 7% 6% ldo.o 4483 10% 8% lapi.o 4864 11% 9% lvm.o 5193 12% 9% lbuiltin.o 5429 13% 10% llex.o 8560 20% 15% lparser.oこのリストから、字句解析器 (
llex.o) とパーサー (lparser.o) がコアの 33% (全体では 25%) を占めていることがわかります。したがって、これらは削除の主な候補です。実行時に Lua コードをコンパイルする必要がないアプリケーションには、字句解析器とパーサーは必要ありません。コードは、これらの 2 つのモジュールを簡単に削除できるように設計されています。パーサーを呼び出すモジュールは 1 つだけで (ldo.o)、パブリック関数は 1 つだけ (luaY_parser) です。字句解析器を呼び出すモジュールは、lua_open で使用される初期化関数 (luaX_init) を除き、パーサーだけです。したがって、字句解析器とパーサーを削除するには、以下のコードをアプリケーションに追加するだけで済みます(デフォルトで無効になっている lua/src/luac/stubs.c から抽出できます)。
#include "llex.h"
#include "lparser.h"
void luaX_init(void){}
TProtoFunc* luaY_parser(ZIO *z) {
lua_error("parser not loaded");
return NULL;
}
このコードを含むアプリケーションは、llex.o または lparser.o にリンクせず、Lua ソースコードをロードしようとするとエラーが発生します。しかし、それでは、アプリケーションはどのようにして Lua コードをロードするのでしょうか?答えは、ソースコードの代わりにプリコンパイルされたチャンクをロードすることです。プリコンパイルされたチャンクは luac で作成されます。luac は字句解析器とパーサーを *含んでいますが、外部アプリケーションです。プリコンパイルされたチャンクをロードするモジュールは lundump.o で、これは十分に小さいです。
lua_dofile と dofile はプリコンパイルされたチャンクを自動的に検出しますが、便利な方法の 1 つは、プリコンパイルされたチャンクをアプリケーションに静的にリンクして lua_dobuffer を使用することです (このためには lua/etc/bin2c.c が役立ちます)。組み込みシステムにはファイルシステムすらないからです。(これは高速なソリューションですが、アプリケーションのサイズが増加し、柔軟性に欠ける可能性があります。)
字句解析器とパーサーを削除すると、コアはわずか 28821 バイト、つまり 28K 強になります。Lua のような強力な言語にしては、これは非常に小さいです!この削減はソースコードに触れることなく行われたことにも注意してください。リンカーからの少しの助けが必要なだけです。
削除のもう 1 つの候補は、組み込み関数を含む lbuiltin.o です。標準ライブラリと同様に、スリムな Lua を必要とするアプリケーションは、本当に必要な組み込み関数を検討する必要があります。lbuiltin.c を調べて、不要な関数を削除するのは簡単です。それらはコメント内の対応する {...} でマークされたブロックに分割されているため、簡単に識別できます。組み込み関数がまったく必要ない場合は、最も簡単な方法は以下を追加することです。
#include "lbuiltin.h"
void luaB_predefine(void){}
上記のスタブコードに追加し、lbuiltin.o をロードしないようにリンカーに頼ることです。このノートでは、Lua ライブラリによってアプリケーションに追加されるコードの量を減らすことに焦点を当ててきました。これを必要とするアプリケーションは、Lua の数値に浮動小数点数ではなく整数を使用することも好むでしょう。(電子レンジに浮動小数点数が必要でしょうか?)これは lua/config で説明されているように簡単に行うことができますが、詳細は別の LTN で議論されるでしょう。