Hi! With JohnW's suggestion, I managed to code a profiler which works reasonably well. You pass it a function, and it gives you approximately how many ms it takes to run.
Put the code in a script entity named "logProfiler". Then call the profiler using something like that (example called from onDrawGui using
profileOnce, you can manually call
profile as well).:
Code: Select all
logProfiler.profileOnce("JohnW's QuickBar",
function()
qb_script.onDrawGui(g); -- JW's QuickBar hook
end)
Which gave me the following results:
If you have too much "discarded passes", lower the iterations number, but it might lower the accuracy a bit.
More passes = more accurate measurement, but it takes longer to calculate.
Code: Select all
-- LoG Profiler by Diarmuid
-- To setup, link a call to logProfiler.onDrawGui() from the onDrawGui party hook.
-- To run, call logProfiler.profile(profileName, function)
-- You can also call logProfiler.profileOnce(profileName, function), it will only run the profiler once (if it's from a timer, or onDrawGui for example).
-- Profile name is just an identifier.
iterations = 100;
passes = 500;
frame = 1;
functName = "";
funct = nil;
playTime = {};
frame25 = math.floor(passes * 2 * 0.25);
frame50 = math.floor(passes * 2 * 0.5);
frame75 = math.floor(passes * 2 * 0.75);
function profileOnce(fName, f)
if functName == fName then return; end
frame = -5;
runProfile(fName, f);
end
function profile(fName, f)
frame = 0;
runProfile(fName, f);
end
function runProfile(fName, f)
if funct then return; end
if type(fName) ~= "string" or type(f) ~= "function" then
print("Warning: LoG Profiler, attempting to run with incorrect arguments.");
return;
end
playTime = {};
functName = fName;
funct = f;
print("==== Starting LoG Profiler for "..functName.." ("..iterations.." iterations, "..passes.." passes) ====");
end
function onDrawGui()
if not(funct) then return; end
frame = frame + 1;
if frame < 1 then return; end
if frame <= passes * 2 then
if frame % 2 == 1 then
registerTime();
end
if frame % 2 == 0 then
registerTime();
executeFunction();
end
else
registerTime();
funct = nil;
printReport();
return;
end
if frame == frame25 then
print("..25%");
elseif frame == frame50 then
print("..50%");
elseif frame == frame75 then
print("..75%");
end
end
function registerTime()
playTime[frame] = getStatistic("play_time");
end
function executeFunction()
for i = 1, iterations do
funct();
end
end
function printReport()
local dt = {};
local maxDt = 0;
local oDt = {}
for i = 1, passes * 2 do
dt[i] = playTime[i + 1] - playTime[i];
if dt[i] >= 0.1 then
--print("Warning: dt["..i.."] capped at 0.1s");
table.insert(oDt, i);
end
if dt[i] > maxDt then
maxDt = dt[i];
end
end
local pTime = {};
local totalTime = 0;
local oPasses = 0`;
for i = 1, passes do
pTime[i] = dt[i * 2] - dt[(i * 2) - 1];
for j in ipairs(oDt) do
if oDt[j] == i * 2 or oDt[j] == (i * 2) - 1 then
--print("Overloaded pass time ["..i.."] :"..pTime[i]);
oPasses = oPasses + 1;
totalTime = totalTime - pTime[i];
end
end
totalTime = totalTime + pTime[i];
end
print("==== Profiling results for "..functName.." ====");
print("Total time: "..round(totalTime, 2).."s for "..(iterations * (passes - oPasses)).." calls ("..oPasses.." passes discarded).");
print("Average pass time: "..round(totalTime / (passes - oPasses), 3).."s.");
print("Average call: "..round(totalTime / (passes - oPasses) / iterations * 1000, 2).."ms.");
end
function round(num, idp)
local mult = 10^(idp or 0)
if num >= 0 then return math.floor(num * mult + 0.5) / mult
else return math.ceil(num * mult - 0.5) / mult end
end