Module:Sandbox/Aidan9382/Benchmarker Explained

-- In-depth execution speed benchmarker - read the /doc for more info

--

--

-- Always use rawget/rawset on _G to bypass strictlocal ActiveHooker = rawget(_G, "_BenchmarkerHooker")if ActiveHooker ~= nil then return ActiveHookerend

--

Personal stuff

--local function dp(x, n) n = n or 4 return math.floor(x*10^n+0.5) / 10^nend

local function GetVarargInfo(...) return, select("#", ...)end

local function DetermineCaller(stacktrace) for line in stacktrace:gmatch("[^\n]+") do if not line:find("^stack traceback:") and not line:find("Aidan9382/Benchmarker") then local f, l = line:match("^%s*([^:]+):([^:]+)") return end endend

local CompleteCalls = local FunctionCallStack = local NoHookZone =

local function FinishUp -- Note: Don't currently use caller stats. Eh, whatever local TotalTimeTaken = 0 local ModuleTotalTimes = local FunctionTotalTimes = local SeenModules = local SeenFunctions = for _, Call in next, CompleteCalls do local CallTime = Call.TimeTaken - Call.Offset TotalTimeTaken = TotalTimeTaken + CallTime if not ModuleTotalTimes[Call.Origin] then ModuleTotalTimes[Call.Origin] = 0 SeenModules[#SeenModules+1] = Call.Origin end ModuleTotalTimes[Call.Origin] = ModuleTotalTimes[Call.Origin] + CallTime local UniqueName = Call.Origin .. "." .. Call.Name if not FunctionTotalTimes[UniqueName] then FunctionTotalTimes[UniqueName] = 0 SeenFunctions[#SeenFunctions+1] = UniqueName end FunctionTotalTimes[UniqueName] = FunctionTotalTimes[UniqueName] + CallTime end if TotalTimeTaken > .01 then table.sort(SeenModules, function(a, b) return ModuleTotalTimes[a] > ModuleTotalTimes[b] end) table.sort(SeenFunctions, function(a, b) return FunctionTotalTimes[a] > FunctionTotalTimes[b] end) mw.log("\n-- Benchmarker Finished --") mw.log("Total time taken: " .. dp(TotalTimeTaken)*1000 .. "ms") mw.log("\nTop 5 modules by time taken:") for i = 1, math.min(5, #SeenModules) do local t = dp(ModuleTotalTimes[SeenModules[i]]) mw.log(SeenModules[i] .. ": " .. t*1000 .. "ms (" .. dp(t/TotalTimeTaken, 3)*100 .. "%)") end mw.log("\nTop 5 functions by time taken:") for i = 1, math.min(5, #SeenFunctions) do local t = dp(FunctionTotalTimes[SeenFunctions[i]]) mw.log(SeenFunctions[i] .. ": " .. t*1000 .. "ms (" .. dp(t/TotalTimeTaken, 3)*100 .. "%)") end mw.log("") -- extra newline end CompleteCalls = end

local function HookFunction(f, fname, origin) if not NoHookZone[f] then local out = function(...) local callerinfo = DetermineCaller(debug.traceback) local StackObject = FunctionCallStack[#FunctionCallStack+1] = StackObject local s = os.clock local response, length = GetVarargInfo(f(...)) local timetaken = os.clock - s StackObject.TimeTaken = timetaken CompleteCalls[#CompleteCalls+1] = StackObject local maxi = #FunctionCallStack FunctionCallStack[maxi] = nil if maxi

1 then FinishUp else FunctionCallStack[maxi-1].Offset = FunctionCallStack[maxi-1].Offset + timetaken end return unpack(response, 1, length) end NoHookZone[out] = true return out else return f endendlocal function HookTable(obj, origin) -- safety catch since we export this function if type(obj)

"function" then return HookFunction(obj, "

", origin) end for a, b in next, obj do if type(b)

"function" then obj[a] = HookFunction(b, a, origin) end end return objendrawset(_G, "_BenchmarkerHooker", HookTable)

--

Global hooking

--local require = requirelocal function hookedrequire(source) local out = require(source) if source ~= "strict" and source ~= "Module:Sandbox/Aidan9382/Benchmarker" then if type(out)

"table" then HookTable(out, source) elseif type(out)

"function" then out = HookFunction(out, "

", source) end end return outendrawset(_G, "require", hookedrequire)

return HookTable