1. 程式人生 > >lua:部分常用操作的效率對比及程式碼優化建議(附測試程式碼)

lua:部分常用操作的效率對比及程式碼優化建議(附測試程式碼)

前言:

1、以下測試結果均基於大量迴圈得來,少量使用次數的情況未知;

2、測試用例有限,文中所有結論只基於我當前的認知得出,正確性無法確保,如有問題歡迎一起探討,也期待有人告知更多的優化方法;

3、只有前兩條在採用luajit的情況下額外測試過,由於在iOS平臺無法使用luajit,所以平時寫程式碼還是以採用官方直譯器的情況為準;

4、不想看正文測試結果的,可以直接看最後的結論;

5、如果想自己測試驗證的,建議不同情況用不同檔案分開測試,避免因為記憶體佔用等原因第一種情況的程式碼執行完之後影響第二種情況。

正文:

1、table.insert(tab, 1, a)的效率遠遠低於table.insert(tab, a),luagit下情況相同;
2、tab[#tab + 1] = a比table.insert(tab, a)的效率提升15~25%,luagit下效率幾乎相同;

3、以下4種迴圈方式對比:(分別為正序數值for迴圈、反序數值for迴圈、ipairs泛型for迴圈、pairs泛型for迴圈)

local tab = {}
for i = 1, 20000000 do
	tab[#tab + 1] = true
end

local time1 = os.clock()
for i = 1, #tab do
	tab[i] = true
end
print('1====================', os.clock() - time1)

local time2 = os.clock()
for i = #tab, 1, -1 do
	tab[i] = true
end
print('2====================', os.clock() - time2)

local time3 = os.clock()
for k, v in ipairs(tab) do
	v = true
end
print('3====================', os.clock() - time3)

local time4 = os.clock()
for k, v in pairs(tab) do
	v = true
end
print('4====================', os.clock() - time4)
效率比約為:0.285:0.26:0.922:0.87

將迴圈體替換為:

tab[i] = true
tab[i] = 1
tab[i] = {true}
tab[i] = nil
tab[i] = true
v = true
v = 1
v = {true}
v = nil
v = true

效率比約為:3.65:4.66:4.86:4.805

迴圈體內的程式碼加倍後:

效率比約為:8.57:9.6:9.15:9.15

迴圈體內的程式碼加兩倍後:

效率比約為:12.95:13.9:12.2:12.25

在迴圈體後新增下列程式碼後:

local a, b, c = 1, 2, 3
c = a * b + c

效率比同上。

將迴圈次數縮小100倍後:

效率比接近1,且不太穩定(偶爾某種迴圈稍微快一點)。

結論:迴圈體內很簡單時,2的效率最高,table為純陣列的情況建議用2;但實際情況迴圈體內一般操作不會過於簡單,隨著複雜度的增加,兩個泛型for迴圈的效率接近,且逐漸優於兩個數值for迴圈;迴圈次數減少後,效率比接近1。建議平時寫程式碼時根據習慣選擇就好。

4、(1)迴圈內建立變數比迴圈外建立變數的效率提升隨著次數增加逐漸提高(猜想可能因為迴圈外建立的變數在迴圈體使用時需要跨域,代價比建立變數大);

local tab = {}
for i = 1, 2000000 do
	tab[#tab + 1] = true
end

local time1 = os.clock()
for k, v in ipairs(tab) do
	local a = true
	local b = 1
	local c = {true}
	local d = nil
	local e = 'a'
end
print('1====================', os.clock() - time1)

local time2 = os.clock()
local a, b, c, d, e = nil, nil, nil, nil, nil
for k, v in ipairs(tab) do
	a = true
    b = 1
	c = {true}
	d = nil
	e = 'a'
end
print('2====================', os.clock() - time2)

     (2)迴圈內建立閉包和迴圈外建立閉包的效率差不多(當閉包數量較多時,在迴圈外建立閉包會稍微快一點,幾乎可以忽略);

local tab = {}
for i = 1, 2000000 do
	tab[#tab + 1] = true
end

local time1 = os.clock()
for k, v in ipairs(tab) do
	local a = function ()
		local n = 'n'
		local m = 'm'
		local str = ''
		for i = 1, 10 do
            str = str .. n .. m
		end
		return str 
	end

	local b = a() .. 'a'
end
print('1====================', os.clock() - time1)

local time2 = os.clock()
local a = function ()
	local n = 'n'
	local m = 'm'
	local str = ''
	for i = 1, 10 do
        str = str .. n .. m
	end
	return str 
end
for k, v in ipairs(tab) do
	local b = a() .. 'a'
end
print('2====================', os.clock() - time2)

     (3)迴圈外建立長表比迴圈內建立長表的效率提升隨著次數增加逐漸提高。

local tab = {}
for i = 1, 2000000 do
	tab[#tab + 1] = true
end

local time1 = os.clock()
for k, v in ipairs(tab) do
	local a = {1, 1, 1, 1, 1}

	local num = a[1]
end
print('1====================', os.clock() - time1)

local time2 = os.clock()
local a = {1, 1, 1, 1, 1}
for k, v in ipairs(tab) do
	local num = a[1]
end
print('2====================', os.clock() - time2)

結論:在迴圈內建立變數和迴圈外建立變數的效率取決於建立代價和跨域代價的對比,根據經驗選擇最合適的處理。

5、以區域性變數代替多次使用的外部變數(xx.xx之類)比直接使用外部變數的效率提升隨著次數增加逐漸提高;

local tab = {}
for i = 1, 2000000 do
	tab[#tab + 1] = true
end

local publicTab = {a = true, b = 1, c = {true}, d = nil, e = 'a'}

local time1 = os.clock()
for k, v in ipairs(tab) do
	if publicTab.a then
		local n = publicTab.b * 2 / 3 + 4 - 5 + #publicTab.c
	end
	if not publicTab.d then
		local m = publicTab.e .. publicTab.e .. publicTab.e
	end
end
print('1====================', os.clock() - time1)

local time2 = os.clock()
for k, v in ipairs(tab) do
	local a = publicTab.a
    local b = publicTab.b
	local c = publicTab.c
	local d = publicTab.d
	local e = publicTab.e

	if a then
		local n = b * 2 / 3 + 4 - 5 + #c
	end
	if not d then
		local m = e .. e .. e
	end
end
print('2====================', os.clock() - time2)

6、減少函式呼叫可以很大提高效率;

local tab = {}
for i = 1, 2000000 do
	tab[#tab + 1] = true
end

local time1 = os.clock()
local func = function (a, b)
    a = a * b + a / b - a + b
    return a
end

for k, v in ipairs(tab) do
    local a = func(1, 2)
end
print('1====================', os.clock() - time1)

local time2 = os.clock()
for k, v in ipairs(tab) do
    local a = 1 * 2 + 1 / 2 - 1 + 2
end
print('2====================', os.clock() - time2)

7、提前宣告table大小可以很大提高效率;

local tab = {}
for i = 1, 2000000 do
	tab[#tab + 1] = true
end

local time1 = os.clock()
for k, v in ipairs(tab) do
	local changeTab = {}
	for i = 1, 5 do
        changeTab[#changeTab + 1] = true
	end
end
print('1====================', os.clock() - time1)

local time2 = os.clock()
for k, v in ipairs(tab) do
	local changeTab = {nil, nil, nil, nil, nil}
	for i = 1, 5 do
        changeTab[#changeTab + 1] = true
	end
end
print('2====================', os.clock() - time2)

8、string.format比..效率低很多;

local tab = {}
for i = 1, 2000000 do
	tab[#tab + 1] = true
end

local time1 = os.clock()
for k, v in ipairs(tab) do
	local a = 'a' .. 'n' .. 'b' .. 'm' .. 'c'
end
print('1====================', os.clock() - time1)

local time2 = os.clock()
for k, v in ipairs(tab) do
	local a = string.format('%sn%sm%s', 'a', 'b', 'c')
end
print('2====================', os.clock() - time2)

9、乘法和除法效率一樣;

local tab = {}
for i = 1, 20000000 do
	tab[#tab + 1] = true
end

local time1 = os.clock()
for k, v in ipairs(tab) do
	local a = 1 * 0.5 * 0.25 * 0.2 * 0.1
end
print('1====================', os.clock() - time1)

local time2 = os.clock()
for k, v in ipairs(tab) do
	local a = 1 / 2 / 4 / 5 / 10
end
print('2====================', os.clock() - time2)

10、判斷陣列表是否為空時用#tab>0隨著tab的長度增加所花時間逐漸增加,而用next(tab)時所花時間固定不變,但只有當tab長度很長時,#tab>0的效率才會低於next(tab)。

總結:

1、給table新增元素時,tab[#tab + 1] = a比table.insert(tab, a)效率高,遠比table.insert(tab, 1, a)效率高;

2、4種迴圈方式根據習慣選擇就好,效率差別不大;

3、在迴圈內建立變數和迴圈外建立變數的效率取決於建立代價和跨域代價的對比,根據經驗選擇最合適的處理;

4、以區域性變數代替多次使用的外部變數(xx.xx之類);

5、減少函式呼叫可以很大提高效率,但會降低程式碼的可讀性,按需選擇;

6、提前宣告table大小可以很大提高效率,可以做到的情況儘量做到;

7、string.format比..效率低很多,但可讀性大大提高,按需選擇;

8、判斷陣列表是否為空時,一般情況用#tab>0即可,除非tab長度特別大,用next(tab)的效率才會更高。