Jump to content

Module:Timing

From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by Jeblad (talk | contribs) at 21:21, 16 January 2016 (from nowiki). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.
(diff) ← Previous revision | Latest revision (diff) | Newer revision → (diff)

-- module profiling individual functions
-- © John Erling Blad, Creative Commons by Attribution 3.0

-- @var The table holding this modules exported members
local p = {}

-- calculate the statistics for time series, and report mean and variance
-- for some background on this calculation, see [[w:en:average]] and [[w:en:Standard deviation]]
-- @param table timing is a sequence of time differences
-- @return table of mean and variance
function p.stats( timing )
	local minVal = timing[1]
	local maxVal = timing[1]
	for i,v in ipairs(timing) do
	end
	local sqr,mean=0,0
	for i,v in ipairs(timing) do
		minVal = v < minVal and v or minVal
		maxVal = v > maxVal and v or maxVal
		mean = v + mean
		sqr = math.pow(v,2) + sqr
	end
	mean = mean / #timing
	local var = (sqr / #timing) - math.pow(mean,2)
	return { mean, var, minVal, maxVal }
end

-- runner that iterates a provided function while taking the time for each chunk of iterations
-- @param function func that is the kernel of the iterations
-- @param integer count that is the size of each chunk of iterations
-- @param integer sets that is the number of chunks to process
-- @return table of runtime for the given function
function p.runner(func, count, sets)
	
	-- measure the function
	local time = {}
	for i=1,sets do
		time[1+#time] = os.clock()
		for j=1,count do
			func()
		end
		time[#time] = os.clock() - time[#time]
	end
	
	return time
end

-- combine the measurements into a new form
-- for some background on this calculation, see [[w:en:Sum of normally distributed random variables]]
-- @param table tick stats for the baseline
-- @param table tack stats for the observed function
-- @return table with the combined stats, shifted from variance to standard deviation
function p.combine(tick, tack)
	-- adjust the actual measurement for the baseline
	return { tack[1] - tick[1], math.pow(tack[2] + tick[2], 0.5), tick[3], tick[4] }
end

-- formatter for the result produced by the runner
-- @param table timing as a mean and a standard deviation
-- @return string describing the result
function p.report( timing )
	local messages = {}
	messages['call-result'] = 'Each call was running for about $1 seconds.'
	messages['mean-result'] = 'Mean runtime for each set was $1 seconds,\nwith standard deviation of $2 seconds,\nminimum $3, maximum $4.'
	messages['overall-result'] = 'Total time spent was about $1 seconds.'
	
	local function g( key, ...)
		local msg = mw.message.new( 'profile-' .. key )
		if msg:isBlank() then
			msg = mw.message.newRawMessage( messages[key] )
		end
		return msg
	end
	
	local strings = {
		g('call-result'):numParams(unpack(timing[1])):plain(),
		g('mean-result'):numParams(unpack(timing[2])):plain(),
		g('overall-result'):numParams(unpack(timing[3])):plain()
	}
	return table.concat(strings, ' ')
end

-- metatable for the export
local mt = {
	-- call-form of the table, with arguments as if it were runner()
	-- @paramfunction func that is the kernel of the iterations
	-- @param integer count that is the size of each chunk of iterations, default is 100
	-- @param integer sets that is the number of chunks to process, default is 10
	-- @return string describing the result
	__call = function (self, func, count, sets)
		local tick = os.clock()
		
		-- the defaults
		if not count then
			count = 100
		end
		if not sets then
			sets = 10
		end
	
		-- a dummy function that is used for a baseline measure
		function dummy()
			return nil
		end
		
		local baseline = self.stats( self.runner(dummy, count, sets) )
		local actual = self.stats( self.runner(func, count, sets) )
		local combined = self.combine(baseline, actual)
		local tack = os.clock()
		return self.report({{ combined[1] / count }, combined, { tack - tick }})
	end
}

-- install the metatable
setmetatable(p, mt)

-- done
return p