はじめに
Luaではtable.insert()
という関数が標準ライブラリとして用意されています。
しかし、この関数は遅く、単に末尾に要素を追加するだけならもっと早い方法があります。
その方法としては大きく2つ、カウンターを使う方法と#
(長さ演算子)を使う方法が知られていると思います。
このうち、長さ演算子が5.4で高速化したと聞いたので、簡単にベンチを取ってみました。
使用したソースコード
1から1,000,000までの連番で配列を作る速度を競います。
グローバル変数へのアクセス自体が遅いという話もあるので、一度table.insert
をローカル変数に束縛する方法も試してみます。
local function mean(arr)
local sum = 0
for i = 1, #arr do
sum = sum + arr[i]
end
return sum / #arr
end
local time_insert = {}
for i = 1, 10 do
time_insert[i] = os.clock()
local new = {}
for j = 1, 1000000 do
table.insert(new, j)
end
time_insert[i] = os.clock() - time_insert[i]
end
print("table.insert: " .. mean(time_insert))
local time_insert2 = {}
for i = 1, 10 do
time_insert2[i] = os.clock()
local insert = table.insert
local new = {}
for j = 1, 1000000 do
insert(new, j)
end
time_insert2[i] = os.clock() - time_insert2[i]
end
print("local insert: " .. mean(time_insert2))
local time_counter = {}
for i = 1, 10 do
time_counter[i] = os.clock()
local new = {}
local c = 0
for j = 1, 1000000 do
c = c + 1
new[c] = j
end
time_counter[i] = os.clock() - time_counter[i]
end
print("counter: " .. mean(time_counter))
local time_length = {}
for i = 1, 10 do
time_length[i] = os.clock()
local new = {}
for j = 1, 1000000 do
new[#new + 1] = j
end
time_length[i] = os.clock() - time_length[i]
end
print("length: " .. mean(time_length))
結果
比較用のLua5.3.3だとこうなります。
table.insert: 0.1646712
local insert: 0.1468932
counter: 0.0185333
length: 0.1276985
lua5.4.3だとこう。
table.insert: 0.0581953
local insert: 0.0498282
counter: 0.0133932
length: 0.0204012
全体的に早くなっていますが、特に#
はかなり早くなっていますね。6倍くらいです。
table.insert()
も倍以上早くなっているのは驚きでした。
結論
しかし一番早いのはカウンター使う方法で変わりませんでした。うーん。
おまけ
neovimにbuilt-inされているluajitだとこうなります
table.insert: 0.0054451
local insert: 0.0057246
counter: 0.0052584
length: 0.0056855