Page 1 of 1

PROBE.lua: realtime graphical profiler

Posted: Sun Aug 03, 2014 9:01 pm
by Chèvre
Is your game running slow?
Have you ever wished you could profile rabidly obnoxious bottlenecks effortlessly?
Well then PROBE.lua is for you!

I hope you'll find it useful! If not, it was fun to write.
I'm open to comments/suggestions/feature requests/bug reports/you name it!

Source: https://github.com/jorio/PROBE
Demo: see attachments

Screenshot of the demo below. The actual profiler is the two bars on the sides; the colored junk in the middle is what's being profiled.

Latest update 2014-08-22

2014-08-22: pause/resume on the fly (PROBE.enable()), preserve return values of hooked functions (thanks for the heads-up lemtzas)
2014-08-03: first version

Image

Re: PROBE.lua: realtime graphical profiler

Posted: Mon Aug 04, 2014 7:38 am
by Ragzouken
this is amazing!

i suggest you do whatever you can to make this auto-detect the settings: "GPROFILER:hookAll(_G, "draw", {love, lg})" i don't really know what this means, but do i need to know? what if "GPROFILE:hookAll("draw")" did the rest by default?

also it'd be nice if you could make it auto-detect the screen size and you could just do something like: probe.draw(CPROFILE, GPROFILE, ...) and it would draw the columns without you specifying coordinates

these are all usability suggestions, the library itself is very impressive and i'm using it immediately

Re: PROBE.lua: realtime graphical profiler

Posted: Mon Aug 04, 2014 9:44 am
by Chèvre
Hi! Thanks for the feedback! You're right, placing hooks automatically isn't the most intuitive thing in the world. I should write a tutorial and/or provide a sensible mass-hook default function.

But the gist of it is:
  1. create a new profiler
  2. surround code that you would like to profile with profiler:startCycle() and profiler:endCycle() (typically in love.draw() or love.update())
  3. create "events" for which you'd like to have detailed statistics
If you don't create custom events, everybody's runtime will be lumped together in a default event named "<ROOT>". Fortunately, it's easy to create events automatically by placing hooks onto functions that do important things, typically your draw() or update() functions. To create events from functions, you can either:
  1. hook each individual function by hand
  2. mass-hook a ton of functions that have the same name
A. is tedious for "real world" projects; it's only really useful to profile one-off functions that have a unique name. But it's done as such, assuming Missile, Enemy, Player are tables (e.g. classes):

Code: Select all

profiler:hook(Missile, 'draw', 'draw a missile')
profiler:hook(Enemy, 'draw', 'draw an enemy')
profiler:hook(Player, 'draw', 'draw myself')
Now for B., assuming Missile/Enemy/Player are global tables, you can do this instead:

Code: Select all

profiler:hookAll(_G, 'draw', {love})
This will look in all subtables of _G (in Lua, _G is the table of all globals) for functions named 'draw' and place a profiling hook on it. In other words, it'll match:
_G.Enemy.draw
_G.Missile.draw
_G.Player.draw
_G.love.draw
Oops! We don't want to place an automatic hook on love.draw(). That's why we give hookAll a list of tables to skip, in this case just {love}.

(Technical reason why you don't want to place a hook on love.draw(): hooks actually just wrap a function call in an event; events may only occur in a "profiling cycle"; but you start and end the cycles in love.draw(), so if an event is triggered before a cycle exists, the profiler will crash)

Similarly, in the little example posted on github, I've also told the profiler to ignore lg. I used "lg" as an alias for "love.graphics". Since I don't want the profiler to place a hook on love.graphics.draw (through lg.draw), I told it to ignore lg.

Note that while it is dangerous to profile love.draw, it's totally okay to profile love.graphics.draw (that's the one that draws images). You can even profile LÖVE itself, e.g. love.graphics.rectangle, love.graphics.print, etc.

So I've mainly talked about hooks on functions here, but you can also create little events in your code with pushEvent()/popEvent() (it's really simple). I recommend reading main.lua from the demo because it explores all possible use cases. Also, don't hesitate to read the comments in PROBE.lua, they also describe how you should call each function.

While writing this post, I've realized the API could be made more intuitive, and I'll think of ways to simplify interaction with the hook mechanism. I should also post tutorials/luadocs somewhere :) Thankfully, you only need place hooks once (typically in love.load()) and then you don't have to touch them again, so if you'd like to use the profiler right away, you don't really *need* to understand the hook conundrum in-depth. But I'm really glad it's useful to you!

Re: PROBE.lua: realtime graphical profiler

Posted: Mon Aug 04, 2014 10:14 am
by Ragzouken
thanks for explaining that, it really helps. i didn't have a lot of my classes imported into main.lua, which meant it didn't automatically profile everything with the example line - so i was confused by that. this is really great

Re: PROBE.lua: realtime graphical profiler

Posted: Mon Aug 11, 2014 3:29 am
by lemtzas
Thanks! I found this really useful for tracking down bottlenecks in my game.

I had to make a modification though (functions that return values wouldn't profile correctly, so I packed, stored, unpacked, and returned the results).

A couple other features I'd like (or will probably make on my own eventually) are clearing all tracked functions and/or telling it to not worry about running a function without starting a cycle and/or the ability to toggle tracking. So I can quickly add/remove it without it getting too uppity when functions get called outside the segment i'm actually tracking.

Re: PROBE.lua: realtime graphical profiler

Posted: Fri Aug 22, 2014 1:54 pm
by Chèvre
lemtzas wrote:Thanks! I found this really useful for tracking down bottlenecks in my game.
Hi! Great! Thank you for giving it a try!
Sorry about the delay. Reply notifications were turned off :oops:
lemtzas wrote:I had to make a modification though (functions that return values wouldn't profile correctly, so I packed, stored, unpacked, and returned the results).
Whoops! Nice find. I should've thought about this. I've updated it.
lemtzas wrote:A couple other features I'd like (or will probably make on my own eventually) are clearing all tracked functions and/or telling it to not worry about running a function without starting a cycle and/or the ability to toggle tracking. So I can quickly add/remove it without it getting too uppity when functions get called outside the segment i'm actually tracking.
You can try unhook().
I've also added an "enable()" function that lets you pause/resume profiling at any time. Check out the updated demo in the thread's top message.
Btw, don't hesitate to send pull requests for any changes you make ;)