以下のスクリプトを書いてテストしてみた。
-- rawget と ls[k] および -- rawset と ls[k] = v は、どっちが高速か? -- 関数 f を n 回実行するのにかかった時間を測定する do local clock = os.clock function time( n, f, ... ) local oldtime = clock() for i = 1, n do f(...) end return clock() - oldtime end end -- ある関数 f を 1 秒間で何回実行出来るかを測定する do local time = time function benchmark( f, ... ) local n = 1000 -- 最低でも実行する回数 local t = 0 local total = 0 repeat t = t + time( n, f, ... ) total = total + n n = n * 2 until t >= 1.0 return total / t end end -- ベンチマークを表示する do local print = print local floor = math.floor function print_benchmark( message, f, ... ) print( message, floor( benchmark( f, ... ) + 0.5 ) ) end end -- テストと実行速度の安定を兼ねて、関数呼び出しのみの場合を計測 function id(...) return ... end print_benchmark( "id", id ) -- テーブルアクセス do local n = 100 local t = {} for i = 1, n do t[i] = i end -- 存在するキーに対して get function getA() local tmp for i = 1, n do tmp = t[i] end end -- 存在しないキーに対して get function getB() local tmp for i = n + 1, n + n do tmp = t[i] end end local rawget = rawget -- 存在するキーに対して rawget function rawgetA() local tmp for i = 1, n do tmp = rawget( t, i ) end end -- 存在しないキーに対して rawget function rawgetB() local tmp for i = n + 1, n + n do tmp = rawget( t, i ) end end -- 実装補助 function set_( t ) for i = 1, n do t[i] = i end end local rawset = rawset function rawset_( t ) for i = 1, n do rawset( t, i, i ) end end -- 存在するキーに対して set function setA() set_(t) end -- 存在しないキーに対して set function setB() set_({}) end -- 存在するキーに対して rawset function rawsetA() rawset_(t) end function rawsetB() rawset_({}) end end print_benchmark( "getA", getA ) print_benchmark( "getB", getB ) print_benchmark( "rawgetA", rawgetA ) print_benchmark( "rawgetB", rawgetB ) print_benchmark( "setA", setA ) print_benchmark( "setB", setB ) print_benchmark( "rawsetA", rawsetA ) print_benchmark( "rawsetB", rawsetB )
結果(例):
id 11032323 getA 206645 getB 147059 rawgetA 80482 rawgetB 70673 setA 175499 setB 77392 rawsetA 58442 rawsetB 42000
既に存在するキーに対するアクセスの場合も、そうでない場合も、
基本的に普通のテーブルアクセスの方が高速のようです。
恐らくこれは、関数呼び出しをしている分のコストなのだと思います。
普通にテーブルアクセスをする場合は、専用の VM コードにコンパイルされるでしょうから。
また、今回はメタテーブルのない場合でテストしましたが、
メタテーブルを使った場合でも、既に存在するキーに対しては全く処理内容は変わらないですし、
そうでない場合には rawget/rawset とインデックスアクセスで処理内容変わりますから、
結局、通常のインデックスアクセスの方が、 rawset/rawget より常に速いと言えそうです。