Module:Sandbox/Aidan9382/Benchmarker: Difference between revisions

Content deleted Content added
dumb
allow specifying a custom MinTimeTaken
 
(13 intermediate revisions by the same user not shown)
Line 1:
-- In-depth execution speed benchmarker - read the /doc for more info
-- This is TODO, expect stuff to combust on use
 
-- =================================================================== --
-- In-depth execution speed benchmarker
-- This is a meta-module that hooks lots of globals, andwhich cancould be disruptive --
-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --
-- Be careful including this module outside of sandboxes --
-- This is a meta-module that hooks lots of globals and can be disruptive --
-- =================================================================== --
-- Avoid using this outside of "Show preview" testing and sandboxes --
-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --
 
-- Always use rawget/rawset on _G to bypass strict
iflocal ActiveHooker = rawget(_G, "_BenchmarkerActive_BenchmarkerHooker") ~= nil then
if ActiveHooker ~= nil then
return false
return ActiveHooker
end
rawset(_G, "_BenchmarkerActive", true)
 
--== Personal stuff ==--
returnlocal function dp(...x, n)
n = n or 4
return math.floor(x*10^n+0.5) / 10^n
end
 
local function GetVarargInfo(...)
return {...}, select("#", ...)
Line 29 ⟶ 33:
local CompleteCalls = {}
local FunctionCallStack = {}
local NoHookZone = {}
 
local function FinishUp()
-- Note: Don't currently use caller stats. Eh, whatever
mw.log("TODO!")
local TotalTimeTaken = 0
mw.logObject(CompleteCalls)
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
local MinTimeTaken = rawget(_G, "_MinTimeTaken") or 0.01
if TotalTimeTaken > MinTimeTaken 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
return function(...)
local out = function(...)
local callerinfo = DetermineCaller(debug.traceback())
local StackObject = {
Name=fname, Origin=origin, Offset=0,
Caller=callerinfo.Function, CallLine=callerinfo.Line
}
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 unpack(response, 1, length)
return out
else
return f
end
end
local function HookTable(obj, origin)
-- safety catch since we export this function
if type(obj) == "function" then
return HookFunction(obj, "<main>", origin)
end
for a, b in next, obj do
if type(b) == "function" then
Line 65 ⟶ 121:
end
end
return falseobj
end
rawset(_G, "_BenchmarkerHooker", HookTable)
 
--== Global hooking ==--
Line 82 ⟶ 140:
rawset(_G, "require", hookedrequire)
 
return true -- Nothing to returnHookTable