Benchmark Module


Now, here is my benchmarking module. I wrote it to find out, which is the fastest way to do some things in Lua. It requires "HiResTimer".

Benchmark.lua

---------------------------
--- Benchmark provides a set of functions, which measures and compares execution time of different code fragments
---------------------------
module("Benchmark",package.seeall)
local hrt=require"HiResTimer"
local bench_results={}
local total_diff=0.0
---------------------------
--- Call a function for several times and measure execution time.
-- @param name (string) literal to identify test in result table
-- @param func (function) function to be benchmarked
-- @param loops (number) how often this function should be called
----------------------------
function Bench(name,func,loops)
	loops=loops or 1000
	local q0=hrt.clock()
	for i=1,loops do
		func()
	end
	local q1=hrt.clock()
	local result=bench_results[name] or {count=0,diff=0}
	local diff=(q1-q0)
	result.count=result.count+loops
	result.diff=result.diff+diff
	total_diff=total_diff+diff
	bench_results[name]=result
end
---------------------------
--- Do Benchmark over a table of functions
-- @param functions (table) table of functions to check
-- @param loops (number) how often to call the function (optional, default 1000)
---------------------------
function BenchTable(functions,loops)
	loops=loops or 1000
	for name,func in pairs(functions) do
		Bench(name,func,loops)
	end
end
----------------------------
--- Printout benchmark results.
-- @param Output (function) to receive values (optional, default=io.write)
----------------------------
function Results(Output)
	--
	-- prepare the output
	--
	Output=Output or io.write
	local function printf(form,...)
		Output(string.format(form,...))
	end
	--
	-- calculate mean values
	-- create a table of names
	--
	local names={}
	local namlen=0
	for name,result in pairs(bench_results) do
		result.mean=result.diff/result.count
--~ 		printf("diff=%8.3g cnt=%d mean=%8.3g\n",result.diff,result.count,result.mean)
		names[#names+1]=name
		if #name>namlen then namlen=#name end
	end
	--
	-- sort table by mean value
	--
	local function comp(na,nb)
		return bench_results[na].mean<bench_results[nb].mean
	end
	table.sort(names,comp)
	--
	-- derive some reasonable output scaling
	--
	local max=bench_results[names[#names]].mean
	local fac,unit=1,"sec"
	if max<0.001 then
		fac,unit=1000000,"オs"
	elseif max<1.0 then
		fac,unit=1000,"ms"
	end
	--
	-- create a format string (due "%-*s" is missing in string.format)
	--
	local form=string.gsub("-- %-#s : %8.3f %s = %6d loops/s [%6.2f %%] %5.3f times faster\n","#",tostring(namlen))
	--
	-- now print the result
	--
	printf("-----------------------------------\n")
	printf("-- MAX = %8.3f %s\n",max*fac,unit)
	for i=1,#names do
		local name=names[i]
		result=bench_results[name]
		local ratio=result.mean*100/max
		local times=max/result.mean
		local loops=1/result.mean
		printf(form,name,result.mean*fac,unit,loops,ratio,times)
	end
	printf("-----------------------------------\n")
end
return Benchmark

Example : string concatenation

The question is, how to combine some literals and some variables to a string.

require"Benchmark"
local sf=string.format
local TestCases=
{
	TextConcat=function()
		local t,a,s='A','#',5
		local n=""
		for i=1,1000 do
			n="\symbol{circled"..s.."}="..t..a..i
		end
	end,
	-- extreme slow
	TableConcat=function()
		local t,a,s='A','#',5
		local n=""
		for i=1,1000 do
			n=table.concat{"\symbol{circled",s,"}=",t,a,i}
		end
	end,
	StringFormat=function ()
		local t,a,s='A','#',5
		local n=""
		for i=1,1000 do
			n=string.format("\symbol{circled%d}=%s%s%d",s,t,a,i)
		end
	end,
	FunctionLocalStringFormat=function()
		local t,a,s='A','#',5
		local n=""
		local sf=string.format
		for i=1,1000 do
			n=sf("\symbol{circled%d}=%s%s%d",s,t,a,i)
		end
	end,
	ModuleLocalStringFormat=function()
		local t,a,s='A','#',5
		local n=""
		for i=1,1000 do
			n=sf("\symbol{circled%d}=%s%s%d",s,t,a,i)
		end
	end
}
Benchmark.BenchTable(TestCases,100)
Benchmark.Results()

Result

surprisingly string.format does it best. Localizing it gives just a little speedup then.
-----------------------------------
-- MAX = 4.951 ms
-- ModuleLocalStringFormat : 1.665 ms = 600 loops/s [ 33.63 %] 2.974 times faster
-- FunctionLocalStringFormat : 1.696 ms = 589 loops/s [ 34.26 %] 2.919 times faster
-- StringFormat : 1.728 ms = 578 loops/s [ 34.90 %] 2.865 times faster
-- TextConcat : 2.754 ms = 363 loops/s [ 55.64 %] 1.797 times faster
-- TableConcat : 4.951 ms = 201 loops/s [100.00 %] 1.000 times faster
-----------------------------------

Example loop over array

Often diskussed for i=1,#array vs. for i,v in ipairs

-----------------------------------
-- setup test data
-----------------------------------
local array={}
for i=1,100 do
	local x={}
	for j=1,100 do
		x[j]=tostring(i)..tostring(j)
	end
	array[i]=x
end
local TestCases=
{
	for_i_array=function()
		local count=0
		for i=1,#array do
			local x=array[i]
			for j=1,#x do
				local y=x[j]
				-- do something with y
			end
		end
		return count
	end,
	for_ipairs=function()
		local count=0
		for i,x in ipairs(array) do
			for j,y in ipairs(x) do
				-- do something with y
			end
		end
		return count
	end
}
local Benchmark=require"Benchmark"
Benchmark.BenchTable(TestCases,1000)
Benchmark.Results()

Result

for i=1,#array is the winner
-----------------------------------
-- MAX = 1.127 ms
-- for_i_array : 0.689 ms = 1451 loops/s [ 61.12 %] 1.636 times faster
-- for_ipairs : 1.127 ms = 887 loops/s [100.00 %] 1.000 times faster
-----------------------------------

RecentChanges · preferences
edit · history
Last edited August 30, 2010 10:33 am GMT (diff)

AltStyle によって変換されたページ (->オリジナル) /