12 分くらいで知るluavm
DESCRIPTION
第三回カーネル/VM探検隊でLuaVMに関してLTをした資料ですTRANSCRIPT
LuaVM1 2 分 く ら い で 知 る L u a V M
2010年2月24日水曜日
自己紹介
blog: 神様なんて信じない僕らのためにhatena: Isoparametrictwitter: isoparametric
name:イソッパ(http://d.hatena.ne.jp/Isoparametric/)
2010年2月24日水曜日
Lua?
カーネルとか仮想化技術とは関係ありません。ごめんなさい、スクリプト言語です
Lua知らない人はググってください
一応説明しておくと、ブラジルで生まれた軽量で最速で最小で柔軟な組み込みに向いたLL言語です!(ANSI Cだけで記述)
2010年2月24日水曜日
なんでLuaVM?単にプロジェクトでLuaを採用したから!
LuaとC系自作オリジナル言語(ただし、四則演算、if、forしかないプロトタイプ)を比較したらLuaの方が速かったから!
まぁ、Luaすげーなって思ったって、ことですよ
レジスタベースVMにも興味がありました
2010年2月24日水曜日
今日の話
Luaのインストラクションセット
レジスタベースVMとスタックベースVM
その他
2010年2月24日水曜日
海外の人曰くI think that one cannot completely grok a scripting language, or any complex system for that matter, without slitting the animal open and examining the entrails, organs and other yucky stuff that isn’t normally seen.
私は、動物を切り裂いて、内蔵、器官、その他不潔なものを調べずに、人がスクリプト言語、またはどんな複合システムをも完全に理解できるわけがないと思うの。
2010年2月24日水曜日
LuaVMの素敵レジスタベースVM(5.0から)
中間コードによる最適化有り
Windowsでコンパイルして、組み込み機器で動作ということが簡単
パーサを切り離せるので、組み込みコードサイズが凄く小さい
2010年2月24日水曜日
LuaVMの注意事項
内部構造は全く標準化されていないし、する気もない。ので、ユーザは内部構造に依存したコードを書いてはいけない。
故に、後にも先にも互換性を期待できない。
2010年2月24日水曜日
VMのためのCPU用語集オペコード……CPU命令の番号のことオペランド……データが入っているメモリアドレス、あるいはジャンプ命令での飛び先、演算結果アドレス、ないしは定数データ部……プログラム内部で利用するデータが記録されている場所。命令にオペランドがある場合、ここまでジャンプして、データを取得/記録するPC……プログラムカウンタ。現在実行位置
釈迦に説法な
2010年2月24日水曜日
VMのためのCPU用語集オペコード……CPU命令の番号のことオペランド……データが入っているメモリアドレス、あるいはジャンプ命令での飛び先、演算結果アドレス、ないしは定数データ部……プログラム内部で利用するデータが記録されている場所。命令にオペランドがある場合、ここまでジャンプして、データを取得/記録するPC……プログラムカウンタ。現在実行位置
釈迦に説法な
テストにでる!
2010年2月24日水曜日
ザザッと見ようインストラクションセット38命令!(5.1.4時点)
R(X)はX番目のレジスタを意味
K(X)はX番目の定数を意味
RK(X)はR(X)か、K(X-k)を意味レジスタか定数と思って!(定数は負数で指すの)
/*----------------------------------------------------------------------name args description------------------------------------------------------------------------*/OP_MOVE,/* A B R(A) := R(B) */OP_LOADK,/* A Bx R(A) := Kst(Bx) */OP_LOADBOOL,/* A B C R(A) := (Bool)B; if (C) pc++ */OP_LOADNIL,/* A B R(A) := ... := R(B) := nil */OP_GETUPVAL,/* A B R(A) := UpValue[B] */OP_GETGLOBAL,/* A Bx R(A) := Gbl[Kst(Bx)] */OP_GETTABLE,/* A B C R(A) := R(B)[RK(C)] */OP_SETGLOBAL,/* A Bx Gbl[Kst(Bx)] := R(A) */OP_SETUPVAL,/* A B UpValue[B] := R(A) */OP_SETTABLE,/* A B C R(A)[RK(B)] := RK(C) */OP_NEWTABLE,/* A B C R(A) := {} (size = B,C) */OP_SELF,/* A B C R(A+1) := R(B); R(A) := R(B)[RK(C)] */OP_ADD,/* A B C R(A) := RK(B) + RK(C) */OP_SUB,/* A B C R(A) := RK(B) - RK(C) */OP_MUL,/* A B C R(A) := RK(B) * RK(C) */OP_DIV,/* A B C R(A) := RK(B) / RK(C) */OP_MOD,/* A B C R(A) := RK(B) % RK(C) */OP_POW,/* A B C R(A) := RK(B) ^ RK(C) */OP_UNM,/* A B R(A) := -R(B) */OP_NOT,/* A B R(A) := not R(B) */OP_LEN,/* A B R(A) := length of R(B) */OP_CONCAT,/*A B C R(A) := R(B).. ... ..R(C) */OP_JMP,/* sBx pc+=sBx */OP_EQ,/* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */OP_LT,/* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */OP_LE,/* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */OP_TEST,/* A C if not (R(A) <=> C) then pc++ */ OP_TESTSET,/* A B C if (R(B) <=> C) then R(A) := R(B) else pc++ */ OP_CALL,/* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */OP_TAILCALL,/* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */OP_RETURN,/*A B return R(A), ... ,R(A+B-2) (see note) */OP_FORLOOP,/* A sBx R(A)+=R(A+2); if R(A) <?= R(A+1) then { pc+=sBx; R(A+3)=R(A) }*/OP_FORPREP,/* A sBx R(A)-=R(A+2); pc+=sBx */OP_TFORLOOP,/* A C R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2)); if R(A+3) ~= nil then R(A+2)=R(A+3) else pc++*/ OP_SETLIST,/* A B C R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B */OP_CLOSE,/* A close all variables in the stack up to (>=) R(A)*/OP_CLOSURE,/* A Bx R(A) := closure(KPROTO[Bx], R(A), ... ,R(A+n)) */OP_VARARG/* A B R(A), R(A+1), ..., R(A+B-1) = vararg */
2010年2月24日水曜日
ザザッと見ようインストラクションセット38命令!(5.1.4時点)
R(X)はX番目のレジスタを意味
K(X)はX番目の定数を意味
RK(X)はR(X)か、K(X-k)を意味レジスタか定数と思って!(定数は負数で指すの)
/*----------------------------------------------------------------------name args description------------------------------------------------------------------------*/OP_MOVE,/* A B R(A) := R(B) */OP_LOADK,/* A Bx R(A) := Kst(Bx) */OP_LOADBOOL,/* A B C R(A) := (Bool)B; if (C) pc++ */OP_LOADNIL,/* A B R(A) := ... := R(B) := nil */OP_GETUPVAL,/* A B R(A) := UpValue[B] */OP_GETGLOBAL,/* A Bx R(A) := Gbl[Kst(Bx)] */OP_GETTABLE,/* A B C R(A) := R(B)[RK(C)] */OP_SETGLOBAL,/* A Bx Gbl[Kst(Bx)] := R(A) */OP_SETUPVAL,/* A B UpValue[B] := R(A) */OP_SETTABLE,/* A B C R(A)[RK(B)] := RK(C) */OP_NEWTABLE,/* A B C R(A) := {} (size = B,C) */OP_SELF,/* A B C R(A+1) := R(B); R(A) := R(B)[RK(C)] */OP_ADD,/* A B C R(A) := RK(B) + RK(C) */OP_SUB,/* A B C R(A) := RK(B) - RK(C) */OP_MUL,/* A B C R(A) := RK(B) * RK(C) */OP_DIV,/* A B C R(A) := RK(B) / RK(C) */OP_MOD,/* A B C R(A) := RK(B) % RK(C) */OP_POW,/* A B C R(A) := RK(B) ^ RK(C) */OP_UNM,/* A B R(A) := -R(B) */OP_NOT,/* A B R(A) := not R(B) */OP_LEN,/* A B R(A) := length of R(B) */OP_CONCAT,/*A B C R(A) := R(B).. ... ..R(C) */OP_JMP,/* sBx pc+=sBx */OP_EQ,/* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */OP_LT,/* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */OP_LE,/* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */OP_TEST,/* A C if not (R(A) <=> C) then pc++ */ OP_TESTSET,/* A B C if (R(B) <=> C) then R(A) := R(B) else pc++ */ OP_CALL,/* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */OP_TAILCALL,/* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */OP_RETURN,/*A B return R(A), ... ,R(A+B-2) (see note) */OP_FORLOOP,/* A sBx R(A)+=R(A+2); if R(A) <?= R(A+1) then { pc+=sBx; R(A+3)=R(A) }*/OP_FORPREP,/* A sBx R(A)-=R(A+2); pc+=sBx */OP_TFORLOOP,/* A C R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2)); if R(A+3) ~= nil then R(A+2)=R(A+3) else pc++*/ OP_SETLIST,/* A B C R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B */OP_CLOSE,/* A close all variables in the stack up to (>=) R(A)*/OP_CLOSURE,/* A Bx R(A) := closure(KPROTO[Bx], R(A), ... ,R(A+n)) */OP_VARARG/* A B R(A), R(A+1), ..., R(A+B-1) = vararg */
2010年2月24日水曜日
命令 用途
MOVE
LOADK
LOADBOOL
LOADNIL
GETUPVAL
GETGLOBAL
GETTABLE
SETGLOBAL
SETUPVAL
SETTABLE
レジスタ間の値コピー
定数をレジスタにロード
論理値をレジスタにロード
nilを指定範囲のレジスタにロード
上位値の値をレジスタにロード
グローバルの値をレジスタにロード
テーブル要素をレジスタにロード
レジスタの値をグローバルにセット
レジスタの値を上位値にセット
レジスタの値をテーブルにセット
2010年2月24日水曜日
命令 用途
NEWTABLE
SELF
ADD
SUB
MUL
DIV
MOD
POW
UNM
NOT
新しいテーブルを生成
オブジェクトメソッドを呼ぶ準備
加算
減算
乗算
除算
剰余算
指数演算
単項マイナス
否定演算
2010年2月24日水曜日
命令 用途
LEN
CONCAT
JMP
EQ
LT
LE
TEST
TESTSET
CALL
TAILCALL
長さ演算
レジスタ範囲内の要素を文字列連結
無条件ジャンプ
等価テスト&条件付きジャンプ
より小さいかテスト&条件付きジャンプ
以下テスト&条件付きジャンプ
論理値テスト&条件付きジャンプ
論理値テスト&条件付きジャンプ&代入
関数呼び出し
末尾関数呼び出し
2010年2月24日水曜日
命令 用途
RETURN
FORLOOP
FORPREP
TFORLOOP
SETLIST
CLOSE
CLOSURE
VARARG
関数から戻る
数値forループ(FORPREPとセット)
数値forループ初期化
ジェネリックforループ
配列の要素をテーブルにセット
一部の上位値のローカル変数を閉じる
クロージャを生成
可変長引数をレジスタに割り当て
2010年2月24日水曜日
インストラクションレイアウト0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
OP A B C
OP A Bx
OP A sBx
Figure 6: Instruction layout
function max (a,b)
local m = a 1 MOVE 2 0 0 ; R(2) = R(0)
if b > a then 2 LT 0 0 1 ; R(0) < R(1) ?
m = b 3 JMP 1 ; to 5 (4+1)
end 4 MOVE 2 1 0 ; R(2) = R(1)
return m 5 RETURN 2 2 0 ; return R(2)
end 6 RETURN 0 1 0 ; return
Figure 7: Bytecode for a Lua function
a register or a constant (using the representation RK(X) explained above). Withthis format, several typical operations in Lua can be coded in a single instruction.For instance, the increment of a local variable, such as a = a + 1, is codedas ADD x x y, where x represents the register holding the local variable and y
represents the constant 1. An assignment like a = b.f, when both a and b arelocal variables, is also coded as the single instruction GETTABLE x y z, where x
is the register for a, y is the register for b, and z is the index of the stringconstant "f". (In Lua, the syntax b.f is syntactic sugar for b["f"], that is, bindexed by the string "f".)
Branch instructions pose a di!culty because they need to specify two operandsto be compared plus a jump o"set. Packing all this data inside a single instruc-tion would limit jump o"sets to 256 (assuming a signed 9-bit field). The solutionadopted in Lua is that, conceptually, a test instruction simply skips the next in-struction when the test fails; this next instruction is a regular jump, which usesan 18-bit o"set. Actually, because a test instruction is always followed by a jumpinstruction, the interpreter executes both instructions together. That is, whenexecuting a test instruction that succeeds, the interpreter immediately fetchesthe next instruction and does the jump, instead of doing it in the next dispatchcycle. Figure 7 shows an example of Lua code and the corresponding bytecode.Note the structure of the conditional and jump instructions just described.
Figure 8 shows a small sample of the optimizations performed by the Luacompiler. Figure 9 shows the same code compiled for Lua 4.0, which used a stack-
OPコードは6bit
その他はオペランドです。フィールドAは常にあって8bit
フィールドのBとCは9bitですが、18bitとしても使われます。Bx(unsigned) or sBx(signed)
2010年2月24日水曜日
関数で追ってみる
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
OP A B C
OP A Bx
OP A sBx
Figure 6: Instruction layout
function max (a,b)
local m = a 1 MOVE 2 0 0 ; R(2) = R(0)
if b > a then 2 LT 0 0 1 ; R(0) < R(1) ?
m = b 3 JMP 1 ; to 5 (4+1)
end 4 MOVE 2 1 0 ; R(2) = R(1)
return m 5 RETURN 2 2 0 ; return R(2)
end 6 RETURN 0 1 0 ; return
Figure 7: Bytecode for a Lua function
a register or a constant (using the representation RK(X) explained above). Withthis format, several typical operations in Lua can be coded in a single instruction.For instance, the increment of a local variable, such as a = a + 1, is codedas ADD x x y, where x represents the register holding the local variable and y
represents the constant 1. An assignment like a = b.f, when both a and b arelocal variables, is also coded as the single instruction GETTABLE x y z, where x
is the register for a, y is the register for b, and z is the index of the stringconstant "f". (In Lua, the syntax b.f is syntactic sugar for b["f"], that is, bindexed by the string "f".)
Branch instructions pose a di!culty because they need to specify two operandsto be compared plus a jump o"set. Packing all this data inside a single instruc-tion would limit jump o"sets to 256 (assuming a signed 9-bit field). The solutionadopted in Lua is that, conceptually, a test instruction simply skips the next in-struction when the test fails; this next instruction is a regular jump, which usesan 18-bit o"set. Actually, because a test instruction is always followed by a jumpinstruction, the interpreter executes both instructions together. That is, whenexecuting a test instruction that succeeds, the interpreter immediately fetchesthe next instruction and does the jump, instead of doing it in the next dispatchcycle. Figure 7 shows an example of Lua code and the corresponding bytecode.Note the structure of the conditional and jump instructions just described.
Figure 8 shows a small sample of the optimizations performed by the Luacompiler. Figure 9 shows the same code compiled for Lua 4.0, which used a stack-
2010年2月24日水曜日
関数で追ってみる
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
OP A B C
OP A Bx
OP A sBx
Figure 6: Instruction layout
function max (a,b)
local m = a 1 MOVE 2 0 0 ; R(2) = R(0)
if b > a then 2 LT 0 0 1 ; R(0) < R(1) ?
m = b 3 JMP 1 ; to 5 (4+1)
end 4 MOVE 2 1 0 ; R(2) = R(1)
return m 5 RETURN 2 2 0 ; return R(2)
end 6 RETURN 0 1 0 ; return
Figure 7: Bytecode for a Lua function
a register or a constant (using the representation RK(X) explained above). Withthis format, several typical operations in Lua can be coded in a single instruction.For instance, the increment of a local variable, such as a = a + 1, is codedas ADD x x y, where x represents the register holding the local variable and y
represents the constant 1. An assignment like a = b.f, when both a and b arelocal variables, is also coded as the single instruction GETTABLE x y z, where x
is the register for a, y is the register for b, and z is the index of the stringconstant "f". (In Lua, the syntax b.f is syntactic sugar for b["f"], that is, bindexed by the string "f".)
Branch instructions pose a di!culty because they need to specify two operandsto be compared plus a jump o"set. Packing all this data inside a single instruc-tion would limit jump o"sets to 256 (assuming a signed 9-bit field). The solutionadopted in Lua is that, conceptually, a test instruction simply skips the next in-struction when the test fails; this next instruction is a regular jump, which usesan 18-bit o"set. Actually, because a test instruction is always followed by a jumpinstruction, the interpreter executes both instructions together. That is, whenexecuting a test instruction that succeeds, the interpreter immediately fetchesthe next instruction and does the jump, instead of doing it in the next dispatchcycle. Figure 7 shows an example of Lua code and the corresponding bytecode.Note the structure of the conditional and jump instructions just described.
Figure 8 shows a small sample of the optimizations performed by the Luacompiler. Figure 9 shows the same code compiled for Lua 4.0, which used a stack-
引数はローカル変数なので、レジスタにマッピングされる。この場合なら、R(0) == a, R(1) == b
LTは条件が成立なら次がジャンプ命令なのでジャンプ不成立ならPC++なので、直後のJMP命令を飛ばす
OP_LT,/* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */2010年2月24日水曜日
小粋な分岐比較時に2つのオペランドを指定すると、分岐命令はちょっと困難になります。
1つの命令で全データを保持しようとすると、ジャンプオフセットは256に制限されてしまうので(B/Cの9bit==1bitは符号)
Luaはテストが成功したら、次にジャンプ命令を置くようにしました。これなら、18bitのオフセットが使えます(131071まで)
2010年2月24日水曜日
という人も、VMが分からなくてもLuaは使えるので
気にしないでね!VMが気になってきたら、
ソースコードを書いてlua -lで出力できるよ2010年2月24日水曜日
Luaの歴史1.0 1.1 2.1 2.2 2.4 2.5 3.0 3.1 3.2 4.0 5.0 5.1
constructors • • • • • • • • • • • •garbage collection • • • • • • • • • • • •extensible semantics ◦ ◦ • • • • • • • • • •support for OOP ◦ ◦ • • • • • • • • • •long strings ◦ ◦ ◦ • • • • • • • • •debug API ◦ ◦ ◦ • • • • • • • • •external compiler ◦ ◦ ◦ ◦ • • • • • • • •vararg functions ◦ ◦ ◦ ◦ ◦ • • • • • • •pattern matching ◦ ◦ ◦ ◦ ◦ • • • • • • •conditional compilation ◦ ◦ ◦ ◦ ◦ ◦ • • • ◦ ◦ ◦anonymous functions, closures ◦ ◦ ◦ ◦ ◦ ◦ ◦ • • • • •debug library ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ • • • •multi-state API ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ • • •for statement ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ • • •long comments ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ • •full lexical scoping ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ • •booleans ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ • •coroutines ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ • •incremental garbage collection ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ •module system ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ •
1.0 1.1 2.1 2.2 2.4 2.5 3.0 3.1 3.2 4.0 5.0 5.1libraries 4 4 4 4 4 4 4 4 5 6 8 9built-in functions 5 7 11 11 13 14 25 27 35 0 0 0API functions 30 30 30 30 32 32 33 47 41 60 76 79vm type (stack × register) S S S S S S S S S S R Rvm instructions 64 65 69 67 67 68 69 128 64 49 35 38keywords 16 16 16 16 16 16 16 16 16 18 21 21other tokens 21 21 23 23 23 23 24 25 25 25 24 26
Table 1. The evolution of features in Lua.
Lua was becoming a mature product and needed stability forthe benefit of its growing community. Nevertheless, the needfor stability has not hindered progress. Major new versionsof Lua, such as Lua 4.0 and Lua 5.0, have been released sincethen.
The long times between versions also reflects our releasemodel. Unlike other open-source projects, our alpha versionsare quite stable and beta versions are essentially final, exceptfor uncovered bugs.2 This release model has proved to begood for Lua stability. Several products have been shippedwith alpha or beta versions of Lua and worked fine. How-ever, this release model did not give users much chance toexperiment with new versions; it also deprived us of timelyfeedback on proposed changes. So, during the developmentof Lua 5.0 we started to release “work” versions, which arejust snapshots of the current development of Lua. This movebrought our current release model closer to the “ReleaseEarly, Release Often” motto of the open-source community.
2 The number of bugs found after final versions were released has beenconsistently small: only 10 in Lua 4.0, 17 in Lua 5.0, and 10 in Lua 5.1so far, none of them critical bugs.
In the remainder of this section we discuss some mile-stones in the evolution of Lua. Details on the evolution ofseveral specific features are given in §6. Table 1 summarizesthis evolution. It also contains statistics about the size of Lua,which we now discuss briefly.
The number of standard libraries has been kept small be-cause we expect that most Lua functions will be provided bythe host application or by third-party libraries. Until Lua 3.1,the only standard libraries were for input and output, stringmanipulation, mathematical functions, and a special libraryof built-in functions, which did not use the C API but directlyaccessed the internal data structures. Since then, we haveadded libraries for debugging (Lua 3.2), interfacing with theoperating system (Lua 4.0), tables and coroutines (Lua 5.0),and modules (Lua 5.1).
The size of C API changed significantly when it was re-designed in Lua 4.0. Since then, it has moved slowly towardcompleteness. As a consequence, there are no longer anybuilt-in functions: all standard libraries are implemented ontop the C API, without accessing the internals of Lua.
The virtual machine, which executes Lua programs, wasstack-based until Lua 4.0. In Lua 3.1 we added variants
2010年2月24日水曜日
Luaの歴史1.0 1.1 2.1 2.2 2.4 2.5 3.0 3.1 3.2 4.0 5.0 5.1
constructors • • • • • • • • • • • •garbage collection • • • • • • • • • • • •extensible semantics ◦ ◦ • • • • • • • • • •support for OOP ◦ ◦ • • • • • • • • • •long strings ◦ ◦ ◦ • • • • • • • • •debug API ◦ ◦ ◦ • • • • • • • • •external compiler ◦ ◦ ◦ ◦ • • • • • • • •vararg functions ◦ ◦ ◦ ◦ ◦ • • • • • • •pattern matching ◦ ◦ ◦ ◦ ◦ • • • • • • •conditional compilation ◦ ◦ ◦ ◦ ◦ ◦ • • • ◦ ◦ ◦anonymous functions, closures ◦ ◦ ◦ ◦ ◦ ◦ ◦ • • • • •debug library ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ • • • •multi-state API ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ • • •for statement ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ • • •long comments ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ • •full lexical scoping ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ • •booleans ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ • •coroutines ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ • •incremental garbage collection ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ •module system ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ •
1.0 1.1 2.1 2.2 2.4 2.5 3.0 3.1 3.2 4.0 5.0 5.1libraries 4 4 4 4 4 4 4 4 5 6 8 9built-in functions 5 7 11 11 13 14 25 27 35 0 0 0API functions 30 30 30 30 32 32 33 47 41 60 76 79vm type (stack × register) S S S S S S S S S S R Rvm instructions 64 65 69 67 67 68 69 128 64 49 35 38keywords 16 16 16 16 16 16 16 16 16 18 21 21other tokens 21 21 23 23 23 23 24 25 25 25 24 26
Table 1. The evolution of features in Lua.
Lua was becoming a mature product and needed stability forthe benefit of its growing community. Nevertheless, the needfor stability has not hindered progress. Major new versionsof Lua, such as Lua 4.0 and Lua 5.0, have been released sincethen.
The long times between versions also reflects our releasemodel. Unlike other open-source projects, our alpha versionsare quite stable and beta versions are essentially final, exceptfor uncovered bugs.2 This release model has proved to begood for Lua stability. Several products have been shippedwith alpha or beta versions of Lua and worked fine. How-ever, this release model did not give users much chance toexperiment with new versions; it also deprived us of timelyfeedback on proposed changes. So, during the developmentof Lua 5.0 we started to release “work” versions, which arejust snapshots of the current development of Lua. This movebrought our current release model closer to the “ReleaseEarly, Release Often” motto of the open-source community.
2 The number of bugs found after final versions were released has beenconsistently small: only 10 in Lua 4.0, 17 in Lua 5.0, and 10 in Lua 5.1so far, none of them critical bugs.
In the remainder of this section we discuss some mile-stones in the evolution of Lua. Details on the evolution ofseveral specific features are given in §6. Table 1 summarizesthis evolution. It also contains statistics about the size of Lua,which we now discuss briefly.
The number of standard libraries has been kept small be-cause we expect that most Lua functions will be provided bythe host application or by third-party libraries. Until Lua 3.1,the only standard libraries were for input and output, stringmanipulation, mathematical functions, and a special libraryof built-in functions, which did not use the C API but directlyaccessed the internal data structures. Since then, we haveadded libraries for debugging (Lua 3.2), interfacing with theoperating system (Lua 4.0), tables and coroutines (Lua 5.0),and modules (Lua 5.1).
The size of C API changed significantly when it was re-designed in Lua 4.0. Since then, it has moved slowly towardcompleteness. As a consequence, there are no longer anybuilt-in functions: all standard libraries are implemented ontop the C API, without accessing the internals of Lua.
The virtual machine, which executes Lua programs, wasstack-based until Lua 4.0. In Lua 3.1 we added variants
条件付きコンパイル消滅
2010年2月24日水曜日
Luaの歴史1.0 1.1 2.1 2.2 2.4 2.5 3.0 3.1 3.2 4.0 5.0 5.1
constructors • • • • • • • • • • • •garbage collection • • • • • • • • • • • •extensible semantics ◦ ◦ • • • • • • • • • •support for OOP ◦ ◦ • • • • • • • • • •long strings ◦ ◦ ◦ • • • • • • • • •debug API ◦ ◦ ◦ • • • • • • • • •external compiler ◦ ◦ ◦ ◦ • • • • • • • •vararg functions ◦ ◦ ◦ ◦ ◦ • • • • • • •pattern matching ◦ ◦ ◦ ◦ ◦ • • • • • • •conditional compilation ◦ ◦ ◦ ◦ ◦ ◦ • • • ◦ ◦ ◦anonymous functions, closures ◦ ◦ ◦ ◦ ◦ ◦ ◦ • • • • •debug library ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ • • • •multi-state API ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ • • •for statement ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ • • •long comments ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ • •full lexical scoping ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ • •booleans ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ • •coroutines ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ • •incremental garbage collection ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ •module system ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ ◦ •
1.0 1.1 2.1 2.2 2.4 2.5 3.0 3.1 3.2 4.0 5.0 5.1libraries 4 4 4 4 4 4 4 4 5 6 8 9built-in functions 5 7 11 11 13 14 25 27 35 0 0 0API functions 30 30 30 30 32 32 33 47 41 60 76 79vm type (stack × register) S S S S S S S S S S R Rvm instructions 64 65 69 67 67 68 69 128 64 49 35 38keywords 16 16 16 16 16 16 16 16 16 18 21 21other tokens 21 21 23 23 23 23 24 25 25 25 24 26
Table 1. The evolution of features in Lua.
Lua was becoming a mature product and needed stability forthe benefit of its growing community. Nevertheless, the needfor stability has not hindered progress. Major new versionsof Lua, such as Lua 4.0 and Lua 5.0, have been released sincethen.
The long times between versions also reflects our releasemodel. Unlike other open-source projects, our alpha versionsare quite stable and beta versions are essentially final, exceptfor uncovered bugs.2 This release model has proved to begood for Lua stability. Several products have been shippedwith alpha or beta versions of Lua and worked fine. How-ever, this release model did not give users much chance toexperiment with new versions; it also deprived us of timelyfeedback on proposed changes. So, during the developmentof Lua 5.0 we started to release “work” versions, which arejust snapshots of the current development of Lua. This movebrought our current release model closer to the “ReleaseEarly, Release Often” motto of the open-source community.
2 The number of bugs found after final versions were released has beenconsistently small: only 10 in Lua 4.0, 17 in Lua 5.0, and 10 in Lua 5.1so far, none of them critical bugs.
In the remainder of this section we discuss some mile-stones in the evolution of Lua. Details on the evolution ofseveral specific features are given in §6. Table 1 summarizesthis evolution. It also contains statistics about the size of Lua,which we now discuss briefly.
The number of standard libraries has been kept small be-cause we expect that most Lua functions will be provided bythe host application or by third-party libraries. Until Lua 3.1,the only standard libraries were for input and output, stringmanipulation, mathematical functions, and a special libraryof built-in functions, which did not use the C API but directlyaccessed the internal data structures. Since then, we haveadded libraries for debugging (Lua 3.2), interfacing with theoperating system (Lua 4.0), tables and coroutines (Lua 5.0),and modules (Lua 5.1).
The size of C API changed significantly when it was re-designed in Lua 4.0. Since then, it has moved slowly towardcompleteness. As a consequence, there are no longer anybuilt-in functions: all standard libraries are implemented ontop the C API, without accessing the internals of Lua.
The virtual machine, which executes Lua programs, wasstack-based until Lua 4.0. In Lua 3.1 we added variants
条件付きコンパイル消滅
命令数を増やして効率化終了
2010年2月24日水曜日
レジスタベースVMV S ス タ ッ ク ベ ー ス V M
2010年2月24日水曜日
スタックベースVM?スタックベースVMは計算要素などをスタックにpushしてから計算命令を実行するようにオペコードが設計されている
構造がシンプルで、移植性が高く、暗黙にスタックに対する操作であると決定づけができるので命令サイズが小さくできる
古典的VMは殆どスタックベースVM
2010年2月24日水曜日
レジスタベースVM?Luaの人曰く広く使われたレジスタベースVMはLuaが初めてのもの局所変数をレジスタに割り付けるので効率的だよ(引数やlocal変数など)オペランドを使って命令総数を減らせるよスタックにコピーするコストがないよ(push/popのコストがないよ)
2010年2月24日水曜日
LuaVMの性質Luaのレジスタはランタイムのスタックに配置されます(それは単なる配列です)。なので速い!(と言ってます)
ローカルでないグローバル変数なども、ただのテーブルなので、変数名に対応したハッシュ値は事前計算されているので速いよ!
2010年2月24日水曜日
あれ?ランタイムスタック==レジスタ==スタックじゃね?
2010年2月24日水曜日
あれ?ランタイムスタック==レジスタ==スタックじゃね?
嘘じゃないんだよー、そういうVMなんだよー
LuaがレジスタベースVMとか……
2010年2月24日水曜日
メリットだけではない
レジスタマシンの問題は2つある
1つは命令サイズの問題
もう1つデコードのオーバヘッド
2010年2月24日水曜日
命令サイズの問題レジスタマシンでは、オペランドの指定があるので、スタックマシンよりオペコードが大きくなります(LuaVMのオペコードサイズは4byte。対して、以前のLuaなら1byteか2byteだった)
しかし、オペコードの量が少なくなるので、総コードサイズはそこまで大きくならないですよ、と言ってます
2010年2月24日水曜日
デコードの問題スタックマシンのオペコードには、暗黙の指示があるので単純で速い。例えばADD
(ADDはスタックにのっている2つの数を加算するだけなのでADDだけで済む)
レジスタマシンはオペランド解析があるので、オーバヘッドが加算されてしまう(ADDならどのレジスタ同志の加算かのオペランド情報+解析が必要)
2010年2月24日水曜日
が、スタックマシンもどうせスタック操作が入るし、オペランド解析の方が論理演算などで済むので実際にはコストは安い
しかも、スタックマシンはよくマルチバイトオペランド(JavaVMなら分岐やgotoの2byte)を必要にするので、移植性含めてアライメントの問題などを考えたとき、オペランドをとってくるコストはレジスタマシンの方が安い(マシンによっては2byte単位でとれないかもだし)
2010年2月24日水曜日
レジスタマシン スタックマシン
オペコードサイズ
オペコード量
実行時オペランド解析
レジスタに対する操作なので大きくなる
Luaなら4byte
スタックに対する操作なので小さくて済む
Luaなら1 or 2byte
少なくて済む半分から三分の一
スタック操作の都合上多くなる
する しない
2010年2月24日水曜日
local a,t,ia=a+ia=a+100a=t[i]
これをオペコードにすると!(ちなみにこの例は実行時エラーになるYO)
2010年2月24日水曜日
1[1]PUSHNIL 3 2[2]GETLOCAL 0 ; a 3[2]GETLOCAL 2 ; i 4[2]ADD 5[2]SETLOCAL 0 ; a 6[3]GETLOCAL 0 ; a 7[3]ADDI 100 8[3]SETLOCAL 0 ; a 9[4]GETLOCAL 1 ; t 10[4]GETINDEXED 2 ; i 11[4]SETLOCAL 0 ; a 12[4]END
local a,t,ia=a+ia=a+100a=t[i]
スタックベース
これをオペコードにすると!(ちなみにこの例は実行時エラーになるYO)
2010年2月24日水曜日
1[1]PUSHNIL 3 2[2]GETLOCAL 0 ; a 3[2]GETLOCAL 2 ; i 4[2]ADD 5[2]SETLOCAL 0 ; a 6[3]GETLOCAL 0 ; a 7[3]ADDI 100 8[3]SETLOCAL 0 ; a 9[4]GETLOCAL 1 ; t 10[4]GETINDEXED 2 ; i 11[4]SETLOCAL 0 ; a 12[4]END
1 [2]ADD 0 0 22 [3]ADD 0 0 -1 ; - 1003 [4]GETTABLE 0 1 24 [4]RETURN 0 1
local a,t,ia=a+ia=a+100a=t[i]
スタックベース レジスタベース
これをオペコードにすると!(ちなみにこの例は実行時エラーになるYO)
2010年2月24日水曜日
Lua 4.0.1(スタックベース)の場合
main <0:@test.lua> (12 instructions/96 bytes at 0x100100650)0 params, 5 stacks, 3 locals, 0 strings, 0 numbers, 0 functions, 5 lines 1[1]PUSHNIL 3 2[2]GETLOCAL 0 ; a 3[2]GETLOCAL 2 ; i 4[2]ADD 5[2]SETLOCAL 0 ; a 6[3]GETLOCAL 0 ; a 7[3]ADDI 100 8[3]SETLOCAL 0 ; a 9[4]GETLOCAL 1 ; t 10[4]GETINDEXED 2 ; i 11[4]SETLOCAL 0 ; a 12[4]END
2010年2月24日水曜日
Lua 4.0.1(スタックベース)の場合
main <0:@test.lua> (12 instructions/96 bytes at 0x100100650)0 params, 5 stacks, 3 locals, 0 strings, 0 numbers, 0 functions, 5 lines 1[1]PUSHNIL 3 2[2]GETLOCAL 0 ; a 3[2]GETLOCAL 2 ; i 4[2]ADD 5[2]SETLOCAL 0 ; a 6[3]GETLOCAL 0 ; a 7[3]ADDI 100 8[3]SETLOCAL 0 ; a 9[4]GETLOCAL 1 ; t 10[4]GETINDEXED 2 ; i 11[4]SETLOCAL 0 ; a 12[4]END
local a,t,i
2010年2月24日水曜日
Lua 4.0.1(スタックベース)の場合
main <0:@test.lua> (12 instructions/96 bytes at 0x100100650)0 params, 5 stacks, 3 locals, 0 strings, 0 numbers, 0 functions, 5 lines 1[1]PUSHNIL 3 2[2]GETLOCAL 0 ; a 3[2]GETLOCAL 2 ; i 4[2]ADD 5[2]SETLOCAL 0 ; a 6[3]GETLOCAL 0 ; a 7[3]ADDI 100 8[3]SETLOCAL 0 ; a 9[4]GETLOCAL 1 ; t 10[4]GETINDEXED 2 ; i 11[4]SETLOCAL 0 ; a 12[4]END
local a,t,i
a=a+i
2010年2月24日水曜日
Lua 4.0.1(スタックベース)の場合
main <0:@test.lua> (12 instructions/96 bytes at 0x100100650)0 params, 5 stacks, 3 locals, 0 strings, 0 numbers, 0 functions, 5 lines 1[1]PUSHNIL 3 2[2]GETLOCAL 0 ; a 3[2]GETLOCAL 2 ; i 4[2]ADD 5[2]SETLOCAL 0 ; a 6[3]GETLOCAL 0 ; a 7[3]ADDI 100 8[3]SETLOCAL 0 ; a 9[4]GETLOCAL 1 ; t 10[4]GETINDEXED 2 ; i 11[4]SETLOCAL 0 ; a 12[4]END
local a,t,i
a=a+i
a=a+100
2010年2月24日水曜日
Lua 4.0.1(スタックベース)の場合
main <0:@test.lua> (12 instructions/96 bytes at 0x100100650)0 params, 5 stacks, 3 locals, 0 strings, 0 numbers, 0 functions, 5 lines 1[1]PUSHNIL 3 2[2]GETLOCAL 0 ; a 3[2]GETLOCAL 2 ; i 4[2]ADD 5[2]SETLOCAL 0 ; a 6[3]GETLOCAL 0 ; a 7[3]ADDI 100 8[3]SETLOCAL 0 ; a 9[4]GETLOCAL 1 ; t 10[4]GETINDEXED 2 ; i 11[4]SETLOCAL 0 ; a 12[4]END
local a,t,i
a=a+i
a=a+100
a=t[i]
2010年2月24日水曜日
Lua 4.0.1(スタックベース)の場合
main <0:@test.lua> (12 instructions/96 bytes at 0x100100650)0 params, 5 stacks, 3 locals, 0 strings, 0 numbers, 0 functions, 5 lines 1[1]PUSHNIL 3 2[2]GETLOCAL 0 ; a 3[2]GETLOCAL 2 ; i 4[2]ADD 5[2]SETLOCAL 0 ; a 6[3]GETLOCAL 0 ; a 7[3]ADDI 100 8[3]SETLOCAL 0 ; a 9[4]GETLOCAL 1 ; t 10[4]GETINDEXED 2 ; i 11[4]SETLOCAL 0 ; a 12[4]END
local a,t,i
a=a+i
a=a+100
a=t[i]
殆どスタックに対する操作じゃないかYO!
ADDとか、ADDIはスタックにある要素に対する操作なので仕方がない……
2010年2月24日水曜日
main <test.lua:0,0> (4 instructions, 16 bytes at 0x100101050)0+ params, 3 slots, 0 upvalues, 3 locals, 1 constant, 0 functions 1 [2] ADD 0 0 2 2 [3] ADD 0 0 -1 ; - 100 3 [4] GETTABLE 0 1 2 4 [4] RETURN 0 1
Lua 5.1.4(レジスタベース)の場合
2010年2月24日水曜日
main <test.lua:0,0> (4 instructions, 16 bytes at 0x100101050)0+ params, 3 slots, 0 upvalues, 3 locals, 1 constant, 0 functions 1 [2] ADD 0 0 2 2 [3] ADD 0 0 -1 ; - 100 3 [4] GETTABLE 0 1 2 4 [4] RETURN 0 1
Lua 5.1.4(レジスタベース)の場合
a=a+i
2010年2月24日水曜日
main <test.lua:0,0> (4 instructions, 16 bytes at 0x100101050)0+ params, 3 slots, 0 upvalues, 3 locals, 1 constant, 0 functions 1 [2] ADD 0 0 2 2 [3] ADD 0 0 -1 ; - 100 3 [4] GETTABLE 0 1 2 4 [4] RETURN 0 1
Lua 5.1.4(レジスタベース)の場合
a=a+ia=a+100
2010年2月24日水曜日
main <test.lua:0,0> (4 instructions, 16 bytes at 0x100101050)0+ params, 3 slots, 0 upvalues, 3 locals, 1 constant, 0 functions 1 [2] ADD 0 0 2 2 [3] ADD 0 0 -1 ; - 100 3 [4] GETTABLE 0 1 2 4 [4] RETURN 0 1
Lua 5.1.4(レジスタベース)の場合
a=a+ia=a+100a=t[i]
2010年2月24日水曜日
main <test.lua:0,0> (4 instructions, 16 bytes at 0x100101050)0+ params, 3 slots, 0 upvalues, 3 locals, 1 constant, 0 functions 1 [2] ADD 0 0 2 2 [3] ADD 0 0 -1 ; - 100 3 [4] GETTABLE 0 1 2 4 [4] RETURN 0 1
Lua 5.1.4(レジスタベース)の場合
a=a+ia=a+100a=t[i]
パラメータは多いけど、わかりやすい状態になった。
そして、結果的に省メモリになってる。local a,t,iも命令として消えてるし……。
2010年2月24日水曜日
というわけでもないのかなーとは思います2010年2月24日水曜日
なぜか?本気でやるとレジスタ割り当てのためコード生成の実装が複雑になるので難しくなる(Luaは微塵もやってない:-p)
レジスタはハードウェアレジスタではないので、そこまで速くならない
関数呼び出しなどには結局スタックを使う羽目になる
頑張った割に報われない、らしい……
2010年2月24日水曜日
それでもLuaでレジスタベースが有効な理由
Luaでは、数値でも12byte(tag+実体のunion)で、スタックへのコピーコストが高いのでなくしたかった
固定長の命令セットにできるのでアライメントの問題で効率がこれだけでも良くなる
普通の努力でそこそこの結果がでてる
2010年2月24日水曜日
こんなに速くなりました
8 Conclusion
In this paper we have presented the most innovative aspects of the implementa-tion of Lua 5.0: its register-based virtual machine, the new algorithm for opti-mizing tables used as arrays, and the implementation of closures.
To our knowledge, Lua is the first language in wide use to adopt a register-based virtual machine. The optimization for tables allows a table to be partiallyimplemented as an array when it is used that way (that is, when it has enoughkeys in a range 1 . . . n). Its implementation of closures is also unique, combin-ing the use of an array-based stack with lexically scoped first-order functions,without complex control-flow analysis.
The table in Figure 10 shows some simple performance comparisons betweenthe old implementation and the new one. The tests were run on an Intel Pen-tium IV machine with 512 Mbytes running Linux 2.6, with Lua compiled withgcc 3.3. Lua 4.0 uses neither the register-based virtual machine (its machine isstack based) nor the table–array optimization. Lua 5’ is Lua 5.0 without table–array optimization, tail calls, and dynamic stacks (related to coroutines); Lua 5’is essentially Lua 4.0 with the new register-based virtual machine.
We took all test cases from The Great Computer Language Shootout [2],except the first one (sum), which is a simple loop to add all integers from 1to n. This first test spends most of its time in the virtual machine; it shows thatthe new virtual machine can be more than twice as fast as the old one. The othertests spend more time in other tasks (function calls, table/array access, etc.),so the gain in the virtual machine has a smaller e!ect on the total time. In thetests that use arrays (sieve, heapsort, and matrix ), the combination of the newvirtual machine with the new optimization for arrays can reduce the runningtime up to 40%.
The complete code of Lua 5.0 is available for browsing at Lua’s web site:http://www.lua.org/source/5.0/.
program Lua 4.0 Lua 5’ Lua 5.0
sum (2e7) 1.23 0.54 (44%) 0.54 (44%)
fibo (30) 0.95 0.68 (72%) 0.69 (73%)
ack (8) 1.00 0.86 (86%) 0.88 (88%)
random (1e6) 1.04 0.96 (92%) 0.96 (92%)
sieve (100) 0.93 0.82 (88%) 0.57 (61%)
heapsort (5e4) 1.08 1.05 (97%) 0.70 (65%)
matrix (50) 0.84 0.82 (98%) 0.59 (70%)
Figure 10: Benchmarks (times in seconds; percentages are relative to Lua 4.0)Luaのレジスタマシンが一般のレジスタマシンではないので参考までに
2010年2月24日水曜日
その他関 数 コ ー ル と 他 言 語 L u a V M
2010年2月24日水曜日
Luaの関数コールLuaは関数コールに2つのスタックを使います1つは戻りアドレスなどを保持するものもう1つは、単にLua変数の配列です(引数、local変数の一時変数保持)一般的に「リターンスタック」と「データスタック」と呼ぶものレジスタ・ウィンドウも使ってるよ
2010年2月24日水曜日
レジスタ・ウィンドウプログラムの各部分ごとにレジスタセットを提供する技法
レジスタが簡単に溢れないようにプロシージャコールごとに見えるレジスタを制限する
Luaでは単に仮想レジスタをその都度作る
2010年2月24日水曜日
他環境でのLuaVMLua-Alchemy(Lua on Flash)
kahlua(Lua on Java[J2ME])
Yueliang(Lua on Lua)
LuaCLR(Lua on .NET)
Lua2js(Lua on JavaScript)
詳しくはhttp://lua-users.org/wiki/
LuaImplementations
2010年2月24日水曜日
参考文献
The Implementation of Lua 5.0
The Evolution of Lua
A No-Frills Introduction to Lua 5.1 VM Instructions
Luaのソースのlvm.cとlopcodes.h
2010年2月24日水曜日
まとめLuaのインストラクションセットはシンプルだよ!
古典的なスタックベースも良いけど、レジスタベースも良いよ!
Luaはレジスタベースで頑張ったので偉い!(レジスタ割り当てとか全くやってないけどね)
2010年2月24日水曜日
最後にもしLuaを高速化、最適化したいのであれば、それは重い動作をC関数に落とし込むのが最善です
LuaにはJITや最適化バイトコードの話もありますが、まずは重い処理をC関数にするのが良いです
VMみるのは恐らく、最終手段2010年2月24日水曜日
ご静聴ありがとうございました
2010年2月24日水曜日