Skip to content

Instantly share code, notes, and snippets.

@meepen
Last active June 17, 2018 16:28
Show Gist options
  • Save meepen/90dba4c510c8721e3b82014abeaea3f7 to your computer and use it in GitHub Desktop.
Save meepen/90dba4c510c8721e3b82014abeaea3f7 to your computer and use it in GitHub Desktop.
-- major features of the hook library need to be considered
-- performance is eaten from in-engine hook.Call calls every few frames/ticks
-- add/remove are not super important but should be considered still
local hook_name = "HookSuite"
local slow = true
local call_count = slow and 32000000 or 2000000000
local no_hook_call_count = slow and 200000000 or 2000000000
local invalid_call_count = slow and 200 or 20000
local hooks = {
function()
end,
function()
end,
function()
end
}
local gm = {
[hook_name] = function() end,
NEVERUSETHISOK = function() end
}
return {
CallNoGM = function(lib)
local call = lib.Call
for i = 1, call_count do
call(hook_name)
end
return call_count
end,
CallGM = function(lib)
local call = lib.Call
for i = 1, call_count do
call(hook_name, gm)
end
return call_count
end,
CallNoHooks = function(lib)
local call = lib.Call
local hook_name, no_hook_call_count = "NEVERUSETHISOK", no_hook_call_count -- yes this is important
for i = 1, no_hook_call_count do
call(hook_name)
end
return no_hook_call_count
end,
CallGMOnly = function(lib)
local call = lib.Call
local hook_name, no_hook_call_count = "NEVERUSETHISOK", no_hook_call_count -- yes this is important
for i = 1, no_hook_call_count do
call(hook_name, gm)
end
return no_hook_call_count
end,
CallInvalid = function(lib)
local call, add = lib.Call, lib.Add
local hook_name, invalid_call_count = "NEVERUSETHISOKENTS", invalid_call_count -- yes this is important
local function nop() end
local invalid = {}
local valid = {}
for i = 1, 25 do
invalid[i] = {IsValid = function() return false end}
valid[i] = {IsValid = function() return true end}
end
for i = 1, invalid_call_count do
for i2 = 1, 25 do -- very extreme case
add(hook_name, invalid[i2], nop)
add(hook_name, valid[i2], nop)
end
call(hook_name)
end
return invalid_call_count
end,
Verify = function(hook)
local HOOK_ID = "VERIFY_HOOK"
local call_count = 0
local function add() call_count = call_count + 1 end
hook.Add(HOOK_ID, HOOK_ID, add)
hook.Call(HOOK_ID)
assert(call_count == 1, "Call count not sane")
call_count = 0
hook.Remove(HOOK_ID, HOOK_ID)
hook.Call(HOOK_ID)
assert(call_count == 0, "Call count not sane after remove")
call_count = 0
hook.Add(HOOK_ID, HOOK_ID, add)
hook.Call(HOOK_ID)
assert(call_count == 1, "Call count not sane after readd")
call_count = 0
hook.Add(HOOK_ID, HOOK_ID, add)
hook.Call(HOOK_ID)
assert(call_count == 1, "Call count not sane after duped add")
call_count = 0
local t = {IsValid = function() return true end}
hook.Add(HOOK_ID, t, function(self)
assert(t == self, "did not pass object name to hook")
add()
end)
hook.Call(HOOK_ID)
assert(call_count == 2, "Call count not sane after calling IsValid")
--[[
hook.Add(HOOK_ID, HOOK_ID)
assert(hook.GetTable()[HOOK_ID][HOOK_ID], "Removed instead of returning")
call_count = 0
hook.Call(HOOK_ID)
assert(call_count == 2, "Wrong call count after add with nil function")
]]
hook.Remove(HOOK_ID, HOOK_ID)
call_count = 0
hook.Call(HOOK_ID)
assert(call_count == 1, "Call count not one")
hook.Remove(HOOK_ID, t)
call_count = 0
hook.Call(HOOK_ID)
assert(call_count == 0, "Call count not zero")
call_count = 0
hook.Add(HOOK_ID, HOOK_ID, add)
t = {IsValid = function() return true end}
hook.Add(HOOK_ID, t, add)
hook.Add(HOOK_ID, {IsValid = function() return false end}, add)
hook.Call(HOOK_ID)
assert(call_count == 2, "Call count not two after adding isvalids "..call_count)
call_count = 0
hook.Remove(HOOK_ID, HOOK_ID)
hook.Add(HOOK_ID, {IsValid = function() return false end}, add)
hook.Add(HOOK_ID, t, add)
hook.Call(HOOK_ID)
assert(call_count == 1, "Call count not one after adding isvalids "..call_count)
call_count = 0
hook.Add(HOOK_ID, {IsValid = function() return false end}, add)
hook.Call(HOOK_ID)
assert(call_count == 1, "Call count not one after adding invalid at end "..call_count)
call_count = 0
hook.Call(HOOK_ID)
assert(call_count == 1, "Call count not one after removing invalid at end"..call_count)
call_count = 0
hook.Add(HOOK_ID, t, add)
pcall(hook.Add, HOOK_ID, HOOK_ID)
hook.Remove(HOOK_ID, HOOK_ID)
hook.Call(HOOK_ID)
assert(call_count == 1, "Call count not one after instantly removing "..call_count)
call_count = 0
hook.Remove(HOOK_ID, t)
assert(call_count == 0, "Call count not zero after removing all hooks")
hook.Add(HOOK_ID, HOOK_ID, function()
add()
hook.Remove(HOOK_ID, HOOK_ID)
hook.Call(HOOK_ID)
assert(call_count == 1, "Call count not 1 after removing inside and calling again "..call_count)
hook.Add(HOOK_ID, HOOK_ID, add)
hook.Call(HOOK_ID)
assert(call_count == 2, "Call count not 2 after removing inside and calling then adding again "..call_count)
end)
local GM
GM = {
[HOOK_ID] = function(self)
assert(self == GM, "GM not passed")
add()
end
}
call_count = 0
hook.Call(HOOK_ID, GM)
assert(call_count == 3, "Call count not 3 in final test "..call_count)
hook.Remove(HOOK_ID, HOOK_ID)
return 1, true
end,
All = function(self, lib)
local bench_time = SysTime
for i = 1, #hooks do
lib.Add(hook_name, tostring(i), hooks[i])
end
self.Verify(lib)
local rets = {}
for _, k in ipairs { "CallInvalid", "CallGM", "CallGMOnly", "CallNoHooks", "CallNoGM" } do
jit.on(self[k], true)
local start_time = bench_time()
local amountofcalls, success = self[k](lib)
local end_time = bench_time()
rets[k] = {
Time = end_time - start_time,
Calls = amountofcalls,
Success = success
}
end
for i = 1, #hooks do
lib.Remove(hook_name, tostring(i))
end
return rets
end
}
-- include "jit/verbose.lua" jit.verbose.on(Msg) include "hooklib/hooktests.lua"{file1 = "hooklib/linkedhook.lua", file2 = "includes/modules/hook.lua", hook1 = "linkedhook", hook2 = "hook" } jit.verbose.off()
return function(data)
assert(data.file1 and data.file2 and data.hook1 and data.hook2, "testing requires four arguments")
local file1, file2 = data.file1, data.file2
include(file1)
include(file2)
local suite1, suite2 = include "hooklib/hooksuite.lua", include "hooklib/hooksuite.lua"
local hook1, hook2 = getfenv(0)[data.hook1], getfenv(0)[data.hook2]
assert(hook1, "hook1 failed")
assert(hook2, "hook2 failed")
print(file1.."...")
local score1 = suite1:All(hook1)
print(file2.."...")
local score2 = suite2:All(hook2)
print "\n\n\n\n\n\n\n\n"
print "BENCHMARK"
local printafter1 = (" "):rep(math.max(file1:len(), file2:len()) - file1:len())
local printafter2 = (" "):rep(math.max(file1:len(), file2:len()) - file2:len())
for k, v in pairs(score2) do
if (not v.Time) then continue end
local did2win = score1[k].Time > v.Time
print "-------------"
print(string.format("%s (%i calls)\n%s (%.02f%%)", k, v.Calls or 0, did2win and file2 or file1, 100 * (1 - (did2win and score1[k].Time / v.Time or v.Time / score1[k].Time))))
print(string.format("%s:%s %.09f s", file2, printafter2, v.Time))
print(string.format("%s:%s %.09f s", file1, printafter1, score1[k].Time))
end
print "-------------"
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment